diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4978d776d..9e3a565c5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -628,7 +628,7 @@ You'll need to modify the following files - `backend/s3/s3.go` - Add the provider to `providerOption` at the top of the file - Add endpoints and other config for your provider gated on the provider in `fs.RegInfo`. - - Exclude your provider from generic config questions (eg `region` and `endpoint). + - Exclude your provider from generic config questions (eg `region` and `endpoint`). - Add the provider to the `setQuirks` function - see the documentation there. - `docs/content/s3.md` - Add the provider at the top of the page. diff --git a/MANUAL.html b/MANUAL.html index d47636c4f..23887b83d 100644 --- a/MANUAL.html +++ b/MANUAL.html @@ -21765,7 +21765,7 @@
Flags helpful for increasing performance.
--buffer-size SizeSuffix In memory buffer size when reading files for each --transfer (default 16Mi)
diff --git a/MANUAL.md b/MANUAL.md
index a3eb4762e..9d61008fa 100644
--- a/MANUAL.md
+++ b/MANUAL.md
@@ -23165,7 +23165,7 @@ Flags for general networking and HTTP stuff.
--tpslimit float Limit HTTP transactions per second to this
--tpslimit-burst int Max burst of transactions for --tpslimit (default 1)
--use-cookies Enable session cookiejar
- --user-agent string Set the user-agent to a specified string (default "rclone/v1.73.1")
+ --user-agent string Set the user-agent to a specified string (default "rclone/v1.73.2")
```
diff --git a/MANUAL.txt b/MANUAL.txt
index cabd37c9b..d0e315077 100644
--- a/MANUAL.txt
+++ b/MANUAL.txt
@@ -22527,7 +22527,7 @@ Flags for general networking and HTTP stuff.
--tpslimit float Limit HTTP transactions per second to this
--tpslimit-burst int Max burst of transactions for --tpslimit (default 1)
--use-cookies Enable session cookiejar
- --user-agent string Set the user-agent to a specified string (default "rclone/v1.73.1")
+ --user-agent string Set the user-agent to a specified string (default "rclone/v1.73.2")
Performance
diff --git a/README.md b/README.md
index a2220f140..7af251973 100644
--- a/README.md
+++ b/README.md
@@ -1,21 +1,4 @@
-> ⚠️ **Unofficial Fork Disclaimer**
-> This is an **unofficial fork** of [rclone](https://rclone.org), with additional enhancements such as **Alist**, **Alldebrid**, **iCloud Photos**, **Teldrive** and **Terabox** support.
-> I am **not affiliated with the upstream maintainers**, and this fork **does not intend to be malicious or harmful** in any way.
-> Please **read the source code** if you're unsure or want to verify that it behaves as described.
-> Contributions, feedback, and scrutiny are welcome.
-
----
-
-> ### 🚨 **BEWARE OF MALICIOUS FORKS**
->
-> **This project is actively maintained.**
->
-> There are forks (such as `jabiralam7/bclone-continued`) that were created **in advance** to falsely imply this project has been discontinued. **This is not true.** These forks are a known setup to inject malware at a later date.
->
-> **Only download releases from this official repository.** Do not trust any fork claiming to be a "continuation" of bclone.
-
----
-
+
[
](https://rclone.org/#gh-light-mode-only)
[
](https://rclone.org/#gh-dark-mode-only)
@@ -28,7 +11,10 @@
[Installation](https://rclone.org/install/) |
[Forum](https://forum.rclone.org/)
-[](https://github.com/benjithatfoxguy/bclone/actions?query=workflow%3Abuild)
+[](https://github.com/rclone/rclone/actions?query=workflow%3Abuild)
+[](https://goreportcard.com/report/github.com/rclone/rclone)
+[](https://godoc.org/github.com/rclone/rclone)
+[](https://hub.docker.com/r/rclone/rclone)
# Rclone
@@ -37,101 +23,104 @@ directories to and from different cloud storage providers.
## Storage providers
- * 1Fichier [:page_facing_up:](https://rclone.org/fichier/)
- * Akamai Netstorage [:page_facing_up:](https://rclone.org/netstorage/)
- * Alibaba Cloud (Aliyun) Object Storage System (OSS) [:page_facing_up:](https://rclone.org/s3/#alibaba-oss)
- * Alist [:page_facing_up:](https://github.com/AlistGo/alist)
- * Alldebrid [:page_facing_up:](https://alldebrid.com/)
- * Amazon S3 [:page_facing_up:](https://rclone.org/s3/)
- * ArvanCloud Object Storage (AOS) [:page_facing_up:](https://rclone.org/s3/#arvan-cloud-object-storage-aos)
- * Backblaze B2 [:page_facing_up:](https://rclone.org/b2/)
- * Box [:page_facing_up:](https://rclone.org/box/)
- * Ceph [:page_facing_up:](https://rclone.org/s3/#ceph)
- * China Mobile Ecloud Elastic Object Storage (EOS) [:page_facing_up:](https://rclone.org/s3/#china-mobile-ecloud-eos)
- * Cloudflare R2 [:page_facing_up:](https://rclone.org/s3/#cloudflare-r2)
- * Citrix ShareFile [:page_facing_up:](https://rclone.org/sharefile/)
- * DigitalOcean Spaces [:page_facing_up:](https://rclone.org/s3/#digitalocean-spaces)
- * Digi Storage [:page_facing_up:](https://rclone.org/koofr/#digi-storage)
- * Dreamhost [:page_facing_up:](https://rclone.org/s3/#dreamhost)
- * Dropbox [:page_facing_up:](https://rclone.org/dropbox/)
- * Enterprise File Fabric [:page_facing_up:](https://rclone.org/filefabric/)
- * Fastmail Files [:page_facing_up:](https://rclone.org/webdav/#fastmail-files)
- * Files.com [:page_facing_up:](https://rclone.org/filescom/)
- * FlashBlade [:page_facing_up:](https://rclone.org/s3/#pure-storage-flashblade)
- * FTP [:page_facing_up:](https://rclone.org/ftp/)
- * GoFile [:page_facing_up:](https://rclone.org/gofile/)
- * Google Cloud Storage [:page_facing_up:](https://rclone.org/googlecloudstorage/)
- * Google Drive [:page_facing_up:](https://rclone.org/drive/)
- * Google Photos [:page_facing_up:](https://rclone.org/googlephotos/)
- * HDFS (Hadoop Distributed Filesystem) [:page_facing_up:](https://rclone.org/hdfs/)
- * Hetzner Storage Box [:page_facing_up:](https://rclone.org/sftp/#hetzner-storage-box)
- * HiDrive [:page_facing_up:](https://rclone.org/hidrive/)
- * HTTP [:page_facing_up:](https://rclone.org/http/)
- * Huawei Cloud Object Storage Service(OBS) [:page_facing_up:](https://rclone.org/s3/#huawei-obs)
- * iCloud Drive [:page_facing_up:](https://rclone.org/iclouddrive/)
- * iCloud Photos [:page_facing_up:](https://github.com/rclone/rclone/issues/7982)
- * ImageKit [:page_facing_up:](https://rclone.org/imagekit/)
- * Internet Archive [:page_facing_up:](https://rclone.org/internetarchive/)
- * Jottacloud [:page_facing_up:](https://rclone.org/jottacloud/)
- * IBM COS S3 [:page_facing_up:](https://rclone.org/s3/#ibm-cos-s3)
- * IONOS Cloud [:page_facing_up:](https://rclone.org/s3/#ionos)
- * Koofr [:page_facing_up:](https://rclone.org/koofr/)
- * Leviia Object Storage [:page_facing_up:](https://rclone.org/s3/#leviia)
- * Liara Object Storage [:page_facing_up:](https://rclone.org/s3/#liara-object-storage)
- * Linkbox [:page_facing_up:](https://rclone.org/linkbox)
- * Linode Object Storage [:page_facing_up:](https://rclone.org/s3/#linode)
- * Magalu Object Storage [:page_facing_up:](https://rclone.org/s3/#magalu)
- * Mail.ru Cloud [:page_facing_up:](https://rclone.org/mailru/)
- * Memset Memstore [:page_facing_up:](https://rclone.org/swift/)
- * MEGA [:page_facing_up:](https://rclone.org/mega/)
- * MEGA S4 Object Storage [:page_facing_up:](https://rclone.org/s3/#mega)
- * Memory [:page_facing_up:](https://rclone.org/memory/)
- * Microsoft Azure Blob Storage [:page_facing_up:](https://rclone.org/azureblob/)
- * Microsoft Azure Files Storage [:page_facing_up:](https://rclone.org/azurefiles/)
- * Microsoft OneDrive [:page_facing_up:](https://rclone.org/onedrive/)
- * Minio [:page_facing_up:](https://rclone.org/s3/#minio)
- * Nextcloud [:page_facing_up:](https://rclone.org/webdav/#nextcloud)
- * OVH [:page_facing_up:](https://rclone.org/swift/)
- * Blomp Cloud Storage [:page_facing_up:](https://rclone.org/swift/)
- * OpenDrive [:page_facing_up:](https://rclone.org/opendrive/)
- * OpenStack Swift [:page_facing_up:](https://rclone.org/swift/)
- * Oracle Cloud Storage [:page_facing_up:](https://rclone.org/swift/)
- * Oracle Object Storage [:page_facing_up:](https://rclone.org/oracleobjectstorage/)
- * Outscale [:page_facing_up:](https://rclone.org/s3/#outscale)
- * ownCloud [:page_facing_up:](https://rclone.org/webdav/#owncloud)
- * pCloud [:page_facing_up:](https://rclone.org/pcloud/)
- * Petabox [:page_facing_up:](https://rclone.org/s3/#petabox)
- * PikPak [:page_facing_up:](https://rclone.org/pikpak/)
- * Pixeldrain [:page_facing_up:](https://rclone.org/pixeldrain/)
- * premiumize.me [:page_facing_up:](https://rclone.org/premiumizeme/)
- * put.io [:page_facing_up:](https://rclone.org/putio/)
- * Proton Drive [:page_facing_up:](https://rclone.org/protondrive/)
- * QingStor [:page_facing_up:](https://rclone.org/qingstor/)
- * Qiniu Cloud Object Storage (Kodo) [:page_facing_up:](https://rclone.org/s3/#qiniu)
- * Quatrix [:page_facing_up:](https://rclone.org/quatrix/)
- * Rackspace Cloud Files [:page_facing_up:](https://rclone.org/swift/)
- * RackCorp Object Storage [:page_facing_up:](https://rclone.org/s3/#RackCorp)
- * rsync.net [:page_facing_up:](https://rclone.org/sftp/#rsync-net)
- * Scaleway [:page_facing_up:](https://rclone.org/s3/#scaleway)
- * Seafile [:page_facing_up:](https://rclone.org/seafile/)
- * Seagate Lyve Cloud [:page_facing_up:](https://rclone.org/s3/#lyve)
- * SeaweedFS [:page_facing_up:](https://rclone.org/s3/#seaweedfs)
- * Selectel Object Storage [:page_facing_up:](https://rclone.org/s3/#selectel)
- * SFTP [:page_facing_up:](https://rclone.org/sftp/)
- * SMB / CIFS [:page_facing_up:](https://rclone.org/smb/)
- * StackPath [:page_facing_up:](https://rclone.org/s3/#stackpath)
- * Storj [:page_facing_up:](https://rclone.org/storj/)
- * SugarSync [:page_facing_up:](https://rclone.org/sugarsync/)
- * Synology C2 Object Storage [:page_facing_up:](https://rclone.org/s3/#synology-c2)
- * Tencent Cloud Object Storage (COS) [:page_facing_up:](https://rclone.org/s3/#tencent-cos)
- * Teldrive [📄](https://github.com/tgdrive/teldrive)
- * Terabox [📄](https://terabox.com)
- * Uloz.to [:page_facing_up:](https://rclone.org/ulozto/)
- * Wasabi [:page_facing_up:](https://rclone.org/s3/#wasabi)
- * WebDAV [:page_facing_up:](https://rclone.org/webdav/)
- * Yandex Disk [:page_facing_up:](https://rclone.org/yandex/)
- * Zoho WorkDrive [:page_facing_up:](https://rclone.org/zoho/)
- * The local filesystem [:page_facing_up:](https://rclone.org/local/)
+- 1Fichier [:page_facing_up:](https://rclone.org/fichier/)
+- Akamai Netstorage [:page_facing_up:](https://rclone.org/netstorage/)
+- Alibaba Cloud (Aliyun) Object Storage System (OSS) [:page_facing_up:](https://rclone.org/s3/#alibaba-oss)
+- Amazon S3 [:page_facing_up:](https://rclone.org/s3/)
+- ArvanCloud Object Storage (AOS) [:page_facing_up:](https://rclone.org/s3/#arvan-cloud-object-storage-aos)
+- Backblaze B2 [:page_facing_up:](https://rclone.org/b2/)
+- Box [:page_facing_up:](https://rclone.org/box/)
+- Ceph [:page_facing_up:](https://rclone.org/s3/#ceph)
+- China Mobile Ecloud Elastic Object Storage (EOS) [:page_facing_up:](https://rclone.org/s3/#china-mobile-ecloud-eos)
+- Cloudflare R2 [:page_facing_up:](https://rclone.org/s3/#cloudflare-r2)
+- Citrix ShareFile [:page_facing_up:](https://rclone.org/sharefile/)
+- DigitalOcean Spaces [:page_facing_up:](https://rclone.org/s3/#digitalocean-spaces)
+- Digi Storage [:page_facing_up:](https://rclone.org/koofr/#digi-storage)
+- Dreamhost [:page_facing_up:](https://rclone.org/s3/#dreamhost)
+- Dropbox [:page_facing_up:](https://rclone.org/dropbox/)
+- Enterprise File Fabric [:page_facing_up:](https://rclone.org/filefabric/)
+- Exaba [:page_facing_up:](https://rclone.org/s3/#exaba)
+- Fastmail Files [:page_facing_up:](https://rclone.org/webdav/#fastmail-files)
+- FileLu [:page_facing_up:](https://rclone.org/filelu/)
+- Files.com [:page_facing_up:](https://rclone.org/filescom/)
+- FlashBlade [:page_facing_up:](https://rclone.org/s3/#pure-storage-flashblade)
+- FTP [:page_facing_up:](https://rclone.org/ftp/)
+- GoFile [:page_facing_up:](https://rclone.org/gofile/)
+- Google Cloud Storage [:page_facing_up:](https://rclone.org/googlecloudstorage/)
+- Google Drive [:page_facing_up:](https://rclone.org/drive/)
+- Google Photos [:page_facing_up:](https://rclone.org/googlephotos/)
+- HDFS (Hadoop Distributed Filesystem) [:page_facing_up:](https://rclone.org/hdfs/)
+- Hetzner Object Storage [:page_facing_up:](https://rclone.org/s3/#hetzner)
+- Hetzner Storage Box [:page_facing_up:](https://rclone.org/sftp/#hetzner-storage-box)
+- HiDrive [:page_facing_up:](https://rclone.org/hidrive/)
+- HTTP [:page_facing_up:](https://rclone.org/http/)
+- Huawei Cloud Object Storage Service(OBS) [:page_facing_up:](https://rclone.org/s3/#huawei-obs)
+- iCloud Drive [:page_facing_up:](https://rclone.org/iclouddrive/)
+- ImageKit [:page_facing_up:](https://rclone.org/imagekit/)
+- Internet Archive [:page_facing_up:](https://rclone.org/internetarchive/)
+- Jottacloud [:page_facing_up:](https://rclone.org/jottacloud/)
+- IBM COS S3 [:page_facing_up:](https://rclone.org/s3/#ibm-cos-s3)
+- Intercolo Object Storage [:page_facing_up:](https://rclone.org/s3/#intercolo)
+- IONOS Cloud [:page_facing_up:](https://rclone.org/s3/#ionos)
+- Koofr [:page_facing_up:](https://rclone.org/koofr/)
+- Leviia Object Storage [:page_facing_up:](https://rclone.org/s3/#leviia)
+- Liara Object Storage [:page_facing_up:](https://rclone.org/s3/#liara-object-storage)
+- Linkbox [:page_facing_up:](https://rclone.org/linkbox)
+- Linode Object Storage [:page_facing_up:](https://rclone.org/s3/#linode)
+- Magalu Object Storage [:page_facing_up:](https://rclone.org/s3/#magalu)
+- Mail.ru Cloud [:page_facing_up:](https://rclone.org/mailru/)
+- Memset Memstore [:page_facing_up:](https://rclone.org/swift/)
+- MEGA [:page_facing_up:](https://rclone.org/mega/)
+- MEGA S4 Object Storage [:page_facing_up:](https://rclone.org/s3/#mega)
+- Memory [:page_facing_up:](https://rclone.org/memory/)
+- Microsoft Azure Blob Storage [:page_facing_up:](https://rclone.org/azureblob/)
+- Microsoft Azure Files Storage [:page_facing_up:](https://rclone.org/azurefiles/)
+- Microsoft OneDrive [:page_facing_up:](https://rclone.org/onedrive/)
+- Minio [:page_facing_up:](https://rclone.org/s3/#minio)
+- Nextcloud [:page_facing_up:](https://rclone.org/webdav/#nextcloud)
+- Blomp Cloud Storage [:page_facing_up:](https://rclone.org/swift/)
+- OpenDrive [:page_facing_up:](https://rclone.org/opendrive/)
+- OpenStack Swift [:page_facing_up:](https://rclone.org/swift/)
+- Oracle Cloud Storage [:page_facing_up:](https://rclone.org/swift/)
+- Oracle Object Storage [:page_facing_up:](https://rclone.org/oracleobjectstorage/)
+- Outscale [:page_facing_up:](https://rclone.org/s3/#outscale)
+- OVHcloud Object Storage (Swift) [:page_facing_up:](https://rclone.org/swift/)
+- OVHcloud Object Storage (S3-compatible) [:page_facing_up:](https://rclone.org/s3/#ovhcloud)
+- ownCloud [:page_facing_up:](https://rclone.org/webdav/#owncloud)
+- pCloud [:page_facing_up:](https://rclone.org/pcloud/)
+- Petabox [:page_facing_up:](https://rclone.org/s3/#petabox)
+- PikPak [:page_facing_up:](https://rclone.org/pikpak/)
+- Pixeldrain [:page_facing_up:](https://rclone.org/pixeldrain/)
+- premiumize.me [:page_facing_up:](https://rclone.org/premiumizeme/)
+- put.io [:page_facing_up:](https://rclone.org/putio/)
+- Proton Drive [:page_facing_up:](https://rclone.org/protondrive/)
+- QingStor [:page_facing_up:](https://rclone.org/qingstor/)
+- Qiniu Cloud Object Storage (Kodo) [:page_facing_up:](https://rclone.org/s3/#qiniu)
+- Rabata Cloud Storage [:page_facing_up:](https://rclone.org/s3/#Rabata)
+- Quatrix [:page_facing_up:](https://rclone.org/quatrix/)
+- Rackspace Cloud Files [:page_facing_up:](https://rclone.org/swift/)
+- RackCorp Object Storage [:page_facing_up:](https://rclone.org/s3/#RackCorp)
+- rsync.net [:page_facing_up:](https://rclone.org/sftp/#rsync-net)
+- Scaleway [:page_facing_up:](https://rclone.org/s3/#scaleway)
+- Seafile [:page_facing_up:](https://rclone.org/seafile/)
+- Seagate Lyve Cloud [:page_facing_up:](https://rclone.org/s3/#lyve)
+- SeaweedFS [:page_facing_up:](https://rclone.org/s3/#seaweedfs)
+- Selectel Object Storage [:page_facing_up:](https://rclone.org/s3/#selectel)
+- SFTP [:page_facing_up:](https://rclone.org/sftp/)
+- SMB / CIFS [:page_facing_up:](https://rclone.org/smb/)
+- Spectra Logic [:page_facing_up:](https://rclone.org/s3/#spectralogic)
+- StackPath [:page_facing_up:](https://rclone.org/s3/#stackpath)
+- Storj [:page_facing_up:](https://rclone.org/storj/)
+- SugarSync [:page_facing_up:](https://rclone.org/sugarsync/)
+- Synology C2 Object Storage [:page_facing_up:](https://rclone.org/s3/#synology-c2)
+- Tencent Cloud Object Storage (COS) [:page_facing_up:](https://rclone.org/s3/#tencent-cos)
+- Uloz.to [:page_facing_up:](https://rclone.org/ulozto/)
+- Wasabi [:page_facing_up:](https://rclone.org/s3/#wasabi)
+- WebDAV [:page_facing_up:](https://rclone.org/webdav/)
+- Yandex Disk [:page_facing_up:](https://rclone.org/yandex/)
+- Zoho WorkDrive [:page_facing_up:](https://rclone.org/zoho/)
+- Zata.ai [:page_facing_up:](https://rclone.org/s3/#Zata)
+- The local filesystem [:page_facing_up:](https://rclone.org/local/)
Please see [the full list of all storage providers and their features](https://rclone.org/overview/)
@@ -184,7 +173,7 @@ Please see the [rclone website](https://rclone.org/) for:
## Downloads
- * https://github.com/BenjiThatFoxGuy/bclone/releases/latest/
+-
## License
diff --git a/VERSION b/VERSION
index 719caf748..b12b8266c 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-v1.73.1
+v1.73.2
diff --git a/backend/alias/alias.go b/backend/alias/alias.go
index 92dda17bf..ae4736163 100644
--- a/backend/alias/alias.go
+++ b/backend/alias/alias.go
@@ -1,4 +1,4 @@
-// Package alias implements a virtual provider to rename existing remotes.
+// Package alias implements a virtual provider to rename existing remotes.
package alias
import (
diff --git a/backend/alias/alias_internal_test.go b/backend/alias/alias_internal_test.go
index 18312eb02..9e3e0fe71 100644
--- a/backend/alias/alias_internal_test.go
+++ b/backend/alias/alias_internal_test.go
@@ -1,4 +1,4 @@
-package alias
+package alias
import (
"context"
diff --git a/backend/alist/alist.go b/backend/alist/alist.go
index 1b6bc9749..9b6ed6a40 100644
--- a/backend/alist/alist.go
+++ b/backend/alist/alist.go
@@ -1,4 +1,4 @@
-// Package alist implements an rclone backend for AList
+// Package alist implements an rclone backend for AList
package alist
import (
diff --git a/backend/all/all.go b/backend/all/all.go
index 2f6cb125f..92244a4da 100644
--- a/backend/all/all.go
+++ b/backend/all/all.go
@@ -2,67 +2,71 @@
package all
import (
- // Active file systems
- _ "github.com/rclone/rclone/backend/alias"
- _ "github.com/rclone/rclone/backend/alist"
- _ "github.com/rclone/rclone/backend/alldebrid"
- _ "github.com/rclone/rclone/backend/azureblob"
- _ "github.com/rclone/rclone/backend/azurefiles"
- _ "github.com/rclone/rclone/backend/b2"
- _ "github.com/rclone/rclone/backend/box"
- _ "github.com/rclone/rclone/backend/cache"
- _ "github.com/rclone/rclone/backend/chunker"
- _ "github.com/rclone/rclone/backend/cloudinary"
- _ "github.com/rclone/rclone/backend/combine"
- _ "github.com/rclone/rclone/backend/compress"
- _ "github.com/rclone/rclone/backend/crypt"
- _ "github.com/rclone/rclone/backend/drive"
- _ "github.com/rclone/rclone/backend/dropbox"
- _ "github.com/rclone/rclone/backend/fichier"
- _ "github.com/rclone/rclone/backend/filefabric"
- _ "github.com/rclone/rclone/backend/ftp"
- _ "github.com/rclone/rclone/backend/googlecloudstorage"
- _ "github.com/rclone/rclone/backend/googlephotos"
- _ "github.com/rclone/rclone/backend/hasher"
- _ "github.com/rclone/rclone/backend/hdfs"
- _ "github.com/rclone/rclone/backend/hidrive"
- _ "github.com/rclone/rclone/backend/http"
- _ "github.com/rclone/rclone/backend/imagekit"
- _ "github.com/rclone/rclone/backend/iclouddrive"
- _ "github.com/rclone/rclone/backend/internetarchive"
- _ "github.com/rclone/rclone/backend/jottacloud"
- _ "github.com/rclone/rclone/backend/koofr"
- _ "github.com/rclone/rclone/backend/linkbox"
- _ "github.com/rclone/rclone/backend/local"
- _ "github.com/rclone/rclone/backend/mailru"
- _ "github.com/rclone/rclone/backend/mega"
- _ "github.com/rclone/rclone/backend/memory"
- _ "github.com/rclone/rclone/backend/netstorage"
- _ "github.com/rclone/rclone/backend/onedrive"
- _ "github.com/rclone/rclone/backend/opendrive"
- _ "github.com/rclone/rclone/backend/oracleobjectstorage"
- _ "github.com/rclone/rclone/backend/pcloud"
- _ "github.com/rclone/rclone/backend/pikpak"
- _ "github.com/rclone/rclone/backend/premiumizeme"
- _ "github.com/rclone/rclone/backend/protondrive"
- _ "github.com/rclone/rclone/backend/putio"
- _ "github.com/rclone/rclone/backend/qingstor"
- _ "github.com/rclone/rclone/backend/quatrix"
- _ "github.com/rclone/rclone/backend/s3"
- _ "github.com/rclone/rclone/backend/seafile"
- _ "github.com/rclone/rclone/backend/sftp"
- _ "github.com/rclone/rclone/backend/sharefile"
- _ "github.com/rclone/rclone/backend/sia"
- _ "github.com/rclone/rclone/backend/smb"
- _ "github.com/rclone/rclone/backend/storj"
- _ "github.com/rclone/rclone/backend/sugarsync"
- _ "github.com/rclone/rclone/backend/swift"
- _ "github.com/rclone/rclone/backend/teldrive"
- _ "github.com/rclone/rclone/backend/terabox"
- _ "github.com/rclone/rclone/backend/ulozto"
- _ "github.com/rclone/rclone/backend/union"
- _ "github.com/rclone/rclone/backend/uptobox"
- _ "github.com/rclone/rclone/backend/webdav"
- _ "github.com/rclone/rclone/backend/yandex"
- _ "github.com/rclone/rclone/backend/zoho"
+_ "github.com/rclone/rclone/backend/alias"
+_ "github.com/rclone/rclone/backend/alist"
+_ "github.com/rclone/rclone/backend/alldebrid"
+_ "github.com/rclone/rclone/backend/azureblob"
+_ "github.com/rclone/rclone/backend/azurefiles"
+_ "github.com/rclone/rclone/backend/b2"
+_ "github.com/rclone/rclone/backend/box"
+_ "github.com/rclone/rclone/backend/cache"
+_ "github.com/rclone/rclone/backend/chunker"
+_ "github.com/rclone/rclone/backend/cloudinary"
+_ "github.com/rclone/rclone/backend/combine"
+_ "github.com/rclone/rclone/backend/compress"
+_ "github.com/rclone/rclone/backend/crypt"
+_ "github.com/rclone/rclone/backend/doi"
+_ "github.com/rclone/rclone/backend/drive"
+_ "github.com/rclone/rclone/backend/dropbox"
+_ "github.com/rclone/rclone/backend/fichier"
+_ "github.com/rclone/rclone/backend/filefabric"
+_ "github.com/rclone/rclone/backend/filelu"
+_ "github.com/rclone/rclone/backend/filescom"
+_ "github.com/rclone/rclone/backend/ftp"
+_ "github.com/rclone/rclone/backend/gofile"
+_ "github.com/rclone/rclone/backend/googlecloudstorage"
+_ "github.com/rclone/rclone/backend/googlephotos"
+_ "github.com/rclone/rclone/backend/hasher"
+_ "github.com/rclone/rclone/backend/hdfs"
+_ "github.com/rclone/rclone/backend/hidrive"
+_ "github.com/rclone/rclone/backend/http"
+_ "github.com/rclone/rclone/backend/iclouddrive"
+_ "github.com/rclone/rclone/backend/imagekit"
+_ "github.com/rclone/rclone/backend/internetarchive"
+_ "github.com/rclone/rclone/backend/jottacloud"
+_ "github.com/rclone/rclone/backend/koofr"
+_ "github.com/rclone/rclone/backend/linkbox"
+_ "github.com/rclone/rclone/backend/local"
+_ "github.com/rclone/rclone/backend/mailru"
+_ "github.com/rclone/rclone/backend/mega"
+_ "github.com/rclone/rclone/backend/memory"
+_ "github.com/rclone/rclone/backend/netstorage"
+_ "github.com/rclone/rclone/backend/onedrive"
+_ "github.com/rclone/rclone/backend/opendrive"
+_ "github.com/rclone/rclone/backend/oracleobjectstorage"
+_ "github.com/rclone/rclone/backend/pcloud"
+_ "github.com/rclone/rclone/backend/pikpak"
+_ "github.com/rclone/rclone/backend/pixeldrain"
+_ "github.com/rclone/rclone/backend/premiumizeme"
+_ "github.com/rclone/rclone/backend/protondrive"
+_ "github.com/rclone/rclone/backend/putio"
+_ "github.com/rclone/rclone/backend/qingstor"
+_ "github.com/rclone/rclone/backend/quatrix"
+_ "github.com/rclone/rclone/backend/s3"
+_ "github.com/rclone/rclone/backend/seafile"
+_ "github.com/rclone/rclone/backend/sftp"
+_ "github.com/rclone/rclone/backend/sharefile"
+_ "github.com/rclone/rclone/backend/sia"
+_ "github.com/rclone/rclone/backend/smb"
+_ "github.com/rclone/rclone/backend/storj"
+_ "github.com/rclone/rclone/backend/sugarsync"
+_ "github.com/rclone/rclone/backend/swift"
+_ "github.com/rclone/rclone/backend/teldrive"
+_ "github.com/rclone/rclone/backend/terabox"
+_ "github.com/rclone/rclone/backend/ulozto"
+_ "github.com/rclone/rclone/backend/union"
+_ "github.com/rclone/rclone/backend/uptobox"
+_ "github.com/rclone/rclone/backend/webdav"
+_ "github.com/rclone/rclone/backend/yandex"
+_ "github.com/rclone/rclone/backend/zoho"
)
diff --git a/backend/alldebrid/alldebrid.go b/backend/alldebrid/alldebrid.go
index 9838e4b66..3404a48ce 100644
--- a/backend/alldebrid/alldebrid.go
+++ b/backend/alldebrid/alldebrid.go
@@ -1,4 +1,4 @@
-// Package alldebrid provides an interface to the alldebrid.com
+// Package alldebrid provides an interface to the alldebrid.com
// object storage system.
package alldebrid
diff --git a/backend/alldebrid/alldebrid_test.go b/backend/alldebrid/alldebrid_test.go
index 3b28ccc03..70cae3f29 100644
--- a/backend/alldebrid/alldebrid_test.go
+++ b/backend/alldebrid/alldebrid_test.go
@@ -1,4 +1,4 @@
-package alldebrid_test
+package alldebrid_test
import (
"testing"
diff --git a/backend/alldebrid/api/types.go b/backend/alldebrid/api/types.go
index b847895d2..149a07403 100644
--- a/backend/alldebrid/api/types.go
+++ b/backend/alldebrid/api/types.go
@@ -1,4 +1,4 @@
-// Package api contains definitions for using the alldebrid API
+// Package api contains definitions for using the alldebrid API
package api
import "fmt"
diff --git a/backend/azureblob/azureblob.go b/backend/azureblob/azureblob.go
index 963c11018..fc2919c7e 100644
--- a/backend/azureblob/azureblob.go
+++ b/backend/azureblob/azureblob.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !solaris && !js
+//go:build !plan9 && !solaris && !js
// Package azureblob provides an interface to the Microsoft Azure blob object storage system
package azureblob
@@ -2152,7 +2152,6 @@ func (o *Object) getMetadata() (metadata map[string]*string) {
}
metadata = make(map[string]*string, len(o.meta))
for k, v := range o.meta {
- v := v
metadata[k] = &v
}
return metadata
@@ -2798,8 +2797,6 @@ func (o *Object) clearUncommittedBlocks(ctx context.Context) (err error) {
blockList blockblob.GetBlockListResponse
properties *blob.GetPropertiesResponse
options *blockblob.CommitBlockListOptions
- // Use temporary pacer as this can be called recursively which can cause a deadlock with --max-connections
- pacer = fs.NewPacer(ctx, pacer.NewS3(pacer.MinSleep(minSleep), pacer.MaxSleep(maxSleep), pacer.DecayConstant(decayConstant)))
)
properties, err = o.readMetaDataAlways(ctx)
@@ -2811,7 +2808,7 @@ func (o *Object) clearUncommittedBlocks(ctx context.Context) (err error) {
if objectExists {
// Get the committed block list
- err = pacer.Call(func() (bool, error) {
+ err = o.fs.pacer.Call(func() (bool, error) {
blockList, err = blockBlobSVC.GetBlockList(ctx, blockblob.BlockListTypeAll, nil)
return o.fs.shouldRetry(ctx, err)
})
@@ -2853,7 +2850,7 @@ func (o *Object) clearUncommittedBlocks(ctx context.Context) (err error) {
// Commit only the committed blocks
fs.Debugf(o, "Committing %d blocks to remove uncommitted blocks", len(blockIDs))
- err = pacer.Call(func() (bool, error) {
+ err = o.fs.pacer.Call(func() (bool, error) {
_, err := blockBlobSVC.CommitBlockList(ctx, blockIDs, options)
return o.fs.shouldRetry(ctx, err)
})
diff --git a/backend/azureblob/azureblob_internal_test.go b/backend/azureblob/azureblob_internal_test.go
index 67669775d..adbf4d249 100644
--- a/backend/azureblob/azureblob_internal_test.go
+++ b/backend/azureblob/azureblob_internal_test.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !solaris && !js
+//go:build !plan9 && !solaris && !js
package azureblob
diff --git a/backend/azureblob/azureblob_test.go b/backend/azureblob/azureblob_test.go
index 6f2b65acf..984426753 100644
--- a/backend/azureblob/azureblob_test.go
+++ b/backend/azureblob/azureblob_test.go
@@ -1,4 +1,4 @@
-// Test AzureBlob filesystem interface
+// Test AzureBlob filesystem interface
//go:build !plan9 && !solaris && !js
diff --git a/backend/azureblob/azureblob_unsupported.go b/backend/azureblob/azureblob_unsupported.go
index 50fcea34c..428cf2292 100644
--- a/backend/azureblob/azureblob_unsupported.go
+++ b/backend/azureblob/azureblob_unsupported.go
@@ -1,4 +1,4 @@
-// Build for azureblob for unsupported platforms to stop go complaining
+// Build for azureblob for unsupported platforms to stop go complaining
// about "no buildable Go source files "
//go:build plan9 || solaris || js
diff --git a/backend/azurefiles/azurefiles.go b/backend/azurefiles/azurefiles.go
index a9fed6e2b..1d65b8866 100644
--- a/backend/azurefiles/azurefiles.go
+++ b/backend/azurefiles/azurefiles.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !js
+//go:build !plan9 && !js
// Package azurefiles provides an interface to Microsoft Azure Files
package azurefiles
@@ -1313,10 +1313,29 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object,
}
srcURL := srcObj.fileClient().URL()
fc := f.fileClient(remote)
- _, err = fc.StartCopyFromURL(ctx, srcURL, &opt)
+ startCopy, err := fc.StartCopyFromURL(ctx, srcURL, &opt)
if err != nil {
return nil, fmt.Errorf("Copy failed: %w", err)
}
+
+ // Poll for completion if necessary
+ //
+ // The for loop is never executed for same storage account copies.
+ copyStatus := startCopy.CopyStatus
+ var properties file.GetPropertiesResponse
+ pollTime := 100 * time.Millisecond
+
+ for copyStatus != nil && string(*copyStatus) == string(file.CopyStatusTypePending) {
+ time.Sleep(pollTime)
+
+ properties, err = fc.GetProperties(ctx, &file.GetPropertiesOptions{})
+ if err != nil {
+ return nil, err
+ }
+ copyStatus = properties.CopyStatus
+ pollTime = min(2*pollTime, time.Second)
+ }
+
dstObj, err := f.NewObject(ctx, remote)
if err != nil {
return nil, fmt.Errorf("Copy: NewObject failed: %w", err)
diff --git a/backend/azurefiles/azurefiles_internal_test.go b/backend/azurefiles/azurefiles_internal_test.go
index 4996267a1..b98ffba6f 100644
--- a/backend/azurefiles/azurefiles_internal_test.go
+++ b/backend/azurefiles/azurefiles_internal_test.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !js
+//go:build !plan9 && !js
package azurefiles
diff --git a/backend/azurefiles/azurefiles_test.go b/backend/azurefiles/azurefiles_test.go
index 987d08494..2a15287d3 100644
--- a/backend/azurefiles/azurefiles_test.go
+++ b/backend/azurefiles/azurefiles_test.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !js
+//go:build !plan9 && !js
package azurefiles
diff --git a/backend/azurefiles/azurefiles_unsupported.go b/backend/azurefiles/azurefiles_unsupported.go
index d2723e62f..b8029954a 100644
--- a/backend/azurefiles/azurefiles_unsupported.go
+++ b/backend/azurefiles/azurefiles_unsupported.go
@@ -1,4 +1,4 @@
-// Build for azurefiles for unsupported platforms to stop go complaining
+// Build for azurefiles for unsupported platforms to stop go complaining
// about "no buildable Go source files "
//go:build plan9 || js
diff --git a/backend/b2/api/types.go b/backend/b2/api/types.go
index b221d4c2d..5ebc807f5 100644
--- a/backend/b2/api/types.go
+++ b/backend/b2/api/types.go
@@ -1,4 +1,4 @@
-// Package api provides types used by the Backblaze B2 API.
+// Package api provides types used by the Backblaze B2 API.
package api
import (
diff --git a/backend/b2/api/types_test.go b/backend/b2/api/types_test.go
index f350745fc..ed11f4378 100644
--- a/backend/b2/api/types_test.go
+++ b/backend/b2/api/types_test.go
@@ -1,4 +1,4 @@
-package api_test
+package api_test
import (
"testing"
diff --git a/backend/b2/b2.go b/backend/b2/b2.go
index d87948e37..f5030c1a2 100644
--- a/backend/b2/b2.go
+++ b/backend/b2/b2.go
@@ -1,4 +1,4 @@
-// Package b2 provides an interface to the Backblaze B2 object storage system.
+// Package b2 provides an interface to the Backblaze B2 object storage system.
package b2
// FIXME should we remove sha1 checks from here as rclone now supports
@@ -2224,13 +2224,17 @@ func (f *Fs) OpenChunkWriter(ctx context.Context, remote string, src fs.ObjectIn
return info, nil, err
}
+ up, err := f.newLargeUpload(ctx, o, nil, src, f.opt.ChunkSize, false, nil, options...)
+ if err != nil {
+ return info, nil, err
+ }
+
info = fs.ChunkWriterInfo{
- ChunkSize: int64(f.opt.ChunkSize),
+ ChunkSize: up.chunkSize,
Concurrency: o.fs.opt.UploadConcurrency,
//LeavePartsOnError: o.fs.opt.LeavePartsOnError,
}
- up, err := f.newLargeUpload(ctx, o, nil, src, f.opt.ChunkSize, false, nil, options...)
- return info, up, err
+ return info, up, nil
}
// Remove an object
diff --git a/backend/b2/b2_internal_test.go b/backend/b2/b2_internal_test.go
index 2579928be..8c1dda742 100644
--- a/backend/b2/b2_internal_test.go
+++ b/backend/b2/b2_internal_test.go
@@ -1,4 +1,4 @@
-package b2
+package b2
import (
"context"
diff --git a/backend/b2/b2_test.go b/backend/b2/b2_test.go
index 43e28e5b1..08a0caef3 100644
--- a/backend/b2/b2_test.go
+++ b/backend/b2/b2_test.go
@@ -1,4 +1,4 @@
-// Test B2 filesystem interface
+// Test B2 filesystem interface
package b2
import (
diff --git a/backend/b2/upload.go b/backend/b2/upload.go
index e2cd4a1c2..19efab555 100644
--- a/backend/b2/upload.go
+++ b/backend/b2/upload.go
@@ -1,4 +1,4 @@
-// Upload large files for b2
+// Upload large files for b2
//
// Docs - https://www.backblaze.com/docs/cloud-storage-large-files
diff --git a/backend/box/api/types.go b/backend/box/api/types.go
index cdb292837..1dc2ef0f0 100644
--- a/backend/box/api/types.go
+++ b/backend/box/api/types.go
@@ -1,4 +1,4 @@
-// Package api has type definitions for box
+// Package api has type definitions for box
//
// Converted from the API docs with help from https://mholt.github.io/json-to-go/
package api
diff --git a/backend/box/box.go b/backend/box/box.go
index c21376837..e9033f55f 100644
--- a/backend/box/box.go
+++ b/backend/box/box.go
@@ -1,4 +1,4 @@
-// Package box provides an interface to the Box
+// Package box provides an interface to the Box
// object storage system.
package box
diff --git a/backend/box/box_test.go b/backend/box/box_test.go
index 67e6d4c6a..0bca9f834 100644
--- a/backend/box/box_test.go
+++ b/backend/box/box_test.go
@@ -1,4 +1,4 @@
-// Test Box filesystem interface
+// Test Box filesystem interface
package box_test
import (
diff --git a/backend/box/upload.go b/backend/box/upload.go
index a9b3a9f9e..f9f034c62 100644
--- a/backend/box/upload.go
+++ b/backend/box/upload.go
@@ -1,4 +1,4 @@
-// multipart upload for box
+// multipart upload for box
package box
diff --git a/backend/cache/cache.go b/backend/cache/cache.go
index 6977ff2d7..7ea9b5a18 100644
--- a/backend/cache/cache.go
+++ b/backend/cache/cache.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !js
+//go:build !plan9 && !js
// Package cache implements a virtual provider to cache existing remotes.
package cache
@@ -684,7 +684,7 @@ func (f *Fs) rcFetch(ctx context.Context, in rc.Params) (rc.Params, error) {
start, end int64
}
parseChunks := func(ranges string) (crs []chunkRange, err error) {
- for _, part := range strings.Split(ranges, ",") {
+ for part := range strings.SplitSeq(ranges, ",") {
var start, end int64 = 0, math.MaxInt64
switch ints := strings.Split(part, ":"); len(ints) {
case 1:
diff --git a/backend/cache/cache_internal_test.go b/backend/cache/cache_internal_test.go
index 0138d8d83..3984ab373 100644
--- a/backend/cache/cache_internal_test.go
+++ b/backend/cache/cache_internal_test.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !js && !race
+//go:build !plan9 && !js && !race
package cache_test
diff --git a/backend/cache/cache_test.go b/backend/cache/cache_test.go
index 96b7e36ae..a4ca3b0e9 100644
--- a/backend/cache/cache_test.go
+++ b/backend/cache/cache_test.go
@@ -1,4 +1,4 @@
-// Test Cache filesystem interface
+// Test Cache filesystem interface
//go:build !plan9 && !js && !race
diff --git a/backend/cache/cache_unsupported.go b/backend/cache/cache_unsupported.go
index 8c94aaf26..9e1b532ec 100644
--- a/backend/cache/cache_unsupported.go
+++ b/backend/cache/cache_unsupported.go
@@ -1,4 +1,4 @@
-// Build for cache for unsupported platforms to stop go complaining
+// Build for cache for unsupported platforms to stop go complaining
// about "no buildable Go source files "
//go:build plan9 || js
diff --git a/backend/cache/cache_upload_test.go b/backend/cache/cache_upload_test.go
index a47a1792a..58823b840 100644
--- a/backend/cache/cache_upload_test.go
+++ b/backend/cache/cache_upload_test.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !js && !race
+//go:build !plan9 && !js && !race
package cache_test
diff --git a/backend/cache/directory.go b/backend/cache/directory.go
index 5c45db9c7..c05de40f9 100644
--- a/backend/cache/directory.go
+++ b/backend/cache/directory.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !js
+//go:build !plan9 && !js
package cache
diff --git a/backend/cache/handle.go b/backend/cache/handle.go
index c12080043..71a43d222 100644
--- a/backend/cache/handle.go
+++ b/backend/cache/handle.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !js
+//go:build !plan9 && !js
package cache
diff --git a/backend/cache/object.go b/backend/cache/object.go
index 87c292804..6cddf19e9 100644
--- a/backend/cache/object.go
+++ b/backend/cache/object.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !js
+//go:build !plan9 && !js
package cache
diff --git a/backend/cache/plex.go b/backend/cache/plex.go
index ff686f793..f0c6ec63b 100644
--- a/backend/cache/plex.go
+++ b/backend/cache/plex.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !js
+//go:build !plan9 && !js
package cache
diff --git a/backend/cache/storage_memory.go b/backend/cache/storage_memory.go
index 2b2acf8f4..07d6dc952 100644
--- a/backend/cache/storage_memory.go
+++ b/backend/cache/storage_memory.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !js
+//go:build !plan9 && !js
package cache
diff --git a/backend/cache/storage_persistent.go b/backend/cache/storage_persistent.go
index 737b2e4ae..e606c34ef 100644
--- a/backend/cache/storage_persistent.go
+++ b/backend/cache/storage_persistent.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !js
+//go:build !plan9 && !js
package cache
diff --git a/backend/cache/utils_test.go b/backend/cache/utils_test.go
index 9f85b0155..1231741d4 100644
--- a/backend/cache/utils_test.go
+++ b/backend/cache/utils_test.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !js
+//go:build !plan9 && !js
// +build !plan9,!js
package cache
diff --git a/backend/chunker/chunker.go b/backend/chunker/chunker.go
index 95d32d9be..0d27ba32a 100644
--- a/backend/chunker/chunker.go
+++ b/backend/chunker/chunker.go
@@ -1,4 +1,4 @@
-// Package chunker provides wrappers for Fs and Object which split large files in chunks
+// Package chunker provides wrappers for Fs and Object which split large files in chunks
package chunker
import (
diff --git a/backend/chunker/chunker_internal_test.go b/backend/chunker/chunker_internal_test.go
index 19f665fd8..d95f5c85d 100644
--- a/backend/chunker/chunker_internal_test.go
+++ b/backend/chunker/chunker_internal_test.go
@@ -1,4 +1,4 @@
-package chunker
+package chunker
import (
"bytes"
diff --git a/backend/chunker/chunker_test.go b/backend/chunker/chunker_test.go
index 05a40783b..da85e5ee2 100644
--- a/backend/chunker/chunker_test.go
+++ b/backend/chunker/chunker_test.go
@@ -1,4 +1,4 @@
-// Test the Chunker filesystem interface
+// Test the Chunker filesystem interface
package chunker_test
import (
diff --git a/backend/cloudinary/api/types.go b/backend/cloudinary/api/types.go
index 0c9bc1678..2b99ce7a9 100644
--- a/backend/cloudinary/api/types.go
+++ b/backend/cloudinary/api/types.go
@@ -1,4 +1,4 @@
-// Package api has type definitions for cloudinary
+// Package api has type definitions for cloudinary
package api
import (
diff --git a/backend/cloudinary/cloudinary.go b/backend/cloudinary/cloudinary.go
index 5a80439b2..6724090c4 100644
--- a/backend/cloudinary/cloudinary.go
+++ b/backend/cloudinary/cloudinary.go
@@ -1,4 +1,4 @@
-// Package cloudinary provides an interface to the Cloudinary DAM
+// Package cloudinary provides an interface to the Cloudinary DAM
package cloudinary
import (
diff --git a/backend/cloudinary/cloudinary_test.go b/backend/cloudinary/cloudinary_test.go
index 009771eaa..43fe0c457 100644
--- a/backend/cloudinary/cloudinary_test.go
+++ b/backend/cloudinary/cloudinary_test.go
@@ -1,4 +1,4 @@
-// Test Cloudinary filesystem interface
+// Test Cloudinary filesystem interface
package cloudinary_test
diff --git a/backend/combine/combine.go b/backend/combine/combine.go
index 80e2d6d0f..42c7b91ed 100644
--- a/backend/combine/combine.go
+++ b/backend/combine/combine.go
@@ -1,4 +1,4 @@
-// Package combine implements a backend to combine multiple remotes in a directory tree
+// Package combine implements a backend to combine multiple remotes in a directory tree
package combine
/*
@@ -187,7 +187,6 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (outFs fs
g, gCtx := errgroup.WithContext(ctx)
var mu sync.Mutex
for _, upstream := range opt.Upstreams {
- upstream := upstream
g.Go(func() (err error) {
equal := strings.IndexRune(upstream, '=')
if equal < 0 {
@@ -370,7 +369,6 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (outFs fs
func (f *Fs) multithread(ctx context.Context, fn func(context.Context, *upstream) error) error {
g, gCtx := errgroup.WithContext(ctx)
for _, u := range f.upstreams {
- u := u
g.Go(func() (err error) {
return fn(gCtx, u)
})
@@ -637,7 +635,6 @@ func (f *Fs) ChangeNotify(ctx context.Context, notifyFunc func(string, fs.EntryT
var uChans []chan time.Duration
for _, u := range f.upstreams {
- u := u
if do := u.f.Features().ChangeNotify; do != nil {
ch := make(chan time.Duration)
uChans = append(uChans, ch)
diff --git a/backend/combine/combine_internal_test.go b/backend/combine/combine_internal_test.go
index 5c889bb8c..005efca61 100644
--- a/backend/combine/combine_internal_test.go
+++ b/backend/combine/combine_internal_test.go
@@ -1,4 +1,4 @@
-package combine
+package combine
import (
"fmt"
diff --git a/backend/combine/combine_test.go b/backend/combine/combine_test.go
index c132377b1..dfb1f707a 100644
--- a/backend/combine/combine_test.go
+++ b/backend/combine/combine_test.go
@@ -1,4 +1,4 @@
-// Test Combine filesystem interface
+// Test Combine filesystem interface
package combine_test
import (
diff --git a/backend/compress/compress.go b/backend/compress/compress.go
index 0cc56bc62..569b6231e 100644
--- a/backend/compress/compress.go
+++ b/backend/compress/compress.go
@@ -1,4 +1,4 @@
-// Package compress provides wrappers for Fs and Object which implement compression.
+// Package compress provides wrappers for Fs and Object which implement compression.
package compress
import (
diff --git a/backend/compress/compress_test.go b/backend/compress/compress_test.go
index 8fc954dfa..0dbef4093 100644
--- a/backend/compress/compress_test.go
+++ b/backend/compress/compress_test.go
@@ -1,4 +1,4 @@
-// Test Crypt filesystem interface
+// Test Crypt filesystem interface
package compress
import (
diff --git a/backend/crypt/cipher.go b/backend/crypt/cipher.go
index c9a3786ef..05cb54662 100644
--- a/backend/crypt/cipher.go
+++ b/backend/crypt/cipher.go
@@ -1,4 +1,4 @@
-package crypt
+package crypt
import (
"bytes"
diff --git a/backend/crypt/cipher_test.go b/backend/crypt/cipher_test.go
index fe2edc6f9..23064e3c1 100644
--- a/backend/crypt/cipher_test.go
+++ b/backend/crypt/cipher_test.go
@@ -1,4 +1,4 @@
-package crypt
+package crypt
import (
"bytes"
diff --git a/backend/crypt/crypt.go b/backend/crypt/crypt.go
index 976648fdc..30b463bc2 100644
--- a/backend/crypt/crypt.go
+++ b/backend/crypt/crypt.go
@@ -1,4 +1,4 @@
-// Package crypt provides wrappers for Fs and Object which implement encryption
+// Package crypt provides wrappers for Fs and Object which implement encryption
package crypt
import (
diff --git a/backend/crypt/crypt_internal_test.go b/backend/crypt/crypt_internal_test.go
index a6eaecbc5..f96d9bba1 100644
--- a/backend/crypt/crypt_internal_test.go
+++ b/backend/crypt/crypt_internal_test.go
@@ -1,4 +1,4 @@
-package crypt
+package crypt
import (
"bytes"
diff --git a/backend/crypt/crypt_test.go b/backend/crypt/crypt_test.go
index 49db07dc7..f9eb4fd1d 100644
--- a/backend/crypt/crypt_test.go
+++ b/backend/crypt/crypt_test.go
@@ -1,4 +1,4 @@
-// Test Crypt filesystem interface
+// Test Crypt filesystem interface
package crypt_test
import (
diff --git a/backend/crypt/pkcs7/pkcs7.go b/backend/crypt/pkcs7/pkcs7.go
index b92c0073c..c6607ec60 100644
--- a/backend/crypt/pkcs7/pkcs7.go
+++ b/backend/crypt/pkcs7/pkcs7.go
@@ -1,4 +1,4 @@
-// Package pkcs7 implements PKCS#7 padding
+// Package pkcs7 implements PKCS#7 padding
//
// This is a standard way of encoding variable length buffers into
// buffers which are a multiple of an underlying crypto block size.
diff --git a/backend/crypt/pkcs7/pkcs7_test.go b/backend/crypt/pkcs7/pkcs7_test.go
index 2264c7fd3..c4590f78d 100644
--- a/backend/crypt/pkcs7/pkcs7_test.go
+++ b/backend/crypt/pkcs7/pkcs7_test.go
@@ -1,4 +1,4 @@
-package pkcs7
+package pkcs7
import (
"fmt"
diff --git a/backend/doi/api/dataversetypes.go b/backend/doi/api/dataversetypes.go
index 1df00858c..4ba76a7e5 100644
--- a/backend/doi/api/dataversetypes.go
+++ b/backend/doi/api/dataversetypes.go
@@ -1,4 +1,4 @@
-// Type definitions specific to Dataverse
+// Type definitions specific to Dataverse
package api
diff --git a/backend/doi/api/inveniotypes.go b/backend/doi/api/inveniotypes.go
index bdb866451..b82a81761 100644
--- a/backend/doi/api/inveniotypes.go
+++ b/backend/doi/api/inveniotypes.go
@@ -1,4 +1,4 @@
-// Type definitions specific to InvenioRDM
+// Type definitions specific to InvenioRDM
package api
diff --git a/backend/doi/api/types.go b/backend/doi/api/types.go
index 202cbf6ec..77c05af56 100644
--- a/backend/doi/api/types.go
+++ b/backend/doi/api/types.go
@@ -1,4 +1,4 @@
-// Package api has general type definitions for doi
+// Package api has general type definitions for doi
package api
// DoiResolverResponse is returned by the DOI resolver API
diff --git a/backend/doi/dataverse.go b/backend/doi/dataverse.go
index 40b4f4884..49c53a263 100644
--- a/backend/doi/dataverse.go
+++ b/backend/doi/dataverse.go
@@ -1,4 +1,4 @@
-// Implementation for Dataverse
+// Implementation for Dataverse
package doi
diff --git a/backend/doi/doi.go b/backend/doi/doi.go
index 00e98c494..3c4088fe3 100644
--- a/backend/doi/doi.go
+++ b/backend/doi/doi.go
@@ -1,4 +1,4 @@
-// Package doi provides a filesystem interface for digital objects identified by DOIs.
+// Package doi provides a filesystem interface for digital objects identified by DOIs.
//
// See: https://www.doi.org/the-identifier/what-is-a-doi/
package doi
@@ -598,7 +598,7 @@ It doesn't return anything.
// The result should be capable of being JSON encoded
// If it is a string or a []string it will be shown to the user
// otherwise it will be JSON encoded and shown to the user like that
-func (f *Fs) Command(ctx context.Context, name string, arg []string, opt map[string]string) (out interface{}, err error) {
+func (f *Fs) Command(ctx context.Context, name string, arg []string, opt map[string]string) (out any, err error) {
switch name {
case "metadata":
return f.ShowMetadata(ctx)
@@ -625,7 +625,7 @@ func (f *Fs) Command(ctx context.Context, name string, arg []string, opt map[str
}
// ShowMetadata returns some metadata about the corresponding DOI
-func (f *Fs) ShowMetadata(ctx context.Context) (metadata interface{}, err error) {
+func (f *Fs) ShowMetadata(ctx context.Context) (metadata any, err error) {
doiURL, err := url.Parse("https://doi.org/" + f.opt.Doi)
if err != nil {
return nil, err
diff --git a/backend/doi/doi_internal_test.go b/backend/doi/doi_internal_test.go
index 22046be09..ca3fd8647 100644
--- a/backend/doi/doi_internal_test.go
+++ b/backend/doi/doi_internal_test.go
@@ -1,4 +1,4 @@
-package doi
+package doi
import (
"context"
diff --git a/backend/doi/doi_test.go b/backend/doi/doi_test.go
index 1d9dfc094..253eeb431 100644
--- a/backend/doi/doi_test.go
+++ b/backend/doi/doi_test.go
@@ -1,4 +1,4 @@
-// Test DOI filesystem interface
+// Test DOI filesystem interface
package doi
import (
diff --git a/backend/doi/invenio.go b/backend/doi/invenio.go
index 1f06f67d5..a4ac43471 100644
--- a/backend/doi/invenio.go
+++ b/backend/doi/invenio.go
@@ -1,4 +1,4 @@
-// Implementation for InvenioRDM
+// Implementation for InvenioRDM
package doi
diff --git a/backend/doi/link_header.go b/backend/doi/link_header.go
index 423ea8d89..ff1f1ae86 100644
--- a/backend/doi/link_header.go
+++ b/backend/doi/link_header.go
@@ -1,4 +1,4 @@
-package doi
+package doi
import (
"regexp"
@@ -18,7 +18,7 @@ type headerLink struct {
}
func parseLinkHeader(header string) (links []headerLink) {
- for _, link := range strings.Split(header, ",") {
+ for link := range strings.SplitSeq(header, ",") {
link = strings.TrimSpace(link)
parsed := parseLink(link)
if parsed != nil {
@@ -30,7 +30,7 @@ func parseLinkHeader(header string) (links []headerLink) {
func parseLink(link string) (parsedLink *headerLink) {
var parts []string
- for _, part := range strings.Split(link, ";") {
+ for part := range strings.SplitSeq(link, ";") {
parts = append(parts, strings.TrimSpace(part))
}
diff --git a/backend/doi/link_header_internal_test.go b/backend/doi/link_header_internal_test.go
index 857256b0e..669a950ba 100644
--- a/backend/doi/link_header_internal_test.go
+++ b/backend/doi/link_header_internal_test.go
@@ -1,4 +1,4 @@
-package doi
+package doi
import (
"testing"
diff --git a/backend/doi/zenodo.go b/backend/doi/zenodo.go
index 21f5c22e6..3ce12bf54 100644
--- a/backend/doi/zenodo.go
+++ b/backend/doi/zenodo.go
@@ -1,4 +1,4 @@
-// Implementation for Zenodo
+// Implementation for Zenodo
package doi
diff --git a/backend/drive/drive.go b/backend/drive/drive.go
index 388444aad..f81a7b1a7 100644
--- a/backend/drive/drive.go
+++ b/backend/drive/drive.go
@@ -1,4 +1,4 @@
-// Package drive interfaces with the Google Drive object storage system
+// Package drive interfaces with the Google Drive object storage system
package drive
// FIXME need to deal with some corner cases
@@ -191,7 +191,7 @@ func driveScopes(scopesString string) (scopes []string) {
if scopesString == "" {
scopesString = defaultScope
}
- for _, scope := range strings.Split(scopesString, ",") {
+ for scope := range strings.SplitSeq(scopesString, ",") {
scope = strings.TrimSpace(scope)
scopes = append(scopes, scopePrefix+scope)
}
@@ -1220,7 +1220,7 @@ func isLinkMimeType(mimeType string) bool {
// into a list of unique extensions with leading "." and a list of associated MIME types
func parseExtensions(extensionsIn ...string) (extensions, mimeTypes []string, err error) {
for _, extensionText := range extensionsIn {
- for _, extension := range strings.Split(extensionText, ",") {
+ for extension := range strings.SplitSeq(extensionText, ",") {
extension = strings.ToLower(strings.TrimSpace(extension))
if extension == "" {
continue
diff --git a/backend/drive/drive_internal_test.go b/backend/drive/drive_internal_test.go
index 976ceb50e..db8338297 100644
--- a/backend/drive/drive_internal_test.go
+++ b/backend/drive/drive_internal_test.go
@@ -1,4 +1,4 @@
-package drive
+package drive
import (
"bytes"
diff --git a/backend/drive/drive_test.go b/backend/drive/drive_test.go
index d3c7b214b..6e35acebb 100644
--- a/backend/drive/drive_test.go
+++ b/backend/drive/drive_test.go
@@ -1,4 +1,4 @@
-// Test Drive filesystem interface
+// Test Drive filesystem interface
package drive
diff --git a/backend/drive/metadata.go b/backend/drive/metadata.go
index 3f2800e49..04407a5e5 100644
--- a/backend/drive/metadata.go
+++ b/backend/drive/metadata.go
@@ -1,4 +1,4 @@
-package drive
+package drive
import (
"context"
@@ -386,7 +386,6 @@ func (o *baseObject) parseMetadata(ctx context.Context, info *drive.File) (err e
g.SetLimit(o.fs.ci.Checkers)
var mu sync.Mutex // protect the info.Permissions from concurrent writes
for _, permissionID := range info.PermissionIds {
- permissionID := permissionID
g.Go(func() error {
// must fetch the team drive ones individually to check the inherited flag
perm, inherited, err := o.fs.getPermission(gCtx, actualID(info.Id), permissionID, !o.fs.isTeamDrive)
@@ -520,7 +519,6 @@ func (f *Fs) updateMetadata(ctx context.Context, updateInfo *drive.File, meta fs
}
// merge metadata into request and user metadata
for k, v := range meta {
- k, v := k, v
// parse a boolean from v and write into out
parseBool := func(out *bool) error {
b, err := strconv.ParseBool(v)
diff --git a/backend/drive/upload.go b/backend/drive/upload.go
index 10b3ab506..aa2487fa2 100644
--- a/backend/drive/upload.go
+++ b/backend/drive/upload.go
@@ -1,4 +1,4 @@
-// Upload for drive
+// Upload for drive
//
// Docs
// Resumable upload: https://developers.google.com/drive/web/manage-uploads#resumable
diff --git a/backend/dropbox/batcher.go b/backend/dropbox/batcher.go
index 8f5aba5f4..7d5184903 100644
--- a/backend/dropbox/batcher.go
+++ b/backend/dropbox/batcher.go
@@ -1,4 +1,4 @@
-// This file contains the implementation of the sync batcher for uploads
+// This file contains the implementation of the sync batcher for uploads
//
// Dropbox rules say you can start as many batches as you want, but
// you may only have one batch being committed and must wait for the
diff --git a/backend/dropbox/dbhash/dbhash.go b/backend/dropbox/dbhash/dbhash.go
index 9a1ab3c83..9d3ef60d6 100644
--- a/backend/dropbox/dbhash/dbhash.go
+++ b/backend/dropbox/dbhash/dbhash.go
@@ -1,4 +1,4 @@
-// Package dbhash implements the dropbox hash as described in
+// Package dbhash implements the dropbox hash as described in
//
// https://www.dropbox.com/developers/reference/content-hash
package dbhash
diff --git a/backend/dropbox/dbhash/dbhash_test.go b/backend/dropbox/dbhash/dbhash_test.go
index 3990628a5..2f3faa334 100644
--- a/backend/dropbox/dbhash/dbhash_test.go
+++ b/backend/dropbox/dbhash/dbhash_test.go
@@ -1,4 +1,4 @@
-package dbhash_test
+package dbhash_test
import (
"encoding/hex"
diff --git a/backend/dropbox/dropbox.go b/backend/dropbox/dropbox.go
index f67a9e261..8326d48d6 100644
--- a/backend/dropbox/dropbox.go
+++ b/backend/dropbox/dropbox.go
@@ -1,4 +1,4 @@
-// Package dropbox provides an interface to Dropbox object storage
+// Package dropbox provides an interface to Dropbox object storage
package dropbox
// FIXME dropbox for business would be quite easy to add
diff --git a/backend/dropbox/dropbox_internal_test.go b/backend/dropbox/dropbox_internal_test.go
index 431660ab3..a6543c07a 100644
--- a/backend/dropbox/dropbox_internal_test.go
+++ b/backend/dropbox/dropbox_internal_test.go
@@ -1,4 +1,4 @@
-package dropbox
+package dropbox
import (
"context"
diff --git a/backend/dropbox/dropbox_test.go b/backend/dropbox/dropbox_test.go
index 673082131..c3d5826da 100644
--- a/backend/dropbox/dropbox_test.go
+++ b/backend/dropbox/dropbox_test.go
@@ -1,4 +1,4 @@
-// Test Dropbox filesystem interface
+// Test Dropbox filesystem interface
package dropbox
import (
diff --git a/backend/fichier/api.go b/backend/fichier/api.go
index c9c5f8cdd..fef5e3fc6 100644
--- a/backend/fichier/api.go
+++ b/backend/fichier/api.go
@@ -1,4 +1,4 @@
-package fichier
+package fichier
import (
"context"
diff --git a/backend/fichier/fichier.go b/backend/fichier/fichier.go
index 5ebc8a6db..54ad1e21d 100644
--- a/backend/fichier/fichier.go
+++ b/backend/fichier/fichier.go
@@ -1,4 +1,4 @@
-// Package fichier provides an interface to the 1Fichier storage system.
+// Package fichier provides an interface to the 1Fichier storage system.
package fichier
import (
diff --git a/backend/fichier/fichier_test.go b/backend/fichier/fichier_test.go
index 531e6e9d3..9bbc8d9b0 100644
--- a/backend/fichier/fichier_test.go
+++ b/backend/fichier/fichier_test.go
@@ -1,4 +1,4 @@
-// Test 1Fichier filesystem interface
+// Test 1Fichier filesystem interface
package fichier
import (
diff --git a/backend/fichier/object.go b/backend/fichier/object.go
index 73723e223..b0dc1fde9 100644
--- a/backend/fichier/object.go
+++ b/backend/fichier/object.go
@@ -1,4 +1,4 @@
-package fichier
+package fichier
import (
"context"
diff --git a/backend/fichier/structs.go b/backend/fichier/structs.go
index 289aa64b7..5b46b9c60 100644
--- a/backend/fichier/structs.go
+++ b/backend/fichier/structs.go
@@ -1,4 +1,4 @@
-package fichier
+package fichier
// FileInfoRequest is the request structure of the corresponding request
type FileInfoRequest struct {
diff --git a/backend/filefabric/api/types.go b/backend/filefabric/api/types.go
index 4199bc3fd..b001e22d5 100644
--- a/backend/filefabric/api/types.go
+++ b/backend/filefabric/api/types.go
@@ -1,4 +1,4 @@
-// Package api has type definitions for filefabric
+// Package api has type definitions for filefabric
//
// Converted from the API responses with help from https://mholt.github.io/json-to-go/
package api
diff --git a/backend/filefabric/filefabric.go b/backend/filefabric/filefabric.go
index 9df530f15..3c560b1db 100644
--- a/backend/filefabric/filefabric.go
+++ b/backend/filefabric/filefabric.go
@@ -1,4 +1,4 @@
-// Package filefabric provides an interface to Storage Made Easy's
+// Package filefabric provides an interface to Storage Made Easy's
// Enterprise File Fabric storage system.
package filefabric
diff --git a/backend/filefabric/filefabric_test.go b/backend/filefabric/filefabric_test.go
index 554639212..eed1c6dc6 100644
--- a/backend/filefabric/filefabric_test.go
+++ b/backend/filefabric/filefabric_test.go
@@ -1,4 +1,4 @@
-// Test filefabric filesystem interface
+// Test filefabric filesystem interface
package filefabric_test
import (
diff --git a/backend/filelu/api/types.go b/backend/filelu/api/types.go
index 8c506e810..0916c2421 100644
--- a/backend/filelu/api/types.go
+++ b/backend/filelu/api/types.go
@@ -1,4 +1,4 @@
-// Package api defines types for interacting with the FileLu API.
+// Package api defines types for interacting with the FileLu API.
package api
import "encoding/json"
@@ -8,7 +8,7 @@ type CreateFolderResponse struct {
Status int `json:"status"`
Msg string `json:"msg"`
Result struct {
- FldID interface{} `json:"fld_id"`
+ FldID any `json:"fld_id"`
} `json:"result"`
}
diff --git a/backend/filelu/filelu.go b/backend/filelu/filelu.go
index 306f9f80e..0a22fe381 100644
--- a/backend/filelu/filelu.go
+++ b/backend/filelu/filelu.go
@@ -1,4 +1,4 @@
-// Package filelu provides an interface to the FileLu storage system.
+// Package filelu provides an interface to the FileLu storage system.
package filelu
import (
diff --git a/backend/filelu/filelu_client.go b/backend/filelu/filelu_client.go
index 0beb1085c..bab7d6f33 100644
--- a/backend/filelu/filelu_client.go
+++ b/backend/filelu/filelu_client.go
@@ -1,4 +1,4 @@
-package filelu
+package filelu
import (
"bytes"
diff --git a/backend/filelu/filelu_file_uploader.go b/backend/filelu/filelu_file_uploader.go
index 6da56ebdd..1e7886daa 100644
--- a/backend/filelu/filelu_file_uploader.go
+++ b/backend/filelu/filelu_file_uploader.go
@@ -1,4 +1,4 @@
-package filelu
+package filelu
import (
"context"
diff --git a/backend/filelu/filelu_helper.go b/backend/filelu/filelu_helper.go
index da5d98faf..0a812a2b0 100644
--- a/backend/filelu/filelu_helper.go
+++ b/backend/filelu/filelu_helper.go
@@ -1,4 +1,4 @@
-package filelu
+package filelu
import (
"context"
diff --git a/backend/filelu/filelu_object.go b/backend/filelu/filelu_object.go
index 4c7f3145a..79413647f 100644
--- a/backend/filelu/filelu_object.go
+++ b/backend/filelu/filelu_object.go
@@ -1,4 +1,4 @@
-package filelu
+package filelu
import (
"bytes"
diff --git a/backend/filelu/filelu_test.go b/backend/filelu/filelu_test.go
index 93b364a88..28588bd46 100644
--- a/backend/filelu/filelu_test.go
+++ b/backend/filelu/filelu_test.go
@@ -1,4 +1,4 @@
-package filelu_test
+package filelu_test
import (
"testing"
diff --git a/backend/filelu/utils.go b/backend/filelu/utils.go
index 0525cfd50..abbb5e4b5 100644
--- a/backend/filelu/utils.go
+++ b/backend/filelu/utils.go
@@ -1,4 +1,4 @@
-package filelu
+package filelu
import (
"fmt"
diff --git a/backend/filescom/filescom.go b/backend/filescom/filescom.go
index 38807d03a..871c7553c 100644
--- a/backend/filescom/filescom.go
+++ b/backend/filescom/filescom.go
@@ -1,4 +1,4 @@
-// Package filescom provides an interface to the Files.com
+// Package filescom provides an interface to the Files.com
// object storage system.
package filescom
diff --git a/backend/filescom/filescom_test.go b/backend/filescom/filescom_test.go
index b79121e53..4c42a7999 100644
--- a/backend/filescom/filescom_test.go
+++ b/backend/filescom/filescom_test.go
@@ -1,4 +1,4 @@
-// Test Files filesystem interface
+// Test Files filesystem interface
package filescom_test
import (
diff --git a/backend/ftp/ftp.go b/backend/ftp/ftp.go
index 2c0617911..628e2726c 100644
--- a/backend/ftp/ftp.go
+++ b/backend/ftp/ftp.go
@@ -1,4 +1,4 @@
-// Package ftp interfaces with FTP servers
+// Package ftp interfaces with FTP servers
package ftp
import (
diff --git a/backend/ftp/ftp_internal_test.go b/backend/ftp/ftp_internal_test.go
index d0d5bb1a4..452cec5ec 100644
--- a/backend/ftp/ftp_internal_test.go
+++ b/backend/ftp/ftp_internal_test.go
@@ -1,4 +1,4 @@
-package ftp
+package ftp
import (
"context"
diff --git a/backend/ftp/ftp_test.go b/backend/ftp/ftp_test.go
index 64deb646e..8f17c84a5 100644
--- a/backend/ftp/ftp_test.go
+++ b/backend/ftp/ftp_test.go
@@ -1,4 +1,4 @@
-// Test FTP filesystem interface
+// Test FTP filesystem interface
package ftp_test
import (
diff --git a/backend/gofile/api/types.go b/backend/gofile/api/types.go
index 0a0d63ab9..f5bc1d8ea 100644
--- a/backend/gofile/api/types.go
+++ b/backend/gofile/api/types.go
@@ -1,4 +1,4 @@
-// Package api has type definitions for gofile
+// Package api has type definitions for gofile
//
// Converted from the API docs with help from https://mholt.github.io/json-to-go/
package api
diff --git a/backend/gofile/gofile.go b/backend/gofile/gofile.go
index fc48b7c59..abe1583c8 100644
--- a/backend/gofile/gofile.go
+++ b/backend/gofile/gofile.go
@@ -1,4 +1,4 @@
-// Package gofile provides an interface to the Gofile
+// Package gofile provides an interface to the Gofile
// object storage system.
package gofile
diff --git a/backend/gofile/gofile_test.go b/backend/gofile/gofile_test.go
index 2f8816e61..376493c1f 100644
--- a/backend/gofile/gofile_test.go
+++ b/backend/gofile/gofile_test.go
@@ -1,4 +1,4 @@
-// Test Gofile filesystem interface
+// Test Gofile filesystem interface
package gofile_test
import (
diff --git a/backend/googlecloudstorage/googlecloudstorage.go b/backend/googlecloudstorage/googlecloudstorage.go
index 3ffc59324..522ed1921 100644
--- a/backend/googlecloudstorage/googlecloudstorage.go
+++ b/backend/googlecloudstorage/googlecloudstorage.go
@@ -1,4 +1,4 @@
-// Package googlecloudstorage provides an interface to Google Cloud Storage
+// Package googlecloudstorage provides an interface to Google Cloud Storage
package googlecloudstorage
/*
diff --git a/backend/googlecloudstorage/googlecloudstorage_test.go b/backend/googlecloudstorage/googlecloudstorage_test.go
index f1aa02647..562f9464b 100644
--- a/backend/googlecloudstorage/googlecloudstorage_test.go
+++ b/backend/googlecloudstorage/googlecloudstorage_test.go
@@ -1,4 +1,4 @@
-// Test GoogleCloudStorage filesystem interface
+// Test GoogleCloudStorage filesystem interface
package googlecloudstorage_test
diff --git a/backend/googlephotos/albums.go b/backend/googlephotos/albums.go
index bfb6404db..b6b431209 100644
--- a/backend/googlephotos/albums.go
+++ b/backend/googlephotos/albums.go
@@ -1,4 +1,4 @@
-// This file contains the albums abstraction
+// This file contains the albums abstraction
package googlephotos
diff --git a/backend/googlephotos/albums_test.go b/backend/googlephotos/albums_test.go
index c4c71c80b..fcc50e85c 100644
--- a/backend/googlephotos/albums_test.go
+++ b/backend/googlephotos/albums_test.go
@@ -1,4 +1,4 @@
-package googlephotos
+package googlephotos
import (
"testing"
diff --git a/backend/googlephotos/api/types.go b/backend/googlephotos/api/types.go
index f49195685..c0656189a 100644
--- a/backend/googlephotos/api/types.go
+++ b/backend/googlephotos/api/types.go
@@ -1,4 +1,4 @@
-// Package api provides types used by the Google Photos API.
+// Package api provides types used by the Google Photos API.
package api
import (
diff --git a/backend/googlephotos/googlephotos.go b/backend/googlephotos/googlephotos.go
index bbe7d84b1..9ac8af7b8 100644
--- a/backend/googlephotos/googlephotos.go
+++ b/backend/googlephotos/googlephotos.go
@@ -1,4 +1,4 @@
-// Package googlephotos provides an interface to Google Photos
+// Package googlephotos provides an interface to Google Photos
package googlephotos
// FIXME Resumable uploads not implemented - rclone can't resume uploads in general
diff --git a/backend/googlephotos/googlephotos_test.go b/backend/googlephotos/googlephotos_test.go
index 0eeb99bce..cebfac356 100644
--- a/backend/googlephotos/googlephotos_test.go
+++ b/backend/googlephotos/googlephotos_test.go
@@ -1,4 +1,4 @@
-package googlephotos
+package googlephotos
import (
"context"
diff --git a/backend/googlephotos/pattern.go b/backend/googlephotos/pattern.go
index fbd6d58fb..10f91313c 100644
--- a/backend/googlephotos/pattern.go
+++ b/backend/googlephotos/pattern.go
@@ -1,4 +1,4 @@
-// Store the parsing of file patterns
+// Store the parsing of file patterns
package googlephotos
diff --git a/backend/googlephotos/pattern_test.go b/backend/googlephotos/pattern_test.go
index b446615c2..95dc604c4 100644
--- a/backend/googlephotos/pattern_test.go
+++ b/backend/googlephotos/pattern_test.go
@@ -1,4 +1,4 @@
-package googlephotos
+package googlephotos
import (
"context"
diff --git a/backend/hasher/commands.go b/backend/hasher/commands.go
index 2797e5d4f..48095f42e 100644
--- a/backend/hasher/commands.go
+++ b/backend/hasher/commands.go
@@ -1,4 +1,4 @@
-package hasher
+package hasher
import (
"context"
diff --git a/backend/hasher/hasher.go b/backend/hasher/hasher.go
index 1ee789eaf..de5c870ae 100644
--- a/backend/hasher/hasher.go
+++ b/backend/hasher/hasher.go
@@ -1,4 +1,4 @@
-// Package hasher implements a checksum handling overlay backend
+// Package hasher implements a checksum handling overlay backend
package hasher
import (
diff --git a/backend/hasher/hasher_internal_test.go b/backend/hasher/hasher_internal_test.go
index e289d8f0d..9b34fbc0f 100644
--- a/backend/hasher/hasher_internal_test.go
+++ b/backend/hasher/hasher_internal_test.go
@@ -1,4 +1,4 @@
-package hasher
+package hasher
import (
"context"
diff --git a/backend/hasher/hasher_test.go b/backend/hasher/hasher_test.go
index f5119c724..a66dc6833 100644
--- a/backend/hasher/hasher_test.go
+++ b/backend/hasher/hasher_test.go
@@ -1,4 +1,4 @@
-package hasher_test
+package hasher_test
import (
"os"
diff --git a/backend/hasher/kv.go b/backend/hasher/kv.go
index c51c13b0e..0e88b1295 100644
--- a/backend/hasher/kv.go
+++ b/backend/hasher/kv.go
@@ -1,4 +1,4 @@
-package hasher
+package hasher
import (
"bytes"
diff --git a/backend/hasher/object.go b/backend/hasher/object.go
index 1003807c8..2581db3eb 100644
--- a/backend/hasher/object.go
+++ b/backend/hasher/object.go
@@ -1,4 +1,4 @@
-package hasher
+package hasher
import (
"context"
diff --git a/backend/hdfs/fs.go b/backend/hdfs/fs.go
index f6e306053..1df817cbe 100644
--- a/backend/hdfs/fs.go
+++ b/backend/hdfs/fs.go
@@ -1,4 +1,4 @@
-//go:build !plan9
+//go:build !plan9
package hdfs
diff --git a/backend/hdfs/hdfs.go b/backend/hdfs/hdfs.go
index 87d85e633..753d698c2 100644
--- a/backend/hdfs/hdfs.go
+++ b/backend/hdfs/hdfs.go
@@ -1,4 +1,4 @@
-//go:build !plan9
+//go:build !plan9
// Package hdfs provides an interface to the HDFS storage system.
package hdfs
diff --git a/backend/hdfs/hdfs_test.go b/backend/hdfs/hdfs_test.go
index edbb62330..17be4f63e 100644
--- a/backend/hdfs/hdfs_test.go
+++ b/backend/hdfs/hdfs_test.go
@@ -1,4 +1,4 @@
-// Test HDFS filesystem interface
+// Test HDFS filesystem interface
//go:build !plan9
diff --git a/backend/hdfs/hdfs_unsupported.go b/backend/hdfs/hdfs_unsupported.go
index a14c7725c..514b9cccc 100644
--- a/backend/hdfs/hdfs_unsupported.go
+++ b/backend/hdfs/hdfs_unsupported.go
@@ -1,4 +1,4 @@
-// Build for hdfs for unsupported platforms to stop go complaining
+// Build for hdfs for unsupported platforms to stop go complaining
// about "no buildable Go source files "
//go:build plan9
diff --git a/backend/hdfs/object.go b/backend/hdfs/object.go
index d6d25297f..f3b2ec162 100644
--- a/backend/hdfs/object.go
+++ b/backend/hdfs/object.go
@@ -1,4 +1,4 @@
-//go:build !plan9
+//go:build !plan9
package hdfs
diff --git a/backend/hidrive/api/queries.go b/backend/hidrive/api/queries.go
index 57a1477c1..bbc4464dd 100644
--- a/backend/hidrive/api/queries.go
+++ b/backend/hidrive/api/queries.go
@@ -1,4 +1,4 @@
-package api
+package api
import (
"encoding/json"
diff --git a/backend/hidrive/api/types.go b/backend/hidrive/api/types.go
index 4cc912a72..6fc4bdeec 100644
--- a/backend/hidrive/api/types.go
+++ b/backend/hidrive/api/types.go
@@ -1,4 +1,4 @@
-// Package api has type definitions and code related to API-calls for the HiDrive-API.
+// Package api has type definitions and code related to API-calls for the HiDrive-API.
package api
import (
diff --git a/backend/hidrive/helpers.go b/backend/hidrive/helpers.go
index b7a522257..40ccabd50 100644
--- a/backend/hidrive/helpers.go
+++ b/backend/hidrive/helpers.go
@@ -1,4 +1,4 @@
-package hidrive
+package hidrive
// This file is for helper-functions which may provide more general and
// specialized functionality than the generic interfaces.
diff --git a/backend/hidrive/hidrive.go b/backend/hidrive/hidrive.go
index 33b2fb09c..4e20ba09b 100644
--- a/backend/hidrive/hidrive.go
+++ b/backend/hidrive/hidrive.go
@@ -1,4 +1,4 @@
-// Package hidrive provides an interface to the HiDrive object storage system.
+// Package hidrive provides an interface to the HiDrive object storage system.
package hidrive
// FIXME HiDrive only supports file or folder names of 255 characters or less.
diff --git a/backend/hidrive/hidrive_test.go b/backend/hidrive/hidrive_test.go
index 30e1ef2f1..cf274b43e 100644
--- a/backend/hidrive/hidrive_test.go
+++ b/backend/hidrive/hidrive_test.go
@@ -1,4 +1,4 @@
-// Test HiDrive filesystem interface
+// Test HiDrive filesystem interface
package hidrive
import (
diff --git a/backend/hidrive/hidrivehash/hidrivehash.go b/backend/hidrive/hidrivehash/hidrivehash.go
index 887cda5bf..a377b3ecd 100644
--- a/backend/hidrive/hidrivehash/hidrivehash.go
+++ b/backend/hidrive/hidrivehash/hidrivehash.go
@@ -1,4 +1,4 @@
-// Package hidrivehash implements the HiDrive hashing algorithm which combines SHA-1 hashes hierarchically to a single top-level hash.
+// Package hidrivehash implements the HiDrive hashing algorithm which combines SHA-1 hashes hierarchically to a single top-level hash.
//
// Note: This implementation does not grant access to any partial hashes generated.
//
diff --git a/backend/hidrive/hidrivehash/hidrivehash_test.go b/backend/hidrive/hidrivehash/hidrivehash_test.go
index 817f0a8dd..437c3668e 100644
--- a/backend/hidrive/hidrivehash/hidrivehash_test.go
+++ b/backend/hidrive/hidrivehash/hidrivehash_test.go
@@ -1,4 +1,4 @@
-package hidrivehash_test
+package hidrivehash_test
import (
"crypto/sha1"
diff --git a/backend/hidrive/hidrivehash/internal/internal.go b/backend/hidrive/hidrivehash/internal/internal.go
index 261233c0d..7286c36d7 100644
--- a/backend/hidrive/hidrivehash/internal/internal.go
+++ b/backend/hidrive/hidrivehash/internal/internal.go
@@ -1,4 +1,4 @@
-// Package internal provides utilities for HiDrive.
+// Package internal provides utilities for HiDrive.
package internal
import (
diff --git a/backend/http/http.go b/backend/http/http.go
index d4b426a7f..3d737c4d9 100644
--- a/backend/http/http.go
+++ b/backend/http/http.go
@@ -1,4 +1,4 @@
-// Package http provides a filesystem interface using golang.org/net/http
+// Package http provides a filesystem interface using golang.org/net/http
//
// It treats HTML pages served from the endpoint as directory
// listings, and includes any links found as files.
diff --git a/backend/http/http_internal_test.go b/backend/http/http_internal_test.go
index 90a2bae78..5067a8c4e 100644
--- a/backend/http/http_internal_test.go
+++ b/backend/http/http_internal_test.go
@@ -1,4 +1,4 @@
-package http
+package http
import (
"context"
diff --git a/backend/iclouddrive/api/client.go b/backend/iclouddrive/api/client.go
index 25587d5f1..7ed2a72b3 100644
--- a/backend/iclouddrive/api/client.go
+++ b/backend/iclouddrive/api/client.go
@@ -1,4 +1,4 @@
-// Package api provides functionality for interacting with the iCloud API.
+// Package api provides functionality for interacting with the iCloud API.
package api
import (
diff --git a/backend/iclouddrive/api/drive.go b/backend/iclouddrive/api/drive.go
index a0856b262..acae8d8f2 100644
--- a/backend/iclouddrive/api/drive.go
+++ b/backend/iclouddrive/api/drive.go
@@ -1,4 +1,4 @@
-package api
+package api
import (
"bytes"
diff --git a/backend/iclouddrive/api/photos.go b/backend/iclouddrive/api/photos.go
index c9590f36f..90ca37927 100644
--- a/backend/iclouddrive/api/photos.go
+++ b/backend/iclouddrive/api/photos.go
@@ -1,4 +1,4 @@
-package api
+package api
import (
"context"
diff --git a/backend/iclouddrive/api/session.go b/backend/iclouddrive/api/session.go
index 0fd9072de..18da321d5 100644
--- a/backend/iclouddrive/api/session.go
+++ b/backend/iclouddrive/api/session.go
@@ -1,4 +1,4 @@
-package api
+package api
import (
"context"
diff --git a/backend/iclouddrive/icloud.go b/backend/iclouddrive/icloud.go
index ecad0f977..56266f373 100644
--- a/backend/iclouddrive/icloud.go
+++ b/backend/iclouddrive/icloud.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !solaris
+//go:build !plan9 && !solaris
// Package iclouddrive provides access to iCloud Drive and Photos
package iclouddrive
diff --git a/backend/iclouddrive/iclouddrive.go b/backend/iclouddrive/iclouddrive.go
index 7944e76c8..10ff01e35 100644
--- a/backend/iclouddrive/iclouddrive.go
+++ b/backend/iclouddrive/iclouddrive.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !solaris
+//go:build !plan9 && !solaris
// Package iclouddrive implements the iCloud Drive backend
package iclouddrive
diff --git a/backend/iclouddrive/iclouddrive_test.go b/backend/iclouddrive/iclouddrive_test.go
index 13db708ea..fc49bc054 100644
--- a/backend/iclouddrive/iclouddrive_test.go
+++ b/backend/iclouddrive/iclouddrive_test.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !solaris
+//go:build !plan9 && !solaris
package iclouddrive_test
diff --git a/backend/iclouddrive/iclouddrive_unsupported.go b/backend/iclouddrive/iclouddrive_unsupported.go
index 2eeb6b639..c609f19f8 100644
--- a/backend/iclouddrive/iclouddrive_unsupported.go
+++ b/backend/iclouddrive/iclouddrive_unsupported.go
@@ -1,4 +1,4 @@
-// Build for iclouddrive for unsupported platforms to stop go complaining
+// Build for iclouddrive for unsupported platforms to stop go complaining
// about "no buildable Go source files "
//go:build plan9 || solaris
diff --git a/backend/iclouddrive/icloudphotos.go b/backend/iclouddrive/icloudphotos.go
index ab236a68a..1eb7b5a03 100644
--- a/backend/iclouddrive/icloudphotos.go
+++ b/backend/iclouddrive/icloudphotos.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !solaris
+//go:build !plan9 && !solaris
// iCloud Photos backend implementation
package iclouddrive
diff --git a/backend/imagekit/client/client.go b/backend/imagekit/client/client.go
index 2b10c669d..4110188e1 100644
--- a/backend/imagekit/client/client.go
+++ b/backend/imagekit/client/client.go
@@ -1,4 +1,4 @@
-// Package client provides a client for interacting with the ImageKit API.
+// Package client provides a client for interacting with the ImageKit API.
package client
import (
diff --git a/backend/imagekit/client/media.go b/backend/imagekit/client/media.go
index 1d4ee030b..f4cc93a09 100644
--- a/backend/imagekit/client/media.go
+++ b/backend/imagekit/client/media.go
@@ -1,4 +1,4 @@
-package client
+package client
import (
"context"
diff --git a/backend/imagekit/client/upload.go b/backend/imagekit/client/upload.go
index 7964f8c46..019a79326 100644
--- a/backend/imagekit/client/upload.go
+++ b/backend/imagekit/client/upload.go
@@ -1,4 +1,4 @@
-package client
+package client
import (
"context"
diff --git a/backend/imagekit/client/url.go b/backend/imagekit/client/url.go
index 18738d1e9..75dacc096 100644
--- a/backend/imagekit/client/url.go
+++ b/backend/imagekit/client/url.go
@@ -1,4 +1,4 @@
-package client
+package client
import (
"crypto/hmac"
diff --git a/backend/imagekit/imagekit.go b/backend/imagekit/imagekit.go
index 6a72422c8..c72779890 100644
--- a/backend/imagekit/imagekit.go
+++ b/backend/imagekit/imagekit.go
@@ -1,4 +1,4 @@
-// Package imagekit provides an interface to the ImageKit.io media library.
+// Package imagekit provides an interface to the ImageKit.io media library.
package imagekit
import (
diff --git a/backend/imagekit/imagekit_test.go b/backend/imagekit/imagekit_test.go
index 686976322..332059310 100644
--- a/backend/imagekit/imagekit_test.go
+++ b/backend/imagekit/imagekit_test.go
@@ -1,4 +1,4 @@
-package imagekit
+package imagekit
import (
"testing"
diff --git a/backend/imagekit/util.go b/backend/imagekit/util.go
index d16eee9a5..603e65ffa 100644
--- a/backend/imagekit/util.go
+++ b/backend/imagekit/util.go
@@ -1,4 +1,4 @@
-package imagekit
+package imagekit
import (
"context"
diff --git a/backend/internetarchive/internetarchive.go b/backend/internetarchive/internetarchive.go
index 65f045d22..9f7002384 100644
--- a/backend/internetarchive/internetarchive.go
+++ b/backend/internetarchive/internetarchive.go
@@ -1,4 +1,4 @@
-// Package internetarchive provides an interface to Internet Archive's Item
+// Package internetarchive provides an interface to Internet Archive's Item
// via their native API than using S3-compatible endpoints.
package internetarchive
diff --git a/backend/internetarchive/internetarchive_test.go b/backend/internetarchive/internetarchive_test.go
index 2faa6f484..3d3286007 100644
--- a/backend/internetarchive/internetarchive_test.go
+++ b/backend/internetarchive/internetarchive_test.go
@@ -1,4 +1,4 @@
-// Test internetarchive filesystem interface
+// Test internetarchive filesystem interface
package internetarchive_test
import (
diff --git a/backend/jottacloud/api/types.go b/backend/jottacloud/api/types.go
index dea7ec8ba..8705dd07d 100644
--- a/backend/jottacloud/api/types.go
+++ b/backend/jottacloud/api/types.go
@@ -1,4 +1,4 @@
-// Package api provides types used by the Jottacloud API.
+// Package api provides types used by the Jottacloud API.
package api
import (
diff --git a/backend/jottacloud/api/types_test.go b/backend/jottacloud/api/types_test.go
index 84515048e..5ce2ee5eb 100644
--- a/backend/jottacloud/api/types_test.go
+++ b/backend/jottacloud/api/types_test.go
@@ -1,4 +1,4 @@
-package api
+package api
import (
"encoding/xml"
diff --git a/backend/jottacloud/jottacloud.go b/backend/jottacloud/jottacloud.go
index f074ceea2..df7f4ccaa 100644
--- a/backend/jottacloud/jottacloud.go
+++ b/backend/jottacloud/jottacloud.go
@@ -1,4 +1,4 @@
-// Package jottacloud provides an interface to the Jottacloud storage system.
+// Package jottacloud provides an interface to the Jottacloud storage system.
package jottacloud
import (
@@ -17,6 +17,7 @@ import (
"net/url"
"os"
"path"
+ "slices"
"strconv"
"strings"
"time"
@@ -59,31 +60,43 @@ const (
configVersion = 1
defaultTokenURL = "https://id.jottacloud.com/auth/realms/jottacloud/protocol/openid-connect/token"
- defaultClientID = "jottacli"
+ defaultClientID = "jottacli" // Identified as "Jottacloud CLI" in "My logged in devices"
legacyTokenURL = "https://api.jottacloud.com/auth/v1/token"
legacyRegisterURL = "https://api.jottacloud.com/auth/v1/register"
legacyClientID = "nibfk8biu12ju7hpqomr8b1e40"
legacyEncryptedClientSecret = "Vp8eAv7eVElMnQwN-kgU9cbhgApNDaMqWdlDi5qFydlQoji4JBxrGMF2"
legacyConfigVersion = 0
-
- teliaseCloudTokenURL = "https://cloud-auth.telia.se/auth/realms/telia_se/protocol/openid-connect/token"
- teliaseCloudAuthURL = "https://cloud-auth.telia.se/auth/realms/telia_se/protocol/openid-connect/auth"
- teliaseCloudClientID = "desktop"
-
- telianoCloudTokenURL = "https://sky-auth.telia.no/auth/realms/get/protocol/openid-connect/token"
- telianoCloudAuthURL = "https://sky-auth.telia.no/auth/realms/get/protocol/openid-connect/auth"
- telianoCloudClientID = "desktop"
-
- tele2CloudTokenURL = "https://mittcloud-auth.tele2.se/auth/realms/comhem/protocol/openid-connect/token"
- tele2CloudAuthURL = "https://mittcloud-auth.tele2.se/auth/realms/comhem/protocol/openid-connect/auth"
- tele2CloudClientID = "desktop"
-
- onlimeCloudTokenURL = "https://cloud-auth.onlime.dk/auth/realms/onlime_wl/protocol/openid-connect/token"
- onlimeCloudAuthURL = "https://cloud-auth.onlime.dk/auth/realms/onlime_wl/protocol/openid-connect/auth"
- onlimeCloudClientID = "desktop"
)
+type service struct {
+ key string
+ name string
+ domain string
+ realm string
+ clientID string
+ scopes []string
+}
+
+// The list of services and their settings for supporting traditional OAuth.
+// Please keep these in alphabetical order, but with jottacloud first.
+func getServices() []service {
+ return []service{
+ {"jottacloud", "Jottacloud", "id.jottacloud.com", "jottacloud", "desktop", []string{"openid", "jotta-default", "offline_access"}}, // Chose client id "desktop" here, will be identified as "Jottacloud for Desktop" in "My logged in devices", but could have used "jottacli" here as well.
+ {"elgiganten_dk", "Elgiganten Cloud (Denmark)", "cloud.elgiganten.dk", "elgiganten", "desktop", []string{"openid", "jotta-default", "offline_access"}},
+ {"elgiganten_se", "Elgiganten Cloud (Sweden)", "cloud.elgiganten.se", "elgiganten", "desktop", []string{"openid", "jotta-default", "offline_access"}},
+ {"elkjop", "Elkjøp Cloud (Norway)", "cloud.elkjop.no", "elkjop", "desktop", []string{"openid", "jotta-default", "offline_access"}},
+ {"elko", "ELKO Cloud (Iceland)", "cloud.elko.is", "elko", "desktop", []string{"openid", "jotta-default", "offline_access"}},
+ {"gigantti", "Gigantti Cloud (Finland)", "cloud.gigantti.fi", "gigantti", "desktop", []string{"openid", "jotta-default", "offline_access"}},
+ {"letsgo", "Let's Go Cloud (Germany)", "letsgo.jotta.cloud", "letsgo", "desktop-win", []string{"openid", "offline_access"}},
+ {"mediamarkt", "MediaMarkt Cloud (Multiregional)", "mediamarkt.jottacloud.com", "mediamarkt", "desktop", []string{"openid", "jotta-default", "offline_access"}},
+ {"onlime", "Onlime (Denmark)", "cloud-auth.onlime.dk", "onlime_wl", "desktop", []string{"openid", "jotta-default", "offline_access"}},
+ {"tele2", "Tele2 Cloud (Sweden)", "mittcloud-auth.tele2.se", "comhem", "desktop", []string{"openid", "jotta-default", "offline_access"}},
+ {"telia_no", "Telia Sky (Norway)", "sky-auth.telia.no", "get", "desktop", []string{"openid", "jotta-default", "offline_access"}},
+ {"telia_se", "Telia Cloud (Sweden)", "cloud-auth.telia.se", "telia_se", "desktop", []string{"openid", "jotta-default", "offline_access"}},
+ }
+}
+
// Register with Fs
func init() {
// needs to be done early so we can use oauth during config
@@ -159,36 +172,44 @@ func init() {
}
// Config runs the backend configuration protocol
-func Config(ctx context.Context, name string, m configmap.Mapper, config fs.ConfigIn) (*fs.ConfigOut, error) {
- switch config.State {
+func Config(ctx context.Context, name string, m configmap.Mapper, conf fs.ConfigIn) (*fs.ConfigOut, error) {
+ switch conf.State {
case "":
- return fs.ConfigChooseExclusiveFixed("auth_type_done", "config_type", `Select authentication type.`, []fs.OptionExample{{
+ if isAuthorize, _ := m.Get(config.ConfigAuthorize); isAuthorize == "true" {
+ return nil, errors.New("not supported by this backend")
+ }
+ return fs.ConfigChooseExclusiveFixed("auth_type_done", "config_type", `Type of authentication.`, []fs.OptionExample{{
Value: "standard",
- Help: "Standard authentication.\nUse this if you're a normal Jottacloud user.",
+ Help: `Standard authentication.
+This is primarily supported by the official service, but may also be
+supported by some white-label services. It is designed for command-line
+applications, and you will be asked to enter a single-use personal login
+token which you must manually generate from the account security settings
+in the web interface of your service.`,
}, {
- Value: "legacy",
- Help: "Legacy authentication.\nThis is only required for certain whitelabel versions of Jottacloud and not recommended for normal users.",
- }, {
- Value: "telia_se",
- Help: "Telia Cloud authentication.\nUse this if you are using Telia Cloud (Sweden).",
- }, {
- Value: "telia_no",
- Help: "Telia Sky authentication.\nUse this if you are using Telia Sky (Norway).",
- }, {
- Value: "tele2",
- Help: "Tele2 Cloud authentication.\nUse this if you are using Tele2 Cloud.",
+ Value: "traditional",
+ Help: `Traditional authentication.
+This is supported by the official service and all white-label services
+that rclone knows about. You will be asked which service to connect to.
+It has a limitation of only a single active authentication at a time. You
+need to be on, or have access to, a machine with an internet-connected
+web browser.`,
}, {
- Value: "onlime",
- Help: "Onlime Cloud authentication.\nUse this if you are using Onlime Cloud.",
+ Value: "legacy",
+ Help: `Legacy authentication.
+This is no longer supported by any known services and not recommended
+used. You will be asked for your account's username and password.`,
}})
case "auth_type_done":
// Jump to next state according to config chosen
- return fs.ConfigGoto(config.Result)
+ return fs.ConfigGoto(conf.Result)
case "standard": // configure a jottacloud backend using the modern JottaCli token based authentication
m.Set("configVersion", fmt.Sprint(configVersion))
- return fs.ConfigInput("standard_token", "config_login_token", "Personal login token.\nGenerate here: https://www.jottacloud.com/web/secure")
+ return fs.ConfigInput("standard_token", "config_login_token", `Personal login token.
+Generate it from the account security settings in the web interface of your
+service, for the official service on https://www.jottacloud.com/web/secure.`)
case "standard_token":
- loginToken := config.Result
+ loginToken := conf.Result
m.Set(configClientID, defaultClientID)
m.Set(configClientSecret, "")
@@ -203,10 +224,50 @@ func Config(ctx context.Context, name string, m configmap.Mapper, config fs.Conf
return nil, fmt.Errorf("error while saving token: %w", err)
}
return fs.ConfigGoto("choose_device")
+ case "traditional":
+ services := getServices()
+ options := make([]fs.OptionExample, 0, len(services))
+ for _, service := range services {
+ options = append(options, fs.OptionExample{
+ Value: service.key,
+ Help: service.name,
+ })
+ }
+ return fs.ConfigChooseExclusiveFixed("traditional_type", "config_traditional",
+ "White-label service. This decides the domain name to connect to and\nthe authentication configuration to use.",
+ options)
+ case "traditional_type":
+ services := getServices()
+ i := slices.IndexFunc(services, func(s service) bool { return s.key == conf.Result })
+ if i == -1 {
+ return nil, fmt.Errorf("unexpected service %q", conf.Result)
+ }
+ service := services[i]
+ opts := rest.Opts{
+ Method: "GET",
+ RootURL: "https://" + service.domain + "/auth/realms/" + service.realm + "/.well-known/openid-configuration",
+ }
+ var wellKnown api.WellKnown
+ srv := rest.NewClient(fshttp.NewClient(ctx))
+ _, err := srv.CallJSON(ctx, &opts, nil, &wellKnown)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get authentication provider configuration: %w", err)
+ }
+ m.Set("configVersion", fmt.Sprint(configVersion))
+ m.Set(configClientID, service.clientID)
+ m.Set(configTokenURL, wellKnown.TokenEndpoint)
+ return oauthutil.ConfigOut("choose_device", &oauthutil.Options{
+ OAuth2Config: &oauthutil.Config{
+ AuthURL: wellKnown.AuthorizationEndpoint,
+ TokenURL: wellKnown.TokenEndpoint,
+ ClientID: service.clientID,
+ Scopes: service.scopes,
+ RedirectURL: oauthutil.RedirectLocalhostURL,
+ },
+ })
case "legacy": // configure a jottacloud backend using legacy authentication
m.Set("configVersion", fmt.Sprint(legacyConfigVersion))
return fs.ConfigConfirm("legacy_api", false, "config_machine_specific", `Do you want to create a machine specific API key?
-
Rclone has it's own Jottacloud API KEY which works fine as long as one
only uses rclone on a single machine. When you want to use rclone with
this account on more than one machine it's recommended to create a
@@ -214,7 +275,7 @@ machine specific API key. These keys can NOT be shared between
machines.`)
case "legacy_api":
srv := rest.NewClient(fshttp.NewClient(ctx))
- if config.Result == "true" {
+ if conf.Result == "true" {
deviceRegistration, err := registerDevice(ctx, srv)
if err != nil {
return nil, fmt.Errorf("failed to register device: %w", err)
@@ -223,16 +284,16 @@ machines.`)
m.Set(configClientSecret, obscure.MustObscure(deviceRegistration.ClientSecret))
fs.Debugf(nil, "Got clientID %q and clientSecret %q", deviceRegistration.ClientID, deviceRegistration.ClientSecret)
}
- return fs.ConfigInput("legacy_username", "config_username", "Username (e-mail address)")
+ return fs.ConfigInput("legacy_username", "config_username", "Username (e-mail address) of your account.")
case "legacy_username":
- m.Set(configUsername, config.Result)
- return fs.ConfigPassword("legacy_password", "config_password", "Password (only used in setup, will not be stored)")
+ m.Set(configUsername, conf.Result)
+ return fs.ConfigPassword("legacy_password", "config_password", "Password of your account. This is only used in setup, it will not be stored.")
case "legacy_password":
- m.Set("password", config.Result)
+ m.Set("password", conf.Result)
m.Set("auth_code", "")
return fs.ConfigGoto("legacy_do_auth")
case "legacy_auth_code":
- authCode := strings.ReplaceAll(config.Result, "-", "") // remove any "-" contained in the code so we have a 6 digit number
+ authCode := strings.ReplaceAll(conf.Result, "-", "") // remove any "-" contained in the code so we have a 6 digit number
m.Set("auth_code", authCode)
return fs.ConfigGoto("legacy_do_auth")
case "legacy_do_auth":
@@ -242,12 +303,12 @@ machines.`)
authCode, _ := m.Get("auth_code")
srv := rest.NewClient(fshttp.NewClient(ctx))
- clientID, ok := m.Get(configClientID)
- if !ok {
+ clientID, _ := m.Get(configClientID)
+ if clientID == "" {
clientID = legacyClientID
}
- clientSecret, ok := m.Get(configClientSecret)
- if !ok {
+ clientSecret, _ := m.Get(configClientSecret)
+ if clientSecret == "" {
clientSecret = legacyEncryptedClientSecret
}
@@ -260,7 +321,7 @@ machines.`)
}
token, err := doLegacyAuth(ctx, srv, oauthConfig, username, password, authCode)
if err == errAuthCodeRequired {
- return fs.ConfigInput("legacy_auth_code", "config_auth_code", "Verification Code\nThis account uses 2 factor authentication you will receive a verification code via SMS.")
+ return fs.ConfigInput("legacy_auth_code", "config_auth_code", "Verification code.\nThis account uses 2 factor authentication you will receive a verification code via SMS.")
}
m.Set("password", "")
m.Set("auth_code", "")
@@ -272,58 +333,6 @@ machines.`)
return nil, fmt.Errorf("error while saving token: %w", err)
}
return fs.ConfigGoto("choose_device")
- case "telia_se": // telia_se cloud config
- m.Set("configVersion", fmt.Sprint(configVersion))
- m.Set(configClientID, teliaseCloudClientID)
- m.Set(configTokenURL, teliaseCloudTokenURL)
- return oauthutil.ConfigOut("choose_device", &oauthutil.Options{
- OAuth2Config: &oauthutil.Config{
- AuthURL: teliaseCloudAuthURL,
- TokenURL: teliaseCloudTokenURL,
- ClientID: teliaseCloudClientID,
- Scopes: []string{"openid", "jotta-default", "offline_access"},
- RedirectURL: oauthutil.RedirectLocalhostURL,
- },
- })
- case "telia_no": // telia_no cloud config
- m.Set("configVersion", fmt.Sprint(configVersion))
- m.Set(configClientID, telianoCloudClientID)
- m.Set(configTokenURL, telianoCloudTokenURL)
- return oauthutil.ConfigOut("choose_device", &oauthutil.Options{
- OAuth2Config: &oauthutil.Config{
- AuthURL: telianoCloudAuthURL,
- TokenURL: telianoCloudTokenURL,
- ClientID: telianoCloudClientID,
- Scopes: []string{"openid", "jotta-default", "offline_access"},
- RedirectURL: oauthutil.RedirectLocalhostURL,
- },
- })
- case "tele2": // tele2 cloud config
- m.Set("configVersion", fmt.Sprint(configVersion))
- m.Set(configClientID, tele2CloudClientID)
- m.Set(configTokenURL, tele2CloudTokenURL)
- return oauthutil.ConfigOut("choose_device", &oauthutil.Options{
- OAuth2Config: &oauthutil.Config{
- AuthURL: tele2CloudAuthURL,
- TokenURL: tele2CloudTokenURL,
- ClientID: tele2CloudClientID,
- Scopes: []string{"openid", "jotta-default", "offline_access"},
- RedirectURL: oauthutil.RedirectLocalhostURL,
- },
- })
- case "onlime": // onlime cloud config
- m.Set("configVersion", fmt.Sprint(configVersion))
- m.Set(configClientID, onlimeCloudClientID)
- m.Set(configTokenURL, onlimeCloudTokenURL)
- return oauthutil.ConfigOut("choose_device", &oauthutil.Options{
- OAuth2Config: &oauthutil.Config{
- AuthURL: onlimeCloudAuthURL,
- TokenURL: onlimeCloudTokenURL,
- ClientID: onlimeCloudClientID,
- Scopes: []string{"openid", "jotta-default", "offline_access"},
- RedirectURL: oauthutil.RedirectLocalhostURL,
- },
- })
case "choose_device":
return fs.ConfigConfirm("choose_device_query", false, "config_non_standard", `Use a non-standard device/mountpoint?
Choosing no, the default, will let you access the storage used for the archive
@@ -331,7 +340,7 @@ section of the official Jottacloud client. If you instead want to access the
sync or the backup section, for example, you must choose yes.`)
case "choose_device_query":
- if config.Result != "true" {
+ if conf.Result != "true" {
m.Set(configDevice, "")
m.Set(configMountpoint, "")
return fs.ConfigGoto("end")
@@ -372,7 +381,7 @@ a new by entering a unique name.`, defaultDevice)
return deviceNames[i], ""
})
case "choose_device_result":
- device := config.Result
+ device := conf.Result
oAuthClient, _, err := getOAuthClient(ctx, name, m)
if err != nil {
@@ -432,7 +441,7 @@ You may create a new by entering a unique name.`, device)
return dev.MountPoints[i].Name, ""
})
case "choose_device_mountpoint":
- mountpoint := config.Result
+ mountpoint := conf.Result
oAuthClient, _, err := getOAuthClient(ctx, name, m)
if err != nil {
@@ -463,7 +472,7 @@ You may create a new by entering a unique name.`, device)
if isNew {
if device == defaultDevice {
- return nil, fmt.Errorf("custom mountpoints not supported on built-in %s device: %w", defaultDevice, err)
+ return nil, fmt.Errorf("custom mountpoints not supported on built-in %s device", defaultDevice)
}
fs.Debugf(nil, "Creating new mountpoint: %s", mountpoint)
_, err := createMountPoint(ctx, jfsSrv, path.Join(cust.Username, device, mountpoint))
@@ -478,7 +487,7 @@ You may create a new by entering a unique name.`, device)
// All the config flows end up here in case we need to carry on with something
return nil, nil
}
- return nil, fmt.Errorf("unknown state %q", config.State)
+ return nil, fmt.Errorf("unknown state %q", conf.State)
}
// Options defines the configuration for this backend
@@ -929,12 +938,12 @@ func getOAuthClient(ctx context.Context, name string, m configmap.Mapper) (oAuth
oauthConfig.AuthURL = tokenURL
}
} else if ver == legacyConfigVersion {
- clientID, ok := m.Get(configClientID)
- if !ok {
+ clientID, _ := m.Get(configClientID)
+ if clientID == "" {
clientID = legacyClientID
}
- clientSecret, ok := m.Get(configClientSecret)
- if !ok {
+ clientSecret, _ := m.Get(configClientSecret)
+ if clientSecret == "" {
clientSecret = legacyEncryptedClientSecret
}
oauthConfig.ClientID = clientID
@@ -1000,6 +1009,13 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e
f.features.ListR = nil
}
+ cust, err := getCustomerInfo(ctx, f.apiSrv)
+ if err != nil {
+ return nil, err
+ }
+ f.user = cust.Username
+ f.setEndpoints()
+
// Renew the token in the background
f.tokenRenewer = oauthutil.NewRenew(f.String(), ts, func() error {
_, err := f.readMetaDataForPath(ctx, "")
@@ -1009,13 +1025,6 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e
return err
})
- cust, err := getCustomerInfo(ctx, f.apiSrv)
- if err != nil {
- return nil, err
- }
- f.user = cust.Username
- f.setEndpoints()
-
if root != "" && !rootIsDir {
// Check to see if the root actually an existing file
remote := path.Base(root)
diff --git a/backend/jottacloud/jottacloud_internal_test.go b/backend/jottacloud/jottacloud_internal_test.go
index db9d6a157..b8cc4a375 100644
--- a/backend/jottacloud/jottacloud_internal_test.go
+++ b/backend/jottacloud/jottacloud_internal_test.go
@@ -1,4 +1,4 @@
-package jottacloud
+package jottacloud
import (
"context"
diff --git a/backend/jottacloud/jottacloud_test.go b/backend/jottacloud/jottacloud_test.go
index c509e8431..754027799 100644
--- a/backend/jottacloud/jottacloud_test.go
+++ b/backend/jottacloud/jottacloud_test.go
@@ -1,4 +1,4 @@
-// Test Box filesystem interface
+// Test Box filesystem interface
package jottacloud_test
import (
diff --git a/backend/koofr/koofr.go b/backend/koofr/koofr.go
index 8c718e41d..021fe9a53 100644
--- a/backend/koofr/koofr.go
+++ b/backend/koofr/koofr.go
@@ -1,4 +1,4 @@
-// Package koofr provides an interface to the Koofr storage system.
+// Package koofr provides an interface to the Koofr storage system.
package koofr
import (
diff --git a/backend/koofr/koofr_test.go b/backend/koofr/koofr_test.go
index 3c0dcca75..913076c77 100644
--- a/backend/koofr/koofr_test.go
+++ b/backend/koofr/koofr_test.go
@@ -1,4 +1,4 @@
-package koofr_test
+package koofr_test
import (
"testing"
diff --git a/backend/linkbox/linkbox.go b/backend/linkbox/linkbox.go
index 4537d657e..3a5cde5d9 100644
--- a/backend/linkbox/linkbox.go
+++ b/backend/linkbox/linkbox.go
@@ -1,4 +1,4 @@
-// Package linkbox provides an interface to the linkbox.to Cloud storage system.
+// Package linkbox provides an interface to the linkbox.to Cloud storage system.
//
// API docs: https://www.linkbox.to/api-docs
package linkbox
diff --git a/backend/linkbox/linkbox_test.go b/backend/linkbox/linkbox_test.go
index d9b448c0c..88ff64d4e 100644
--- a/backend/linkbox/linkbox_test.go
+++ b/backend/linkbox/linkbox_test.go
@@ -1,4 +1,4 @@
-// Test Linkbox filesystem interface
+// Test Linkbox filesystem interface
package linkbox_test
import (
diff --git a/backend/local/about_unix.go b/backend/local/about_unix.go
index f8dce54e4..de7cace44 100644
--- a/backend/local/about_unix.go
+++ b/backend/local/about_unix.go
@@ -1,4 +1,4 @@
-//go:build darwin || dragonfly || freebsd || linux
+//go:build darwin || dragonfly || freebsd || linux
package local
diff --git a/backend/local/about_windows.go b/backend/local/about_windows.go
index bda2a5504..99af3750f 100644
--- a/backend/local/about_windows.go
+++ b/backend/local/about_windows.go
@@ -1,4 +1,4 @@
-//go:build windows
+//go:build windows
package local
diff --git a/backend/local/clone_darwin.go b/backend/local/clone_darwin.go
index bf6e5445f..538f23a8a 100644
--- a/backend/local/clone_darwin.go
+++ b/backend/local/clone_darwin.go
@@ -1,4 +1,4 @@
-//go:build darwin && cgo
+//go:build darwin && cgo
// Package local provides a filesystem interface
package local
diff --git a/backend/local/fadvise_other.go b/backend/local/fadvise_other.go
index c07a7d01c..9e3374f4c 100644
--- a/backend/local/fadvise_other.go
+++ b/backend/local/fadvise_other.go
@@ -1,4 +1,4 @@
-//go:build !linux
+//go:build !linux
package local
diff --git a/backend/local/fadvise_unix.go b/backend/local/fadvise_unix.go
index f463e017c..04aadd4fa 100644
--- a/backend/local/fadvise_unix.go
+++ b/backend/local/fadvise_unix.go
@@ -1,4 +1,4 @@
-//go:build linux
+//go:build linux
package local
diff --git a/backend/local/lchmod.go b/backend/local/lchmod.go
index 823718dfe..10fd43096 100644
--- a/backend/local/lchmod.go
+++ b/backend/local/lchmod.go
@@ -1,4 +1,4 @@
-//go:build windows || plan9 || js || linux
+//go:build windows || plan9 || js || linux
package local
diff --git a/backend/local/lchmod_unix.go b/backend/local/lchmod_unix.go
index f1fdc4745..f582bbf2c 100644
--- a/backend/local/lchmod_unix.go
+++ b/backend/local/lchmod_unix.go
@@ -1,4 +1,4 @@
-//go:build !windows && !plan9 && !js && !linux
+//go:build !windows && !plan9 && !js && !linux
package local
diff --git a/backend/local/lchtimes.go b/backend/local/lchtimes.go
index fcabdcc34..db02d1290 100644
--- a/backend/local/lchtimes.go
+++ b/backend/local/lchtimes.go
@@ -1,4 +1,4 @@
-//go:build plan9 || js
+//go:build plan9 || js
package local
diff --git a/backend/local/lchtimes_unix.go b/backend/local/lchtimes_unix.go
index 96889db47..a1f5b43f3 100644
--- a/backend/local/lchtimes_unix.go
+++ b/backend/local/lchtimes_unix.go
@@ -1,4 +1,4 @@
-//go:build !windows && !plan9 && !js
+//go:build !windows && !plan9 && !js
package local
diff --git a/backend/local/lchtimes_windows.go b/backend/local/lchtimes_windows.go
index a6dec9a12..f5b5b8cb7 100644
--- a/backend/local/lchtimes_windows.go
+++ b/backend/local/lchtimes_windows.go
@@ -1,4 +1,4 @@
-//go:build windows
+//go:build windows
package local
diff --git a/backend/local/local.go b/backend/local/local.go
index b4462d8ed..fca2e791e 100644
--- a/backend/local/local.go
+++ b/backend/local/local.go
@@ -1,4 +1,4 @@
-// Package local provides a filesystem interface
+// Package local provides a filesystem interface
package local
import (
@@ -1542,7 +1542,14 @@ func (f *Fs) OpenWriterAt(ctx context.Context, remote string, size int64) (fs.Wr
return nil, errors.New("can't open a symlink for random writing")
}
- out, err := file.OpenFile(o.path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
+ ci := fs.GetConfig(ctx)
+
+ flags := os.O_WRONLY | os.O_CREATE
+ if !ci.MultiThreadResume {
+ flags |= os.O_TRUNC
+ }
+
+ out, err := file.OpenFile(o.path, flags, 0666)
if err != nil {
return nil, err
}
diff --git a/backend/local/local_internal_test.go b/backend/local/local_internal_test.go
index 2fd4fcaac..2839b3ba3 100644
--- a/backend/local/local_internal_test.go
+++ b/backend/local/local_internal_test.go
@@ -1,4 +1,4 @@
-package local
+package local
import (
"bytes"
diff --git a/backend/local/local_internal_windows_test.go b/backend/local/local_internal_windows_test.go
index 6379dd436..e6023526d 100644
--- a/backend/local/local_internal_windows_test.go
+++ b/backend/local/local_internal_windows_test.go
@@ -1,4 +1,4 @@
-//go:build windows
+//go:build windows
package local
diff --git a/backend/local/local_test.go b/backend/local/local_test.go
index d0d54ec26..589afe5d8 100644
--- a/backend/local/local_test.go
+++ b/backend/local/local_test.go
@@ -1,4 +1,4 @@
-// Test Local filesystem interface
+// Test Local filesystem interface
package local_test
import (
diff --git a/backend/local/metadata.go b/backend/local/metadata.go
index 75b195e64..5668ae373 100644
--- a/backend/local/metadata.go
+++ b/backend/local/metadata.go
@@ -1,4 +1,4 @@
-package local
+package local
import (
"fmt"
diff --git a/backend/local/metadata_bsd.go b/backend/local/metadata_bsd.go
index cd5976eaa..fed4bf8fd 100644
--- a/backend/local/metadata_bsd.go
+++ b/backend/local/metadata_bsd.go
@@ -1,4 +1,4 @@
-//go:build darwin || freebsd || netbsd
+//go:build darwin || freebsd || netbsd
package local
diff --git a/backend/local/metadata_linux.go b/backend/local/metadata_linux.go
index dc0b1d233..825fe1b28 100644
--- a/backend/local/metadata_linux.go
+++ b/backend/local/metadata_linux.go
@@ -1,4 +1,4 @@
-//go:build linux
+//go:build linux
package local
diff --git a/backend/local/metadata_other.go b/backend/local/metadata_other.go
index dc6a5a736..989e06c8c 100644
--- a/backend/local/metadata_other.go
+++ b/backend/local/metadata_other.go
@@ -1,4 +1,4 @@
-//go:build dragonfly || plan9 || js
+//go:build dragonfly || plan9 || js
package local
diff --git a/backend/local/metadata_unix.go b/backend/local/metadata_unix.go
index d1695ad7c..930c084f5 100644
--- a/backend/local/metadata_unix.go
+++ b/backend/local/metadata_unix.go
@@ -1,4 +1,4 @@
-//go:build openbsd || solaris
+//go:build openbsd || solaris
package local
diff --git a/backend/local/metadata_windows.go b/backend/local/metadata_windows.go
index 00fad75ba..3f2793966 100644
--- a/backend/local/metadata_windows.go
+++ b/backend/local/metadata_windows.go
@@ -1,4 +1,4 @@
-//go:build windows
+//go:build windows
package local
diff --git a/backend/local/read_device_other.go b/backend/local/read_device_other.go
index 3455ab346..de0f7f285 100644
--- a/backend/local/read_device_other.go
+++ b/backend/local/read_device_other.go
@@ -1,4 +1,4 @@
-// Device reading functions
+// Device reading functions
//go:build !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris
diff --git a/backend/local/read_device_unix.go b/backend/local/read_device_unix.go
index ab7fecbee..d8af1da22 100644
--- a/backend/local/read_device_unix.go
+++ b/backend/local/read_device_unix.go
@@ -1,4 +1,4 @@
-// Device reading functions
+// Device reading functions
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
diff --git a/backend/local/remove_other.go b/backend/local/remove_other.go
index 25a19eedf..34dbd3700 100644
--- a/backend/local/remove_other.go
+++ b/backend/local/remove_other.go
@@ -1,4 +1,4 @@
-//go:build !windows
+//go:build !windows
package local
diff --git a/backend/local/remove_test.go b/backend/local/remove_test.go
index a80075000..5b2001a72 100644
--- a/backend/local/remove_test.go
+++ b/backend/local/remove_test.go
@@ -1,4 +1,4 @@
-package local
+package local
import (
"os"
diff --git a/backend/local/remove_windows.go b/backend/local/remove_windows.go
index 70d65b63e..e792e5ced 100644
--- a/backend/local/remove_windows.go
+++ b/backend/local/remove_windows.go
@@ -1,4 +1,4 @@
-//go:build windows
+//go:build windows
package local
diff --git a/backend/local/setbtime.go b/backend/local/setbtime.go
index bb37b1735..cd9676b4d 100644
--- a/backend/local/setbtime.go
+++ b/backend/local/setbtime.go
@@ -1,4 +1,4 @@
-//go:build !windows
+//go:build !windows
package local
diff --git a/backend/local/setbtime_windows.go b/backend/local/setbtime_windows.go
index 8ae499826..e3180e3e3 100644
--- a/backend/local/setbtime_windows.go
+++ b/backend/local/setbtime_windows.go
@@ -1,4 +1,4 @@
-//go:build windows
+//go:build windows
package local
diff --git a/backend/local/symlink.go b/backend/local/symlink.go
index bdce264c7..b680f9c2d 100644
--- a/backend/local/symlink.go
+++ b/backend/local/symlink.go
@@ -1,4 +1,4 @@
-//go:build !windows && !plan9 && !js
+//go:build !windows && !plan9 && !js
package local
diff --git a/backend/local/symlink_other.go b/backend/local/symlink_other.go
index 2d5bcab07..4bbd191b1 100644
--- a/backend/local/symlink_other.go
+++ b/backend/local/symlink_other.go
@@ -1,4 +1,4 @@
-//go:build windows || plan9 || js
+//go:build windows || plan9 || js
package local
diff --git a/backend/local/tests_test.go b/backend/local/tests_test.go
index f85ea0f15..6da7509ed 100644
--- a/backend/local/tests_test.go
+++ b/backend/local/tests_test.go
@@ -1,4 +1,4 @@
-package local
+package local
import (
"runtime"
diff --git a/backend/local/xattr.go b/backend/local/xattr.go
index e34dc02ad..02e33069e 100644
--- a/backend/local/xattr.go
+++ b/backend/local/xattr.go
@@ -1,4 +1,4 @@
-//go:build !openbsd && !plan9
+//go:build !openbsd && !plan9
package local
diff --git a/backend/local/xattr_unsupported.go b/backend/local/xattr_unsupported.go
index 20c86d07a..ebdd2114c 100644
--- a/backend/local/xattr_unsupported.go
+++ b/backend/local/xattr_unsupported.go
@@ -1,4 +1,4 @@
-// The pkg/xattr module doesn't compile for openbsd or plan9
+// The pkg/xattr module doesn't compile for openbsd or plan9
//go:build openbsd || plan9
diff --git a/backend/mailru/api/bin.go b/backend/mailru/api/bin.go
index de8d1dbb3..f217a5be1 100644
--- a/backend/mailru/api/bin.go
+++ b/backend/mailru/api/bin.go
@@ -1,4 +1,4 @@
-package api
+package api
// BIN protocol constants
const (
diff --git a/backend/mailru/api/helpers.go b/backend/mailru/api/helpers.go
index 6ac2b5d9c..190871d47 100644
--- a/backend/mailru/api/helpers.go
+++ b/backend/mailru/api/helpers.go
@@ -1,4 +1,4 @@
-package api
+package api
// BIN protocol helpers
diff --git a/backend/mailru/api/m1.go b/backend/mailru/api/m1.go
index e4babfecc..e4b4eb72f 100644
--- a/backend/mailru/api/m1.go
+++ b/backend/mailru/api/m1.go
@@ -1,4 +1,4 @@
-// Package api provides types used by the Mail.ru API.
+// Package api provides types used by the Mail.ru API.
package api
import (
diff --git a/backend/mailru/mailru.go b/backend/mailru/mailru.go
index d304bb8a3..5836de515 100644
--- a/backend/mailru/mailru.go
+++ b/backend/mailru/mailru.go
@@ -1,4 +1,4 @@
-// Package mailru provides an interface to the Mail.ru Cloud storage system.
+// Package mailru provides an interface to the Mail.ru Cloud storage system.
package mailru
import (
@@ -400,7 +400,7 @@ type quirks struct {
}
func (q *quirks) parseQuirks(option string) {
- for _, flag := range strings.Split(option, ",") {
+ for flag := range strings.SplitSeq(option, ",") {
switch strings.ToLower(strings.TrimSpace(flag)) {
case "binlist":
// The official client sometimes uses a so called "bin" protocol,
@@ -1770,7 +1770,7 @@ func (f *Fs) parseSpeedupPatterns(patternString string) (err error) {
f.speedupAny = false
uniqueValidPatterns := make(map[string]any)
- for _, pattern := range strings.Split(patternString, ",") {
+ for pattern := range strings.SplitSeq(patternString, ",") {
pattern = strings.ToLower(strings.TrimSpace(pattern))
if pattern == "" {
continue
diff --git a/backend/mailru/mailru_test.go b/backend/mailru/mailru_test.go
index dd05f8abf..751b58b55 100644
--- a/backend/mailru/mailru_test.go
+++ b/backend/mailru/mailru_test.go
@@ -1,4 +1,4 @@
-// Test Mailru filesystem interface
+// Test Mailru filesystem interface
package mailru_test
import (
diff --git a/backend/mailru/mrhash/mrhash.go b/backend/mailru/mrhash/mrhash.go
index eb49c796f..2c7df3eec 100644
--- a/backend/mailru/mrhash/mrhash.go
+++ b/backend/mailru/mrhash/mrhash.go
@@ -1,4 +1,4 @@
-// Package mrhash implements the mailru hash, which is a modified SHA1.
+// Package mrhash implements the mailru hash, which is a modified SHA1.
// If file size is less than or equal to the SHA1 block size (20 bytes),
// its hash is simply its data right-padded with zero bytes.
// Hash sum of a larger file is computed as a SHA1 sum of the file data
diff --git a/backend/mailru/mrhash/mrhash_test.go b/backend/mailru/mrhash/mrhash_test.go
index a143fcec0..1661789e5 100644
--- a/backend/mailru/mrhash/mrhash_test.go
+++ b/backend/mailru/mrhash/mrhash_test.go
@@ -1,4 +1,4 @@
-package mrhash_test
+package mrhash_test
import (
"encoding/hex"
diff --git a/backend/mega/mega.go b/backend/mega/mega.go
index eec71f556..1efae4f3b 100644
--- a/backend/mega/mega.go
+++ b/backend/mega/mega.go
@@ -1,4 +1,4 @@
-// Package mega provides an interface to the Mega
+// Package mega provides an interface to the Mega
// object storage system.
package mega
diff --git a/backend/mega/mega_test.go b/backend/mega/mega_test.go
index 8016b39ae..d1e297874 100644
--- a/backend/mega/mega_test.go
+++ b/backend/mega/mega_test.go
@@ -1,4 +1,4 @@
-// Test Mega filesystem interface
+// Test Mega filesystem interface
package mega_test
import (
diff --git a/backend/memory/memory.go b/backend/memory/memory.go
index 92a79bcdc..dc2352040 100644
--- a/backend/memory/memory.go
+++ b/backend/memory/memory.go
@@ -1,4 +1,4 @@
-// Package memory provides an interface to an in memory object storage system
+// Package memory provides an interface to an in memory object storage system
package memory
import (
diff --git a/backend/memory/memory_internal_test.go b/backend/memory/memory_internal_test.go
index f79c20f8c..7bc2140c4 100644
--- a/backend/memory/memory_internal_test.go
+++ b/backend/memory/memory_internal_test.go
@@ -1,4 +1,4 @@
-package memory
+package memory
import (
"context"
diff --git a/backend/memory/memory_test.go b/backend/memory/memory_test.go
index 9989bc60d..3ca3e1589 100644
--- a/backend/memory/memory_test.go
+++ b/backend/memory/memory_test.go
@@ -1,4 +1,4 @@
-// Test memory filesystem interface
+// Test memory filesystem interface
package memory
import (
diff --git a/backend/netstorage/netstorage.go b/backend/netstorage/netstorage.go
index 74964bd07..bdb96a157 100755
--- a/backend/netstorage/netstorage.go
+++ b/backend/netstorage/netstorage.go
@@ -1,4 +1,4 @@
-// Package netstorage provides an interface to Akamai NetStorage API
+// Package netstorage provides an interface to Akamai NetStorage API
package netstorage
import (
diff --git a/backend/netstorage/netstorage_test.go b/backend/netstorage/netstorage_test.go
index 2b50e392c..9be1b5f8c 100644
--- a/backend/netstorage/netstorage_test.go
+++ b/backend/netstorage/netstorage_test.go
@@ -1,4 +1,4 @@
-package netstorage_test
+package netstorage_test
import (
"testing"
diff --git a/backend/onedrive/api/types.go b/backend/onedrive/api/types.go
index 7fe032c23..6f34f2120 100644
--- a/backend/onedrive/api/types.go
+++ b/backend/onedrive/api/types.go
@@ -1,4 +1,4 @@
-// Package api provides types used by the OneDrive API.
+// Package api provides types used by the OneDrive API.
package api
import (
diff --git a/backend/onedrive/metadata.go b/backend/onedrive/metadata.go
index 63f9e7571..3087572ef 100644
--- a/backend/onedrive/metadata.go
+++ b/backend/onedrive/metadata.go
@@ -1,4 +1,4 @@
-package onedrive
+package onedrive
import (
"context"
@@ -243,7 +243,6 @@ func (m *Metadata) Get(ctx context.Context) (metadata fs.Metadata, err error) {
func (m *Metadata) Set(ctx context.Context, metadata fs.Metadata) (numSet int, err error) {
numSet = 0
for k, v := range metadata {
- k, v := k, v
switch k {
case "mtime":
t, err := time.Parse(timeFormatIn, v)
@@ -422,12 +421,7 @@ func (m *Metadata) orderPermissions(xs []*api.PermissionsType) {
if hasUserIdentity(p.GetGrantedTo(m.fs.driveType)) {
return true
}
- for _, identity := range p.GetGrantedToIdentities(m.fs.driveType) {
- if hasUserIdentity(identity) {
- return true
- }
- }
- return false
+ return slices.ContainsFunc(p.GetGrantedToIdentities(m.fs.driveType), hasUserIdentity)
}
// Put Permissions with a user first, leaving unsorted otherwise
slices.SortStableFunc(xs, func(a, b *api.PermissionsType) int {
diff --git a/backend/onedrive/metadata_test.go b/backend/onedrive/metadata_test.go
index bc7415233..2879fe4c1 100644
--- a/backend/onedrive/metadata_test.go
+++ b/backend/onedrive/metadata_test.go
@@ -1,4 +1,4 @@
-package onedrive
+package onedrive
import (
"encoding/json"
diff --git a/backend/onedrive/onedrive.go b/backend/onedrive/onedrive.go
index ffca73dbe..ccb4df4c9 100644
--- a/backend/onedrive/onedrive.go
+++ b/backend/onedrive/onedrive.go
@@ -1,4 +1,4 @@
-// Package onedrive provides an interface to the Microsoft OneDrive
+// Package onedrive provides an interface to the Microsoft OneDrive
// object storage system.
package onedrive
diff --git a/backend/onedrive/onedrive_internal_test.go b/backend/onedrive/onedrive_internal_test.go
index 843ede35f..80023bf9a 100644
--- a/backend/onedrive/onedrive_internal_test.go
+++ b/backend/onedrive/onedrive_internal_test.go
@@ -1,4 +1,4 @@
-package onedrive
+package onedrive
import (
"context"
diff --git a/backend/onedrive/onedrive_test.go b/backend/onedrive/onedrive_test.go
index c0c4340e4..140a20ace 100644
--- a/backend/onedrive/onedrive_test.go
+++ b/backend/onedrive/onedrive_test.go
@@ -1,4 +1,4 @@
-// Test OneDrive filesystem interface
+// Test OneDrive filesystem interface
package onedrive
import (
diff --git a/backend/onedrive/quickxorhash/quickxorhash.go b/backend/onedrive/quickxorhash/quickxorhash.go
index 890469716..65e36c8a2 100644
--- a/backend/onedrive/quickxorhash/quickxorhash.go
+++ b/backend/onedrive/quickxorhash/quickxorhash.go
@@ -1,4 +1,4 @@
-// Package quickxorhash provides the quickXorHash algorithm which is a
+// Package quickxorhash provides the quickXorHash algorithm which is a
// quick, simple non-cryptographic hash algorithm that works by XORing
// the bytes in a circular-shifting fashion.
//
diff --git a/backend/onedrive/quickxorhash/quickxorhash_test.go b/backend/onedrive/quickxorhash/quickxorhash_test.go
index a239ed02f..0d960b397 100644
--- a/backend/onedrive/quickxorhash/quickxorhash_test.go
+++ b/backend/onedrive/quickxorhash/quickxorhash_test.go
@@ -1,4 +1,4 @@
-package quickxorhash
+package quickxorhash
import (
"crypto/rand"
@@ -172,8 +172,8 @@ func BenchmarkQuickXorHash(b *testing.B) {
require.NoError(b, err)
require.Equal(b, len(buf), n)
h := New()
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
+
+ for b.Loop() {
h.Reset()
h.Write(buf)
h.Sum(nil)
diff --git a/backend/opendrive/opendrive.go b/backend/opendrive/opendrive.go
index 4fa80f351..8c7be26d1 100644
--- a/backend/opendrive/opendrive.go
+++ b/backend/opendrive/opendrive.go
@@ -1,4 +1,4 @@
-// Package opendrive provides an interface to the OpenDrive storage system.
+// Package opendrive provides an interface to the OpenDrive storage system.
package opendrive
import (
diff --git a/backend/opendrive/opendrive_test.go b/backend/opendrive/opendrive_test.go
index f857ed478..f3f39e94c 100644
--- a/backend/opendrive/opendrive_test.go
+++ b/backend/opendrive/opendrive_test.go
@@ -1,4 +1,4 @@
-// Test Opendrive filesystem interface
+// Test Opendrive filesystem interface
package opendrive_test
import (
diff --git a/backend/opendrive/types.go b/backend/opendrive/types.go
index fa897e0d7..faadde9a4 100644
--- a/backend/opendrive/types.go
+++ b/backend/opendrive/types.go
@@ -1,4 +1,4 @@
-package opendrive
+package opendrive
import (
"encoding/json"
diff --git a/backend/oracleobjectstorage/byok.go b/backend/oracleobjectstorage/byok.go
index b3392515b..3822173cb 100644
--- a/backend/oracleobjectstorage/byok.go
+++ b/backend/oracleobjectstorage/byok.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !solaris && !js
+//go:build !plan9 && !solaris && !js
package oracleobjectstorage
diff --git a/backend/oracleobjectstorage/client.go b/backend/oracleobjectstorage/client.go
index 0749c4699..496a732aa 100644
--- a/backend/oracleobjectstorage/client.go
+++ b/backend/oracleobjectstorage/client.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !solaris && !js
+//go:build !plan9 && !solaris && !js
package oracleobjectstorage
diff --git a/backend/oracleobjectstorage/command.go b/backend/oracleobjectstorage/command.go
index fa687fef8..5dbcc62b8 100644
--- a/backend/oracleobjectstorage/command.go
+++ b/backend/oracleobjectstorage/command.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !solaris && !js
+//go:build !plan9 && !solaris && !js
package oracleobjectstorage
diff --git a/backend/oracleobjectstorage/copy.go b/backend/oracleobjectstorage/copy.go
index 76b5d939c..a55e6351b 100644
--- a/backend/oracleobjectstorage/copy.go
+++ b/backend/oracleobjectstorage/copy.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !solaris && !js
+//go:build !plan9 && !solaris && !js
package oracleobjectstorage
diff --git a/backend/oracleobjectstorage/multipart.go b/backend/oracleobjectstorage/multipart.go
index d3f471b91..70ab9ed4e 100644
--- a/backend/oracleobjectstorage/multipart.go
+++ b/backend/oracleobjectstorage/multipart.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !solaris && !js
+//go:build !plan9 && !solaris && !js
package oracleobjectstorage
diff --git a/backend/oracleobjectstorage/object.go b/backend/oracleobjectstorage/object.go
index d9cc20f84..758a6e6db 100644
--- a/backend/oracleobjectstorage/object.go
+++ b/backend/oracleobjectstorage/object.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !solaris && !js
+//go:build !plan9 && !solaris && !js
package oracleobjectstorage
diff --git a/backend/oracleobjectstorage/options.go b/backend/oracleobjectstorage/options.go
index f24a4d156..ea84b20ac 100644
--- a/backend/oracleobjectstorage/options.go
+++ b/backend/oracleobjectstorage/options.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !solaris && !js
+//go:build !plan9 && !solaris && !js
package oracleobjectstorage
diff --git a/backend/oracleobjectstorage/oracleobjectstorage.go b/backend/oracleobjectstorage/oracleobjectstorage.go
index 9e27e997f..6a4281a5f 100644
--- a/backend/oracleobjectstorage/oracleobjectstorage.go
+++ b/backend/oracleobjectstorage/oracleobjectstorage.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !solaris && !js
+//go:build !plan9 && !solaris && !js
// Package oracleobjectstorage provides an interface to the OCI object storage system.
package oracleobjectstorage
diff --git a/backend/oracleobjectstorage/oracleobjectstorage_test.go b/backend/oracleobjectstorage/oracleobjectstorage_test.go
index f41dfb605..2f269d8a3 100644
--- a/backend/oracleobjectstorage/oracleobjectstorage_test.go
+++ b/backend/oracleobjectstorage/oracleobjectstorage_test.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !solaris && !js
+//go:build !plan9 && !solaris && !js
package oracleobjectstorage
diff --git a/backend/oracleobjectstorage/oracleobjectstorage_unsupported.go b/backend/oracleobjectstorage/oracleobjectstorage_unsupported.go
index ccbc39316..d1848805c 100644
--- a/backend/oracleobjectstorage/oracleobjectstorage_unsupported.go
+++ b/backend/oracleobjectstorage/oracleobjectstorage_unsupported.go
@@ -1,4 +1,4 @@
-// Build for oracleobjectstorage for unsupported platforms to stop go complaining
+// Build for oracleobjectstorage for unsupported platforms to stop go complaining
// about "no buildable Go source files "
//go:build plan9 || solaris || js
diff --git a/backend/oracleobjectstorage/waiter.go b/backend/oracleobjectstorage/waiter.go
index d09f00033..edf3bcedd 100644
--- a/backend/oracleobjectstorage/waiter.go
+++ b/backend/oracleobjectstorage/waiter.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !solaris && !js
+//go:build !plan9 && !solaris && !js
package oracleobjectstorage
diff --git a/backend/pcloud/api/types.go b/backend/pcloud/api/types.go
index 6f2d6361b..e46527b7e 100644
--- a/backend/pcloud/api/types.go
+++ b/backend/pcloud/api/types.go
@@ -1,4 +1,4 @@
-// Package api has type definitions for pcloud
+// Package api has type definitions for pcloud
//
// Converted from the API docs with help from https://mholt.github.io/json-to-go/
package api
diff --git a/backend/pcloud/pcloud.go b/backend/pcloud/pcloud.go
index d4962e30f..ae3883e50 100644
--- a/backend/pcloud/pcloud.go
+++ b/backend/pcloud/pcloud.go
@@ -1,4 +1,4 @@
-// Package pcloud provides an interface to the Pcloud
+// Package pcloud provides an interface to the Pcloud
// object storage system.
package pcloud
diff --git a/backend/pcloud/pcloud_test.go b/backend/pcloud/pcloud_test.go
index cdc89a98b..6ae56afa4 100644
--- a/backend/pcloud/pcloud_test.go
+++ b/backend/pcloud/pcloud_test.go
@@ -1,4 +1,4 @@
-// Test Pcloud filesystem interface
+// Test Pcloud filesystem interface
package pcloud_test
import (
diff --git a/backend/pcloud/writer_at.go b/backend/pcloud/writer_at.go
index db91349db..7f26350c5 100644
--- a/backend/pcloud/writer_at.go
+++ b/backend/pcloud/writer_at.go
@@ -1,4 +1,4 @@
-package pcloud
+package pcloud
import (
"bytes"
diff --git a/backend/pikpak/api/types.go b/backend/pikpak/api/types.go
index 0e2d8049b..fccb0b60d 100644
--- a/backend/pikpak/api/types.go
+++ b/backend/pikpak/api/types.go
@@ -1,10 +1,11 @@
-// Package api has type definitions for pikpak
+// Package api has type definitions for pikpak
//
// Manually obtained from the API responses using Browse Dev. Tool and https://mholt.github.io/json-to-go/
package api
import (
"fmt"
+ "net/url"
"reflect"
"strconv"
"time"
@@ -136,8 +137,25 @@ type Link struct {
}
// Valid reports whether l is non-nil, has an URL, and is not expired.
+// It primarily checks the URL's expire query parameter, falling back to the Expire field.
func (l *Link) Valid() bool {
- return l != nil && l.URL != "" && time.Now().Add(10*time.Second).Before(time.Time(l.Expire))
+ if l == nil || l.URL == "" {
+ return false
+ }
+
+ // Primary validation: check URL's expire query parameter
+ if u, err := url.Parse(l.URL); err == nil {
+ if expireStr := u.Query().Get("expire"); expireStr != "" {
+ // Try parsing as Unix timestamp (seconds)
+ if expireInt, err := strconv.ParseInt(expireStr, 10, 64); err == nil {
+ expireTime := time.Unix(expireInt, 0)
+ return time.Now().Add(10 * time.Second).Before(expireTime)
+ }
+ }
+ }
+
+ // Fallback validation: use the Expire field if URL parsing didn't work
+ return time.Now().Add(10 * time.Second).Before(time.Time(l.Expire))
}
// URL is a basic form of URL
diff --git a/backend/pikpak/api/types_test.go b/backend/pikpak/api/types_test.go
new file mode 100644
index 000000000..f6fc27ca0
--- /dev/null
+++ b/backend/pikpak/api/types_test.go
@@ -0,0 +1,99 @@
+package api
+
+import (
+ "fmt"
+ "testing"
+ "time"
+)
+
+// TestLinkValid tests the Link.Valid method for various scenarios
+func TestLinkValid(t *testing.T) {
+ tests := []struct {
+ name string
+ link *Link
+ expected bool
+ desc string
+ }{
+ {
+ name: "nil link",
+ link: nil,
+ expected: false,
+ desc: "nil link should be invalid",
+ },
+ {
+ name: "empty URL",
+ link: &Link{URL: ""},
+ expected: false,
+ desc: "empty URL should be invalid",
+ },
+ {
+ name: "valid URL with future expire parameter",
+ link: &Link{
+ URL: fmt.Sprintf("https://example.com/file?expire=%d", time.Now().Add(time.Hour).Unix()),
+ },
+ expected: true,
+ desc: "URL with future expire parameter should be valid",
+ },
+ {
+ name: "expired URL with past expire parameter",
+ link: &Link{
+ URL: fmt.Sprintf("https://example.com/file?expire=%d", time.Now().Add(-time.Hour).Unix()),
+ },
+ expected: false,
+ desc: "URL with past expire parameter should be invalid",
+ },
+ {
+ name: "URL expire parameter takes precedence over Expire field",
+ link: &Link{
+ URL: fmt.Sprintf("https://example.com/file?expire=%d", time.Now().Add(time.Hour).Unix()),
+ Expire: Time(time.Now().Add(-time.Hour)), // Fallback is expired
+ },
+ expected: true,
+ desc: "URL expire parameter should take precedence over Expire field",
+ },
+ {
+ name: "URL expire parameter within 10 second buffer should be invalid",
+ link: &Link{
+ URL: fmt.Sprintf("https://example.com/file?expire=%d", time.Now().Add(5*time.Second).Unix()),
+ },
+ expected: false,
+ desc: "URL expire parameter within 10 second buffer should be invalid",
+ },
+ {
+ name: "fallback to Expire field when no URL expire parameter",
+ link: &Link{
+ URL: "https://example.com/file",
+ Expire: Time(time.Now().Add(time.Hour)),
+ },
+ expected: true,
+ desc: "should fallback to Expire field when URL has no expire parameter",
+ },
+ {
+ name: "fallback to Expire field when URL expire parameter is invalid",
+ link: &Link{
+ URL: "https://example.com/file?expire=invalid",
+ Expire: Time(time.Now().Add(time.Hour)),
+ },
+ expected: true,
+ desc: "should fallback to Expire field when URL expire parameter is unparseable",
+ },
+ {
+ name: "invalid when both URL expire and Expire field are expired",
+ link: &Link{
+ URL: fmt.Sprintf("https://example.com/file?expire=%d", time.Now().Add(-time.Hour).Unix()),
+ Expire: Time(time.Now().Add(-time.Hour)),
+ },
+ expected: false,
+ desc: "should be invalid when both URL expire and Expire field are expired",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := tt.link.Valid()
+ if result != tt.expected {
+ t.Errorf("Link.Valid() = %v, expected %v. %s", result, tt.expected, tt.desc)
+ }
+ })
+ }
+}
diff --git a/backend/pikpak/helper.go b/backend/pikpak/helper.go
index 9bc8ea041..cd2e68745 100644
--- a/backend/pikpak/helper.go
+++ b/backend/pikpak/helper.go
@@ -1,4 +1,4 @@
-package pikpak
+package pikpak
import (
"bytes"
diff --git a/backend/pikpak/multipart.go b/backend/pikpak/multipart.go
index 85dc8f2d5..0683555df 100644
--- a/backend/pikpak/multipart.go
+++ b/backend/pikpak/multipart.go
@@ -1,4 +1,4 @@
-package pikpak
+package pikpak
import (
"context"
diff --git a/backend/pikpak/pikpak.go b/backend/pikpak/pikpak.go
index e8638af39..b986e704a 100644
--- a/backend/pikpak/pikpak.go
+++ b/backend/pikpak/pikpak.go
@@ -1,4 +1,4 @@
-// Package pikpak provides an interface to the PikPak
+// Package pikpak provides an interface to the PikPak
package pikpak
// ------------------------------------------------------------
diff --git a/backend/pikpak/pikpak_test.go b/backend/pikpak/pikpak_test.go
index ac9d6b8c3..497d13d9e 100644
--- a/backend/pikpak/pikpak_test.go
+++ b/backend/pikpak/pikpak_test.go
@@ -1,4 +1,4 @@
-// Test PikPak filesystem interface
+// Test PikPak filesystem interface
package pikpak
import (
diff --git a/backend/pixeldrain/api_client.go b/backend/pixeldrain/api_client.go
index e422f61c4..a75ff6e15 100644
--- a/backend/pixeldrain/api_client.go
+++ b/backend/pixeldrain/api_client.go
@@ -1,4 +1,4 @@
-package pixeldrain
+package pixeldrain
import (
"context"
diff --git a/backend/pixeldrain/pixeldrain.go b/backend/pixeldrain/pixeldrain.go
index abf6c1f57..0991cabb9 100644
--- a/backend/pixeldrain/pixeldrain.go
+++ b/backend/pixeldrain/pixeldrain.go
@@ -1,4 +1,4 @@
-// Package pixeldrain provides an interface to the Pixeldrain object storage
+// Package pixeldrain provides an interface to the Pixeldrain object storage
// system.
package pixeldrain
diff --git a/backend/pixeldrain/pixeldrain_test.go b/backend/pixeldrain/pixeldrain_test.go
index f36975243..2bbc06dcc 100644
--- a/backend/pixeldrain/pixeldrain_test.go
+++ b/backend/pixeldrain/pixeldrain_test.go
@@ -1,4 +1,4 @@
-// Test pixeldrain filesystem interface
+// Test pixeldrain filesystem interface
package pixeldrain_test
import (
diff --git a/backend/premiumizeme/api/types.go b/backend/premiumizeme/api/types.go
index 45c65fd1b..e0cd14703 100644
--- a/backend/premiumizeme/api/types.go
+++ b/backend/premiumizeme/api/types.go
@@ -1,4 +1,4 @@
-// Package api contains definitions for using the premiumize.me API
+// Package api contains definitions for using the premiumize.me API
package api
import "fmt"
diff --git a/backend/premiumizeme/premiumizeme.go b/backend/premiumizeme/premiumizeme.go
index 3991d19ee..41fe59dfa 100644
--- a/backend/premiumizeme/premiumizeme.go
+++ b/backend/premiumizeme/premiumizeme.go
@@ -1,4 +1,4 @@
-// Package premiumizeme provides an interface to the premiumize.me
+// Package premiumizeme provides an interface to the premiumize.me
// object storage system.
package premiumizeme
diff --git a/backend/premiumizeme/premiumizeme_test.go b/backend/premiumizeme/premiumizeme_test.go
index de5bfa2a9..b2ca6ec7e 100644
--- a/backend/premiumizeme/premiumizeme_test.go
+++ b/backend/premiumizeme/premiumizeme_test.go
@@ -1,4 +1,4 @@
-// Test filesystem interface
+// Test filesystem interface
package premiumizeme_test
import (
diff --git a/backend/protondrive/protondrive.go b/backend/protondrive/protondrive.go
index 821e3f9c7..1a68f17b6 100644
--- a/backend/protondrive/protondrive.go
+++ b/backend/protondrive/protondrive.go
@@ -1,4 +1,4 @@
-// Package protondrive implements the Proton Drive backend
+// Package protondrive implements the Proton Drive backend
package protondrive
import (
@@ -13,6 +13,8 @@ import (
protonDriveAPI "github.com/henrybear327/Proton-API-Bridge"
"github.com/henrybear327/go-proton-api"
+ "github.com/pquerna/otp/totp"
+
"github.com/rclone/rclone/fs"
"github.com/rclone/rclone/fs/config"
"github.com/rclone/rclone/fs/config/configmap"
@@ -87,6 +89,17 @@ The value can also be provided with --protondrive-2fa=000000
The 2FA code of your proton drive account if the account is set up with
two-factor authentication`,
Required: false,
+ }, {
+ Name: "otp_secret_key",
+ Help: `The OTP secret key
+
+The value can also be provided with --protondrive-otp-secret-key=ABCDEFGHIJKLMNOPQRSTUVWXYZ234567
+
+The OTP secret key of your proton drive account if the account is set up with
+two-factor authentication`,
+ Required: false,
+ Sensitive: true,
+ IsPassword: true,
}, {
Name: clientUIDKey,
Help: "Client uid key (internal use only)",
@@ -191,6 +204,7 @@ type Options struct {
Password string `config:"password"`
MailboxPassword string `config:"mailbox_password"`
TwoFA string `config:"2fa"`
+ OtpSecretKey string `config:"otp_secret_key"`
// advanced
Enc encoder.MultiEncoder `config:"encoding"`
@@ -356,7 +370,15 @@ func newProtonDrive(ctx context.Context, f *Fs, opt *Options, m configmap.Mapper
config.FirstLoginCredential.Username = opt.Username
config.FirstLoginCredential.Password = opt.Password
config.FirstLoginCredential.MailboxPassword = opt.MailboxPassword
+ // if 2FA code is provided, use it; otherwise, generate one using the OTP secret key if provided
config.FirstLoginCredential.TwoFA = opt.TwoFA
+ if opt.TwoFA == "" && opt.OtpSecretKey != "" {
+ code, err := totp.GenerateCode(opt.OtpSecretKey, time.Now())
+ if err != nil {
+ return nil, fmt.Errorf("couldn't generate 2FA code: %w", err)
+ }
+ config.FirstLoginCredential.TwoFA = code
+ }
protonDrive, auth, err := protonDriveAPI.NewProtonDrive(ctx, config, authHandler, deAuthHandler)
if err != nil {
return nil, fmt.Errorf("couldn't initialize a new proton drive instance: %w", err)
@@ -395,6 +417,14 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e
}
}
+ if opt.OtpSecretKey != "" {
+ var err error
+ opt.OtpSecretKey, err = obscure.Reveal(opt.OtpSecretKey)
+ if err != nil {
+ return nil, fmt.Errorf("couldn't decrypt OtpSecretKey: %w", err)
+ }
+ }
+
ci := fs.GetConfig(ctx)
root = strings.Trim(root, "/")
diff --git a/backend/protondrive/protondrive_test.go b/backend/protondrive/protondrive_test.go
index 86aa0c075..74cbd7f8c 100644
--- a/backend/protondrive/protondrive_test.go
+++ b/backend/protondrive/protondrive_test.go
@@ -1,4 +1,4 @@
-package protondrive_test
+package protondrive_test
import (
"testing"
diff --git a/backend/putio/error.go b/backend/putio/error.go
index 194315f53..f13240e39 100644
--- a/backend/putio/error.go
+++ b/backend/putio/error.go
@@ -1,4 +1,4 @@
-package putio
+package putio
import (
"context"
diff --git a/backend/putio/fs.go b/backend/putio/fs.go
index a0570c971..e81580750 100644
--- a/backend/putio/fs.go
+++ b/backend/putio/fs.go
@@ -1,4 +1,4 @@
-package putio
+package putio
import (
"bytes"
diff --git a/backend/putio/object.go b/backend/putio/object.go
index 86edb008f..1a7322bb3 100644
--- a/backend/putio/object.go
+++ b/backend/putio/object.go
@@ -1,4 +1,4 @@
-package putio
+package putio
import (
"context"
diff --git a/backend/putio/putio.go b/backend/putio/putio.go
index e003b89dc..0d2cf4559 100644
--- a/backend/putio/putio.go
+++ b/backend/putio/putio.go
@@ -1,4 +1,4 @@
-// Package putio provides an interface to the put.io storage system.
+// Package putio provides an interface to the put.io storage system.
package putio
import (
diff --git a/backend/putio/putio_test.go b/backend/putio/putio_test.go
index 5cad6dac7..c9e6bbcdf 100644
--- a/backend/putio/putio_test.go
+++ b/backend/putio/putio_test.go
@@ -1,4 +1,4 @@
-// Test Put.io filesystem interface
+// Test Put.io filesystem interface
package putio
import (
diff --git a/backend/qingstor/qingstor.go b/backend/qingstor/qingstor.go
index 39113d149..43230fd06 100644
--- a/backend/qingstor/qingstor.go
+++ b/backend/qingstor/qingstor.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !js
+//go:build !plan9 && !js
// Package qingstor provides an interface to QingStor object storage
// Home: https://www.qingcloud.com/
diff --git a/backend/qingstor/qingstor_test.go b/backend/qingstor/qingstor_test.go
index d599b79e0..369f9f733 100644
--- a/backend/qingstor/qingstor_test.go
+++ b/backend/qingstor/qingstor_test.go
@@ -1,4 +1,4 @@
-// Test QingStor filesystem interface
+// Test QingStor filesystem interface
//go:build !plan9 && !js
diff --git a/backend/qingstor/qingstor_unsupported.go b/backend/qingstor/qingstor_unsupported.go
index e16a2e80c..344f5a8a5 100644
--- a/backend/qingstor/qingstor_unsupported.go
+++ b/backend/qingstor/qingstor_unsupported.go
@@ -1,4 +1,4 @@
-// Build for unsupported platforms to stop go complaining
+// Build for unsupported platforms to stop go complaining
// about "no buildable Go source files "
//go:build plan9 || js
diff --git a/backend/qingstor/upload.go b/backend/qingstor/upload.go
index 14ea24aa9..10763f1a4 100644
--- a/backend/qingstor/upload.go
+++ b/backend/qingstor/upload.go
@@ -1,4 +1,4 @@
-// Upload object to QingStor
+// Upload object to QingStor
//go:build !plan9 && !js
diff --git a/backend/quatrix/api/types.go b/backend/quatrix/api/types.go
index 7930fe4d3..7a2d11d67 100644
--- a/backend/quatrix/api/types.go
+++ b/backend/quatrix/api/types.go
@@ -1,4 +1,4 @@
-// Package api provides types used by the Quatrix API.
+// Package api provides types used by the Quatrix API.
package api
import (
diff --git a/backend/quatrix/quatrix.go b/backend/quatrix/quatrix.go
index 19d6f0119..ca3bbd51b 100644
--- a/backend/quatrix/quatrix.go
+++ b/backend/quatrix/quatrix.go
@@ -1,4 +1,4 @@
-// Package quatrix provides an interface to the Quatrix by Maytech
+// Package quatrix provides an interface to the Quatrix by Maytech
// object storage system.
package quatrix
diff --git a/backend/quatrix/quatrix_test.go b/backend/quatrix/quatrix_test.go
index c9759f188..3495b825c 100644
--- a/backend/quatrix/quatrix_test.go
+++ b/backend/quatrix/quatrix_test.go
@@ -1,4 +1,4 @@
-// Test Quatrix filesystem interface
+// Test Quatrix filesystem interface
package quatrix_test
import (
diff --git a/backend/quatrix/upload_memory.go b/backend/quatrix/upload_memory.go
index 290c525da..1a1c3b678 100644
--- a/backend/quatrix/upload_memory.go
+++ b/backend/quatrix/upload_memory.go
@@ -1,4 +1,4 @@
-package quatrix
+package quatrix
import (
"sync"
@@ -59,11 +59,7 @@ func (u *UploadMemoryManager) Consume(fileID string, neededMemory int64, speed f
defer func() { u.fileUsage[fileID] = borrowed }()
- effectiveChunkSize := max(int64(speed*u.effectiveTime.Seconds()), u.reserved)
-
- if neededMemory < effectiveChunkSize {
- effectiveChunkSize = neededMemory
- }
+ effectiveChunkSize := min(neededMemory, max(int64(speed*u.effectiveTime.Seconds()), u.reserved))
if effectiveChunkSize <= u.reserved {
return effectiveChunkSize
diff --git a/backend/s3/gen_setfrom.go b/backend/s3/gen_setfrom.go
index 4748b4da0..23c9ea660 100644
--- a/backend/s3/gen_setfrom.go
+++ b/backend/s3/gen_setfrom.go
@@ -1,4 +1,4 @@
-// Generate boilerplate code for setting similar structs from each other
+// Generate boilerplate code for setting similar structs from each other
//go:build ignore
diff --git a/backend/s3/ibm_signer.go b/backend/s3/ibm_signer.go
index e1f9d1831..1cb927593 100644
--- a/backend/s3/ibm_signer.go
+++ b/backend/s3/ibm_signer.go
@@ -1,4 +1,4 @@
-package s3
+package s3
import (
"context"
diff --git a/backend/s3/ibm_signer_test.go b/backend/s3/ibm_signer_test.go
index bf1f52ff5..519460369 100644
--- a/backend/s3/ibm_signer_test.go
+++ b/backend/s3/ibm_signer_test.go
@@ -1,4 +1,4 @@
-package s3
+package s3
import (
"context"
diff --git a/backend/s3/s3.go b/backend/s3/s3.go
index 3808b48a2..48c04684b 100644
--- a/backend/s3/s3.go
+++ b/backend/s3/s3.go
@@ -1,4 +1,4 @@
-// Package s3 provides an interface to Amazon S3 object storage
+// Package s3 provides an interface to Amazon S3 object storage
package s3
//go:generate go run gen_setfrom.go -o setfrom.go
@@ -104,12 +104,18 @@ var providerOption = fs.Option{
}, {
Value: "Exaba",
Help: "Exaba Object Storage",
+ }, {
+ Value: "FileLu",
+ Help: "FileLu S5 (S3-Compatible Object Storage)",
}, {
Value: "FlashBlade",
Help: "Pure Storage FlashBlade Object Storage",
}, {
Value: "GCS",
Help: "Google Cloud Storage",
+ }, {
+ Value: "Hetzner",
+ Help: "Hetzner Object Storage",
}, {
Value: "HuaweiOBS",
Help: "Huawei Object Storage Service",
@@ -158,6 +164,9 @@ var providerOption = fs.Option{
}, {
Value: "Petabox",
Help: "Petabox Object Storage",
+ }, {
+ Value: "Rabata",
+ Help: "Rabata Cloud Storage",
}, {
Value: "RackCorp",
Help: "RackCorp Object Storage",
@@ -342,83 +351,49 @@ func init() {
}},
}, {
Name: "region",
- Help: "region - the location where your bucket will be created and your data stored.\n",
- Provider: "RackCorp",
+ Help: "Region to connect to.",
+ Provider: "Cloudflare",
+ Examples: []fs.OptionExample{{
+ Value: "auto",
+ Help: "R2 buckets are automatically distributed across Cloudflare's data centers for low latency.",
+ }},
+ }, {
+ Name: "region",
+ Help: "Region to connect to for FileLu S5.",
+ Provider: "FileLu",
Examples: []fs.OptionExample{{
Value: "global",
- Help: "Global CDN (All locations) Region",
- }, {
- Value: "au",
- Help: "Australia (All states)",
- }, {
- Value: "au-nsw",
- Help: "NSW (Australia) Region",
- }, {
- Value: "au-qld",
- Help: "QLD (Australia) Region",
- }, {
- Value: "au-vic",
- Help: "VIC (Australia) Region",
- }, {
- Value: "au-wa",
- Help: "Perth (Australia) Region",
- }, {
- Value: "ph",
- Help: "Manila (Philippines) Region",
+ Help: "Global",
}, {
- Value: "th",
- Help: "Bangkok (Thailand) Region",
- }, {
- Value: "hk",
- Help: "HK (Hong Kong) Region",
- }, {
- Value: "mn",
- Help: "Ulaanbaatar (Mongolia) Region",
- }, {
- Value: "kg",
- Help: "Bishkek (Kyrgyzstan) Region",
- }, {
- Value: "id",
- Help: "Jakarta (Indonesia) Region",
- }, {
- Value: "jp",
- Help: "Tokyo (Japan) Region",
- }, {
- Value: "sg",
- Help: "SG (Singapore) Region",
- }, {
- Value: "de",
- Help: "Frankfurt (Germany) Region",
- }, {
- Value: "us",
- Help: "USA (AnyCast) Region",
+ Value: "us-east",
+ Help: "North America (US-East)",
}, {
- Value: "us-east-1",
- Help: "New York (USA) Region",
+ Value: "eu-central",
+ Help: "Europe (EU-Central)",
}, {
- Value: "us-west-1",
- Help: "Freemont (USA) Region",
+ Value: "ap-southeast",
+ Help: "Asia Pacific (AP-Southeast)",
}, {
- Value: "nz",
- Help: "Auckland (New Zealand) Region",
+ Value: "me-central",
+ Help: "Middle East (ME-Central)",
}},
}, {
Name: "region",
Help: "Region to connect to.",
- Provider: "Scaleway",
+ Provider: "Hetzner",
Examples: []fs.OptionExample{{
- Value: "nl-ams",
- Help: "Amsterdam, The Netherlands",
+ Value: "hel1",
+ Help: "Helsinki",
}, {
- Value: "fr-par",
- Help: "Paris, France",
+ Value: "fsn1",
+ Help: "Falkenstein",
}, {
- Value: "pl-waw",
- Help: "Warsaw, Poland",
+ Value: "nbg1",
+ Help: "Nuremberg",
}},
}, {
Name: "region",
- Help: "Region to connect to. - the location where your bucket will be created and your data stored. Need bo be same with your endpoint.\n",
+ Help: "Region to connect to. - the location where your bucket will be created and your data stored. Need to be same with your endpoint.\n",
Provider: "HuaweiOBS",
Examples: []fs.OptionExample{{
Value: "af-south-1",
@@ -466,50 +441,6 @@ func init() {
Value: "ru-northwest-2",
Help: "RU-Moscow2",
}},
- }, {
- Name: "region",
- Help: "Region to connect to.",
- Provider: "Cloudflare",
- Examples: []fs.OptionExample{{
- Value: "auto",
- Help: "R2 buckets are automatically distributed across Cloudflare's data centers for low latency.",
- }},
- }, {
- // References:
- // https://developer.qiniu.com/kodo/4088/s3-access-domainname
- Name: "region",
- Help: "Region to connect to.",
- Provider: "Qiniu",
- Examples: []fs.OptionExample{{
- Value: "cn-east-1",
- Help: "The default endpoint - a good choice if you are unsure.\nEast China Region 1.\nNeeds location constraint cn-east-1.",
- }, {
- Value: "cn-east-2",
- Help: "East China Region 2.\nNeeds location constraint cn-east-2.",
- }, {
- Value: "cn-north-1",
- Help: "North China Region 1.\nNeeds location constraint cn-north-1.",
- }, {
- Value: "cn-south-1",
- Help: "South China Region 1.\nNeeds location constraint cn-south-1.",
- }, {
- Value: "us-north-1",
- Help: "North America Region.\nNeeds location constraint us-north-1.",
- }, {
- Value: "ap-southeast-1",
- Help: "Southeast Asia Region 1.\nNeeds location constraint ap-southeast-1.",
- }, {
- Value: "ap-northeast-1",
- Help: "Northeast Asia Region 1.\nNeeds location constraint ap-northeast-1.",
- }},
- }, {
- Name: "region",
- Help: "Region where you can connect with.\n",
- Provider: "Zata",
- Examples: []fs.OptionExample{{
- Value: "us-east-1",
- Help: "Indore, Madhya Pradesh, India",
- }},
}, {
Name: "region",
Help: "Region where your bucket will be created and your data stored.\n",
@@ -625,6 +556,133 @@ func init() {
Value: "sa-east-1",
Help: "South America (São Paulo)",
}},
+ }, {
+ // References:
+ // https://developer.qiniu.com/kodo/4088/s3-access-domainname
+ Name: "region",
+ Help: "Region to connect to.",
+ Provider: "Qiniu",
+ Examples: []fs.OptionExample{{
+ Value: "cn-east-1",
+ Help: "The default endpoint - a good choice if you are unsure.\nEast China Region 1.\nNeeds location constraint cn-east-1.",
+ }, {
+ Value: "cn-east-2",
+ Help: "East China Region 2.\nNeeds location constraint cn-east-2.",
+ }, {
+ Value: "cn-north-1",
+ Help: "North China Region 1.\nNeeds location constraint cn-north-1.",
+ }, {
+ Value: "cn-south-1",
+ Help: "South China Region 1.\nNeeds location constraint cn-south-1.",
+ }, {
+ Value: "us-north-1",
+ Help: "North America Region.\nNeeds location constraint us-north-1.",
+ }, {
+ Value: "ap-southeast-1",
+ Help: "Southeast Asia Region 1.\nNeeds location constraint ap-southeast-1.",
+ }, {
+ Value: "ap-northeast-1",
+ Help: "Northeast Asia Region 1.\nNeeds location constraint ap-northeast-1.",
+ }},
+ }, {
+ Name: "region",
+ Help: "Region where your bucket will be created and your data stored.\n",
+ Provider: "Rabata",
+ Examples: []fs.OptionExample{{
+ Value: "us-east-1",
+ Help: "US East (N. Virginia)",
+ }, {
+ Value: "eu-west-1",
+ Help: "EU (Ireland)",
+ }, {
+ Value: "eu-west-2",
+ Help: "EU (London)",
+ }},
+ }, {
+ Name: "region",
+ Help: "region - the location where your bucket will be created and your data stored.\n",
+ Provider: "RackCorp",
+ Examples: []fs.OptionExample{{
+ Value: "global",
+ Help: "Global CDN (All locations) Region",
+ }, {
+ Value: "au",
+ Help: "Australia (All states)",
+ }, {
+ Value: "au-nsw",
+ Help: "NSW (Australia) Region",
+ }, {
+ Value: "au-qld",
+ Help: "QLD (Australia) Region",
+ }, {
+ Value: "au-vic",
+ Help: "VIC (Australia) Region",
+ }, {
+ Value: "au-wa",
+ Help: "Perth (Australia) Region",
+ }, {
+ Value: "ph",
+ Help: "Manila (Philippines) Region",
+ }, {
+ Value: "th",
+ Help: "Bangkok (Thailand) Region",
+ }, {
+ Value: "hk",
+ Help: "HK (Hong Kong) Region",
+ }, {
+ Value: "mn",
+ Help: "Ulaanbaatar (Mongolia) Region",
+ }, {
+ Value: "kg",
+ Help: "Bishkek (Kyrgyzstan) Region",
+ }, {
+ Value: "id",
+ Help: "Jakarta (Indonesia) Region",
+ }, {
+ Value: "jp",
+ Help: "Tokyo (Japan) Region",
+ }, {
+ Value: "sg",
+ Help: "SG (Singapore) Region",
+ }, {
+ Value: "de",
+ Help: "Frankfurt (Germany) Region",
+ }, {
+ Value: "us",
+ Help: "USA (AnyCast) Region",
+ }, {
+ Value: "us-east-1",
+ Help: "New York (USA) Region",
+ }, {
+ Value: "us-west-1",
+ Help: "Freemont (USA) Region",
+ }, {
+ Value: "nz",
+ Help: "Auckland (New Zealand) Region",
+ }},
+ }, {
+ Name: "region",
+ Help: "Region to connect to.",
+ Provider: "Scaleway",
+ Examples: []fs.OptionExample{{
+ Value: "nl-ams",
+ Help: "Amsterdam, The Netherlands",
+ }, {
+ Value: "fr-par",
+ Help: "Paris, France",
+ }, {
+ Value: "pl-waw",
+ Help: "Warsaw, Poland",
+ }},
+ }, {
+ // See endpoints for object storage regions: https://docs.selectel.ru/en/cloud/object-storage/manage/domains/#s3-api-domains
+ Name: "region",
+ Help: "Region where your data stored.\n",
+ Provider: "Selectel",
+ Examples: []fs.OptionExample{{
+ Value: "ru-1",
+ Help: "St. Petersburg",
+ }},
}, {
Name: "region",
Help: "Region where your data stored.\n",
@@ -646,18 +704,17 @@ func init() {
Help: "Asia (Taiwan)",
}},
}, {
- // See endpoints for object storage regions: https://docs.selectel.ru/en/cloud/object-storage/manage/domains/#s3-api-domains
Name: "region",
- Help: "Region where your data stored.\n",
- Provider: "Selectel",
+ Help: "Region where you can connect with.\n",
+ Provider: "Zata",
Examples: []fs.OptionExample{{
- Value: "ru-1",
- Help: "St. Petersburg",
+ Value: "us-east-1",
+ Help: "Indore, Madhya Pradesh, India",
}},
}, {
Name: "region",
Help: "Region to connect to.\n\nLeave blank if you are using an S3 clone and you don't have a region.",
- Provider: "!AWS,Alibaba,ArvanCloud,ChinaMobile,Cloudflare,FlashBlade,Intercolo,IONOS,Petabox,Liara,Linode,Magalu,OVHcloud,Qiniu,RackCorp,Scaleway,Selectel,SpectraLogic,Storj,Synology,TencentCOS,HuaweiOBS,IDrive,Mega,Zata",
+ Provider: "!AWS,Alibaba,ArvanCloud,ChinaMobile,Cloudflare,FlashBlade,FileLu,Hetzner,HuaweiOBS,IDrive,Intercolo,IONOS,Liara,Linode,Magalu,Mega,OVHcloud,Petabox,Qiniu,Rabata,RackCorp,Scaleway,Selectel,SpectraLogic,Storj,Synology,TencentCOS,Zata",
Examples: []fs.OptionExample{{
Value: "",
Help: "Use this if unsure.\nWill use v4 signatures and an empty region.",
@@ -670,13 +727,106 @@ func init() {
Help: "Endpoint for S3 API.\n\nLeave blank if using AWS to use the default endpoint for the region.",
Provider: "AWS",
}, {
- // ChinaMobile endpoints: https://ecloud.10086.cn/op-help-center/doc/article/24534
+ // oss endpoints: https://help.aliyun.com/document_detail/31837.html
Name: "endpoint",
- Help: "Endpoint for China Mobile Ecloud Elastic Object Storage (EOS) API.",
- Provider: "ChinaMobile",
+ Help: "Endpoint for OSS API.",
+ Provider: "Alibaba",
Examples: []fs.OptionExample{{
- Value: "eos-wuxi-1.cmecloud.cn",
- Help: "The default endpoint - a good choice if you are unsure.\nEast China (Suzhou)",
+ Value: "oss-accelerate.aliyuncs.com",
+ Help: "Global Accelerate",
+ }, {
+ Value: "oss-accelerate-overseas.aliyuncs.com",
+ Help: "Global Accelerate (outside mainland China)",
+ }, {
+ Value: "oss-cn-hangzhou.aliyuncs.com",
+ Help: "East China 1 (Hangzhou)",
+ }, {
+ Value: "oss-cn-shanghai.aliyuncs.com",
+ Help: "East China 2 (Shanghai)",
+ }, {
+ Value: "oss-cn-qingdao.aliyuncs.com",
+ Help: "North China 1 (Qingdao)",
+ }, {
+ Value: "oss-cn-beijing.aliyuncs.com",
+ Help: "North China 2 (Beijing)",
+ }, {
+ Value: "oss-cn-zhangjiakou.aliyuncs.com",
+ Help: "North China 3 (Zhangjiakou)",
+ }, {
+ Value: "oss-cn-huhehaote.aliyuncs.com",
+ Help: "North China 5 (Hohhot)",
+ }, {
+ Value: "oss-cn-wulanchabu.aliyuncs.com",
+ Help: "North China 6 (Ulanqab)",
+ }, {
+ Value: "oss-cn-shenzhen.aliyuncs.com",
+ Help: "South China 1 (Shenzhen)",
+ }, {
+ Value: "oss-cn-heyuan.aliyuncs.com",
+ Help: "South China 2 (Heyuan)",
+ }, {
+ Value: "oss-cn-guangzhou.aliyuncs.com",
+ Help: "South China 3 (Guangzhou)",
+ }, {
+ Value: "oss-cn-chengdu.aliyuncs.com",
+ Help: "West China 1 (Chengdu)",
+ }, {
+ Value: "oss-cn-hongkong.aliyuncs.com",
+ Help: "Hong Kong (Hong Kong)",
+ }, {
+ Value: "oss-us-west-1.aliyuncs.com",
+ Help: "US West 1 (Silicon Valley)",
+ }, {
+ Value: "oss-us-east-1.aliyuncs.com",
+ Help: "US East 1 (Virginia)",
+ }, {
+ Value: "oss-ap-southeast-1.aliyuncs.com",
+ Help: "Southeast Asia Southeast 1 (Singapore)",
+ }, {
+ Value: "oss-ap-southeast-2.aliyuncs.com",
+ Help: "Asia Pacific Southeast 2 (Sydney)",
+ }, {
+ Value: "oss-ap-southeast-3.aliyuncs.com",
+ Help: "Southeast Asia Southeast 3 (Kuala Lumpur)",
+ }, {
+ Value: "oss-ap-southeast-5.aliyuncs.com",
+ Help: "Asia Pacific Southeast 5 (Jakarta)",
+ }, {
+ Value: "oss-ap-northeast-1.aliyuncs.com",
+ Help: "Asia Pacific Northeast 1 (Japan)",
+ }, {
+ Value: "oss-ap-south-1.aliyuncs.com",
+ Help: "Asia Pacific South 1 (Mumbai)",
+ }, {
+ Value: "oss-eu-central-1.aliyuncs.com",
+ Help: "Central Europe 1 (Frankfurt)",
+ }, {
+ Value: "oss-eu-west-1.aliyuncs.com",
+ Help: "West Europe (London)",
+ }, {
+ Value: "oss-me-east-1.aliyuncs.com",
+ Help: "Middle East 1 (Dubai)",
+ }},
+ }, {
+ // ArvanCloud endpoints: https://www.arvancloud.ir/en/products/cloud-storage
+ Name: "endpoint",
+ Help: "Endpoint for Arvan Cloud Object Storage (AOS) API.",
+ Provider: "ArvanCloud",
+ Examples: []fs.OptionExample{{
+ Value: "s3.ir-thr-at1.arvanstorage.ir",
+ Help: "The default endpoint - a good choice if you are unsure.\nTehran Iran (Simin)",
+ }, {
+ Value: "s3.ir-tbz-sh1.arvanstorage.ir",
+ Help: "Tabriz Iran (Shahriar)",
+ }},
+ }, {
+ // ChinaMobile endpoints: https://ecloud.10086.cn/op-help-center/doc/article/24534
+ Name: "endpoint",
+ Help: "Endpoint for China Mobile Ecloud Elastic Object Storage (EOS) API.",
+ Provider: "ChinaMobile",
+ Examples: []fs.OptionExample{{
+ Value: "eos-wuxi-1.cmecloud.cn",
+ Help: "The default endpoint - a good choice if you are unsure.\nEast China (Suzhou)",
}, {
Value: "eos-jinan-1.cmecloud.cn",
Help: "East China (Jinan)",
@@ -766,16 +916,85 @@ func init() {
Help: "Anhui China (Huainan)",
}},
}, {
- // ArvanCloud endpoints: https://www.arvancloud.ir/en/products/cloud-storage
Name: "endpoint",
- Help: "Endpoint for Arvan Cloud Object Storage (AOS) API.",
- Provider: "ArvanCloud",
+ Help: "Endpoint for FileLu S5 Object Storage.\nRequired when using FileLu S5.",
+ Provider: "FileLu",
Examples: []fs.OptionExample{{
- Value: "s3.ir-thr-at1.arvanstorage.ir",
- Help: "The default endpoint - a good choice if you are unsure.\nTehran Iran (Simin)",
+ Value: "s5lu.com",
+ Help: "Global FileLu S5 endpoint",
+ }},
+ }, {
+ Name: "endpoint",
+ Help: "Endpoint for Google Cloud Storage.",
+ Provider: "GCS",
+ Examples: []fs.OptionExample{{
+ Value: "https://storage.googleapis.com",
+ Help: "Google Cloud Storage endpoint",
+ }},
+ }, {
+ Name: "endpoint",
+ Help: "Endpoint for Hetzner Object Storage",
+ Provider: "Hetzner",
+ Examples: []fs.OptionExample{{
+ Value: "hel1.your-objectstorage.com",
+ Help: "Helsinki",
}, {
- Value: "s3.ir-tbz-sh1.arvanstorage.ir",
- Help: "Tabriz Iran (Shahriar)",
+ Value: "fsn1.your-objectstorage.com",
+ Help: "Falkenstein",
+ }, {
+ Value: "nbg1.your-objectstorage.com",
+ Help: "Nuremberg",
+ }},
+ }, {
+ // obs endpoints: https://developer.huaweicloud.com/intl/en-us/endpoint?OBS
+ Name: "endpoint",
+ Help: "Endpoint for OBS API.",
+ Provider: "HuaweiOBS",
+ Examples: []fs.OptionExample{{
+ Value: "obs.af-south-1.myhuaweicloud.com",
+ Help: "AF-Johannesburg",
+ }, {
+ Value: "obs.ap-southeast-2.myhuaweicloud.com",
+ Help: "AP-Bangkok",
+ }, {
+ Value: "obs.ap-southeast-3.myhuaweicloud.com",
+ Help: "AP-Singapore",
+ }, {
+ Value: "obs.cn-east-3.myhuaweicloud.com",
+ Help: "CN East-Shanghai1",
+ }, {
+ Value: "obs.cn-east-2.myhuaweicloud.com",
+ Help: "CN East-Shanghai2",
+ }, {
+ Value: "obs.cn-north-1.myhuaweicloud.com",
+ Help: "CN North-Beijing1",
+ }, {
+ Value: "obs.cn-north-4.myhuaweicloud.com",
+ Help: "CN North-Beijing4",
+ }, {
+ Value: "obs.cn-south-1.myhuaweicloud.com",
+ Help: "CN South-Guangzhou",
+ }, {
+ Value: "obs.ap-southeast-1.myhuaweicloud.com",
+ Help: "CN-Hong Kong",
+ }, {
+ Value: "obs.sa-argentina-1.myhuaweicloud.com",
+ Help: "LA-Buenos Aires1",
+ }, {
+ Value: "obs.sa-peru-1.myhuaweicloud.com",
+ Help: "LA-Lima1",
+ }, {
+ Value: "obs.na-mexico-1.myhuaweicloud.com",
+ Help: "LA-Mexico City1",
+ }, {
+ Value: "obs.sa-chile-1.myhuaweicloud.com",
+ Help: "LA-Santiago2",
+ }, {
+ Value: "obs.sa-brazil-1.myhuaweicloud.com",
+ Help: "LA-Sao Paulo1",
+ }, {
+ Value: "obs.ru-northwest-2.myhuaweicloud.com",
+ Help: "RU-Moscow2",
}},
}, {
Name: "endpoint",
@@ -861,7 +1080,7 @@ func init() {
Help: "APAC Cross Regional Tokyo Endpoint",
}, {
Value: "s3.hkg.ap.cloud-object-storage.appdomain.cloud",
- Help: "APAC Cross Regional HongKong Endpoint",
+ Help: "APAC Cross Regional Hong Kong Endpoint",
}, {
Value: "s3.seo.ap.cloud-object-storage.appdomain.cloud",
Help: "APAC Cross Regional Seoul Endpoint",
@@ -873,7 +1092,7 @@ func init() {
Help: "APAC Cross Regional Tokyo Private Endpoint",
}, {
Value: "s3.private.hkg.ap.cloud-object-storage.appdomain.cloud",
- Help: "APAC Cross Regional HongKong Private Endpoint",
+ Help: "APAC Cross Regional Hong Kong Private Endpoint",
}, {
Value: "s3.private.seo.ap.cloud-object-storage.appdomain.cloud",
Help: "APAC Cross Regional Seoul Private Endpoint",
@@ -990,30 +1209,6 @@ func init() {
Value: "s3-eu-south-2.ionoscloud.com",
Help: "Logrono, Spain",
}},
- }, {
- Name: "endpoint",
- Help: "Endpoint for Petabox S3 Object Storage.\n\nSpecify the endpoint from the same region.",
- Provider: "Petabox",
- Required: true,
- Examples: []fs.OptionExample{{
- Value: "s3.petabox.io",
- Help: "US East (N. Virginia)",
- }, {
- Value: "s3.us-east-1.petabox.io",
- Help: "US East (N. Virginia)",
- }, {
- Value: "s3.eu-central-1.petabox.io",
- Help: "Europe (Frankfurt)",
- }, {
- Value: "s3.ap-southeast-1.petabox.io",
- Help: "Asia Pacific (Singapore)",
- }, {
- Value: "s3.me-south-1.petabox.io",
- Help: "Middle East (Bahrain)",
- }, {
- Value: "s3.sa-east-1.petabox.io",
- Help: "South America (São Paulo)",
- }},
}, {
// Leviia endpoints: https://www.leviia.com/object-storage/
Name: "endpoint",
@@ -1120,138 +1315,6 @@ func init() {
Help: "Fortaleza, CE (BR), br-ne1",
},
},
- }, {
- // oss endpoints: https://help.aliyun.com/document_detail/31837.html
- Name: "endpoint",
- Help: "Endpoint for OSS API.",
- Provider: "Alibaba",
- Examples: []fs.OptionExample{{
- Value: "oss-accelerate.aliyuncs.com",
- Help: "Global Accelerate",
- }, {
- Value: "oss-accelerate-overseas.aliyuncs.com",
- Help: "Global Accelerate (outside mainland China)",
- }, {
- Value: "oss-cn-hangzhou.aliyuncs.com",
- Help: "East China 1 (Hangzhou)",
- }, {
- Value: "oss-cn-shanghai.aliyuncs.com",
- Help: "East China 2 (Shanghai)",
- }, {
- Value: "oss-cn-qingdao.aliyuncs.com",
- Help: "North China 1 (Qingdao)",
- }, {
- Value: "oss-cn-beijing.aliyuncs.com",
- Help: "North China 2 (Beijing)",
- }, {
- Value: "oss-cn-zhangjiakou.aliyuncs.com",
- Help: "North China 3 (Zhangjiakou)",
- }, {
- Value: "oss-cn-huhehaote.aliyuncs.com",
- Help: "North China 5 (Hohhot)",
- }, {
- Value: "oss-cn-wulanchabu.aliyuncs.com",
- Help: "North China 6 (Ulanqab)",
- }, {
- Value: "oss-cn-shenzhen.aliyuncs.com",
- Help: "South China 1 (Shenzhen)",
- }, {
- Value: "oss-cn-heyuan.aliyuncs.com",
- Help: "South China 2 (Heyuan)",
- }, {
- Value: "oss-cn-guangzhou.aliyuncs.com",
- Help: "South China 3 (Guangzhou)",
- }, {
- Value: "oss-cn-chengdu.aliyuncs.com",
- Help: "West China 1 (Chengdu)",
- }, {
- Value: "oss-cn-hongkong.aliyuncs.com",
- Help: "Hong Kong (Hong Kong)",
- }, {
- Value: "oss-us-west-1.aliyuncs.com",
- Help: "US West 1 (Silicon Valley)",
- }, {
- Value: "oss-us-east-1.aliyuncs.com",
- Help: "US East 1 (Virginia)",
- }, {
- Value: "oss-ap-southeast-1.aliyuncs.com",
- Help: "Southeast Asia Southeast 1 (Singapore)",
- }, {
- Value: "oss-ap-southeast-2.aliyuncs.com",
- Help: "Asia Pacific Southeast 2 (Sydney)",
- }, {
- Value: "oss-ap-southeast-3.aliyuncs.com",
- Help: "Southeast Asia Southeast 3 (Kuala Lumpur)",
- }, {
- Value: "oss-ap-southeast-5.aliyuncs.com",
- Help: "Asia Pacific Southeast 5 (Jakarta)",
- }, {
- Value: "oss-ap-northeast-1.aliyuncs.com",
- Help: "Asia Pacific Northeast 1 (Japan)",
- }, {
- Value: "oss-ap-south-1.aliyuncs.com",
- Help: "Asia Pacific South 1 (Mumbai)",
- }, {
- Value: "oss-eu-central-1.aliyuncs.com",
- Help: "Central Europe 1 (Frankfurt)",
- }, {
- Value: "oss-eu-west-1.aliyuncs.com",
- Help: "West Europe (London)",
- }, {
- Value: "oss-me-east-1.aliyuncs.com",
- Help: "Middle East 1 (Dubai)",
- }},
- }, {
- // obs endpoints: https://developer.huaweicloud.com/intl/en-us/endpoint?OBS
- Name: "endpoint",
- Help: "Endpoint for OBS API.",
- Provider: "HuaweiOBS",
- Examples: []fs.OptionExample{{
- Value: "obs.af-south-1.myhuaweicloud.com",
- Help: "AF-Johannesburg",
- }, {
- Value: "obs.ap-southeast-2.myhuaweicloud.com",
- Help: "AP-Bangkok",
- }, {
- Value: "obs.ap-southeast-3.myhuaweicloud.com",
- Help: "AP-Singapore",
- }, {
- Value: "obs.cn-east-3.myhuaweicloud.com",
- Help: "CN East-Shanghai1",
- }, {
- Value: "obs.cn-east-2.myhuaweicloud.com",
- Help: "CN East-Shanghai2",
- }, {
- Value: "obs.cn-north-1.myhuaweicloud.com",
- Help: "CN North-Beijing1",
- }, {
- Value: "obs.cn-north-4.myhuaweicloud.com",
- Help: "CN North-Beijing4",
- }, {
- Value: "obs.cn-south-1.myhuaweicloud.com",
- Help: "CN South-Guangzhou",
- }, {
- Value: "obs.ap-southeast-1.myhuaweicloud.com",
- Help: "CN-Hong Kong",
- }, {
- Value: "obs.sa-argentina-1.myhuaweicloud.com",
- Help: "LA-Buenos Aires1",
- }, {
- Value: "obs.sa-peru-1.myhuaweicloud.com",
- Help: "LA-Lima1",
- }, {
- Value: "obs.na-mexico-1.myhuaweicloud.com",
- Help: "LA-Mexico City1",
- }, {
- Value: "obs.sa-chile-1.myhuaweicloud.com",
- Help: "LA-Santiago2",
- }, {
- Value: "obs.sa-brazil-1.myhuaweicloud.com",
- Help: "LA-Sao Paulo1",
- }, {
- Value: "obs.ru-northwest-2.myhuaweicloud.com",
- Help: "RU-Moscow2",
- }},
}, {
Name: "endpoint",
Help: "Endpoint for OVHcloud Object Storage.",
@@ -1317,6 +1380,143 @@ func init() {
Help: "OVHcloud Roubaix, France (Cold Archive)",
Provider: "OVHcloud",
}},
+ }, {
+ Name: "endpoint",
+ Help: "Endpoint for Petabox S3 Object Storage.\n\nSpecify the endpoint from the same region.",
+ Provider: "Petabox",
+ Required: true,
+ Examples: []fs.OptionExample{{
+ Value: "s3.petabox.io",
+ Help: "US East (N. Virginia)",
+ }, {
+ Value: "s3.us-east-1.petabox.io",
+ Help: "US East (N. Virginia)",
+ }, {
+ Value: "s3.eu-central-1.petabox.io",
+ Help: "Europe (Frankfurt)",
+ }, {
+ Value: "s3.ap-southeast-1.petabox.io",
+ Help: "Asia Pacific (Singapore)",
+ }, {
+ Value: "s3.me-south-1.petabox.io",
+ Help: "Middle East (Bahrain)",
+ }, {
+ Value: "s3.sa-east-1.petabox.io",
+ Help: "South America (São Paulo)",
+ }},
+ }, {
+ // Qiniu endpoints: https://developer.qiniu.com/kodo/4088/s3-access-domainname
+ Name: "endpoint",
+ Help: "Endpoint for Qiniu Object Storage.",
+ Provider: "Qiniu",
+ Examples: []fs.OptionExample{{
+ Value: "s3-cn-east-1.qiniucs.com",
+ Help: "East China Endpoint 1",
+ }, {
+ Value: "s3-cn-east-2.qiniucs.com",
+ Help: "East China Endpoint 2",
+ }, {
+ Value: "s3-cn-north-1.qiniucs.com",
+ Help: "North China Endpoint 1",
+ }, {
+ Value: "s3-cn-south-1.qiniucs.com",
+ Help: "South China Endpoint 1",
+ }, {
+ Value: "s3-us-north-1.qiniucs.com",
+ Help: "North America Endpoint 1",
+ }, {
+ Value: "s3-ap-southeast-1.qiniucs.com",
+ Help: "Southeast Asia Endpoint 1",
+ }, {
+ Value: "s3-ap-northeast-1.qiniucs.com",
+ Help: "Northeast Asia Endpoint 1",
+ }},
+ }, {
+ Name: "endpoint",
+ Help: "Endpoint for Rabata Object Storage.",
+ Provider: "Rabata",
+ Examples: []fs.OptionExample{{
+ Value: "s3.us-east-1.rabata.io",
+ Help: "US East (N. Virginia)",
+ }, {
+ Value: "s3.eu-west-1.rabata.io",
+ Help: "EU West (Ireland)",
+ }, {
+ Value: "s3.eu-west-2.rabata.io",
+ Help: "EU West (London)",
+ }},
+ }, {
+ // RackCorp endpoints: https://www.rackcorp.com/storage/s3storage
+ Name: "endpoint",
+ Help: "Endpoint for RackCorp Object Storage.",
+ Provider: "RackCorp",
+ Examples: []fs.OptionExample{{
+ Value: "s3.rackcorp.com",
+ Help: "Global (AnyCast) Endpoint",
+ }, {
+ Value: "au.s3.rackcorp.com",
+ Help: "Australia (Anycast) Endpoint",
+ }, {
+ Value: "au-nsw.s3.rackcorp.com",
+ Help: "Sydney (Australia) Endpoint",
+ }, {
+ Value: "au-qld.s3.rackcorp.com",
+ Help: "Brisbane (Australia) Endpoint",
+ }, {
+ Value: "au-vic.s3.rackcorp.com",
+ Help: "Melbourne (Australia) Endpoint",
+ }, {
+ Value: "au-wa.s3.rackcorp.com",
+ Help: "Perth (Australia) Endpoint",
+ }, {
+ Value: "ph.s3.rackcorp.com",
+ Help: "Manila (Philippines) Endpoint",
+ }, {
+ Value: "th.s3.rackcorp.com",
+ Help: "Bangkok (Thailand) Endpoint",
+ }, {
+ Value: "hk.s3.rackcorp.com",
+ Help: "HK (Hong Kong) Endpoint",
+ }, {
+ Value: "mn.s3.rackcorp.com",
+ Help: "Ulaanbaatar (Mongolia) Endpoint",
+ }, {
+ Value: "kg.s3.rackcorp.com",
+ Help: "Bishkek (Kyrgyzstan) Endpoint",
+ }, {
+ Value: "id.s3.rackcorp.com",
+ Help: "Jakarta (Indonesia) Endpoint",
+ }, {
+ Value: "jp.s3.rackcorp.com",
+ Help: "Tokyo (Japan) Endpoint",
+ }, {
+ Value: "sg.s3.rackcorp.com",
+ Help: "SG (Singapore) Endpoint",
+ }, {
+ Value: "de.s3.rackcorp.com",
+ Help: "Frankfurt (Germany) Endpoint",
+ }, {
+ Value: "us.s3.rackcorp.com",
+ Help: "USA (AnyCast) Endpoint",
+ }, {
+ Value: "us-east-1.s3.rackcorp.com",
+ Help: "New York (USA) Endpoint",
+ }, {
+ Value: "us-west-1.s3.rackcorp.com",
+ Help: "Freemont (USA) Endpoint",
+ }, {
+ Value: "nz.s3.rackcorp.com",
+ Help: "Auckland (New Zealand) Endpoint",
+ }},
+ }, {
+ // Selectel endpoints: https://docs.selectel.ru/en/cloud/object-storage/manage/domains/#s3-api-domains
+ Name: "endpoint",
+ Help: "Endpoint for Selectel Object Storage.",
+ Provider: "Selectel",
+ Examples: []fs.OptionExample{{
+ Value: "s3.ru-1.storage.selcloud.ru",
+ Help: "Saint Petersburg",
+ }},
}, {
Name: "endpoint",
Help: "Endpoint for Scaleway Object Storage.",
@@ -1337,21 +1537,13 @@ func init() {
Provider: "StackPath",
Examples: []fs.OptionExample{{
Value: "s3.us-east-2.stackpathstorage.com",
- Help: "US East Endpoint",
- }, {
- Value: "s3.us-west-1.stackpathstorage.com",
- Help: "US West Endpoint",
- }, {
- Value: "s3.eu-central-1.stackpathstorage.com",
- Help: "EU Endpoint",
- }},
- }, {
- Name: "endpoint",
- Help: "Endpoint for Google Cloud Storage.",
- Provider: "GCS",
- Examples: []fs.OptionExample{{
- Value: "https://storage.googleapis.com",
- Help: "Google Cloud Storage endpoint",
+ Help: "US East Endpoint",
+ }, {
+ Value: "s3.us-west-1.stackpathstorage.com",
+ Help: "US West Endpoint",
+ }, {
+ Value: "s3.eu-central-1.stackpathstorage.com",
+ Help: "EU Endpoint",
}},
}, {
Name: "endpoint",
@@ -1444,96 +1636,6 @@ func init() {
Value: "cos.accelerate.myqcloud.com",
Help: "Use Tencent COS Accelerate Endpoint",
}},
- }, {
- // RackCorp endpoints: https://www.rackcorp.com/storage/s3storage
- Name: "endpoint",
- Help: "Endpoint for RackCorp Object Storage.",
- Provider: "RackCorp",
- Examples: []fs.OptionExample{{
- Value: "s3.rackcorp.com",
- Help: "Global (AnyCast) Endpoint",
- }, {
- Value: "au.s3.rackcorp.com",
- Help: "Australia (Anycast) Endpoint",
- }, {
- Value: "au-nsw.s3.rackcorp.com",
- Help: "Sydney (Australia) Endpoint",
- }, {
- Value: "au-qld.s3.rackcorp.com",
- Help: "Brisbane (Australia) Endpoint",
- }, {
- Value: "au-vic.s3.rackcorp.com",
- Help: "Melbourne (Australia) Endpoint",
- }, {
- Value: "au-wa.s3.rackcorp.com",
- Help: "Perth (Australia) Endpoint",
- }, {
- Value: "ph.s3.rackcorp.com",
- Help: "Manila (Philippines) Endpoint",
- }, {
- Value: "th.s3.rackcorp.com",
- Help: "Bangkok (Thailand) Endpoint",
- }, {
- Value: "hk.s3.rackcorp.com",
- Help: "HK (Hong Kong) Endpoint",
- }, {
- Value: "mn.s3.rackcorp.com",
- Help: "Ulaanbaatar (Mongolia) Endpoint",
- }, {
- Value: "kg.s3.rackcorp.com",
- Help: "Bishkek (Kyrgyzstan) Endpoint",
- }, {
- Value: "id.s3.rackcorp.com",
- Help: "Jakarta (Indonesia) Endpoint",
- }, {
- Value: "jp.s3.rackcorp.com",
- Help: "Tokyo (Japan) Endpoint",
- }, {
- Value: "sg.s3.rackcorp.com",
- Help: "SG (Singapore) Endpoint",
- }, {
- Value: "de.s3.rackcorp.com",
- Help: "Frankfurt (Germany) Endpoint",
- }, {
- Value: "us.s3.rackcorp.com",
- Help: "USA (AnyCast) Endpoint",
- }, {
- Value: "us-east-1.s3.rackcorp.com",
- Help: "New York (USA) Endpoint",
- }, {
- Value: "us-west-1.s3.rackcorp.com",
- Help: "Freemont (USA) Endpoint",
- }, {
- Value: "nz.s3.rackcorp.com",
- Help: "Auckland (New Zealand) Endpoint",
- }},
- }, {
- // Qiniu endpoints: https://developer.qiniu.com/kodo/4088/s3-access-domainname
- Name: "endpoint",
- Help: "Endpoint for Qiniu Object Storage.",
- Provider: "Qiniu",
- Examples: []fs.OptionExample{{
- Value: "s3-cn-east-1.qiniucs.com",
- Help: "East China Endpoint 1",
- }, {
- Value: "s3-cn-east-2.qiniucs.com",
- Help: "East China Endpoint 2",
- }, {
- Value: "s3-cn-north-1.qiniucs.com",
- Help: "North China Endpoint 1",
- }, {
- Value: "s3-cn-south-1.qiniucs.com",
- Help: "South China Endpoint 1",
- }, {
- Value: "s3-us-north-1.qiniucs.com",
- Help: "North America Endpoint 1",
- }, {
- Value: "s3-ap-southeast-1.qiniucs.com",
- Help: "Southeast Asia Endpoint 1",
- }, {
- Value: "s3-ap-northeast-1.qiniucs.com",
- Help: "Northeast Asia Endpoint 1",
- }},
}, {
Name: "endpoint",
Help: "Endpoint for Zata Object Storage.",
@@ -1542,19 +1644,10 @@ func init() {
Value: "idr01.zata.ai",
Help: "South Asia Endpoint",
}},
- }, {
- // Selectel endpoints: https://docs.selectel.ru/en/cloud/object-storage/manage/domains/#s3-api-domains
- Name: "endpoint",
- Help: "Endpoint for Selectel Object Storage.",
- Provider: "Selectel",
- Examples: []fs.OptionExample{{
- Value: "s3.ru-1.storage.selcloud.ru",
- Help: "Saint Petersburg",
- }},
}, {
Name: "endpoint",
Help: "Endpoint for S3 API.\n\nRequired when using an S3 clone.",
- Provider: "!AWS,ArvanCloud,IBMCOS,IDrive,Intercolo,IONOS,TencentCOS,HuaweiOBS,Alibaba,ChinaMobile,GCS,Liara,Linode,LyveCloud,Magalu,OVHcloud,Scaleway,Selectel,StackPath,Storj,Synology,RackCorp,Qiniu,Petabox,Zata",
+ Provider: "!AWS,Alibaba,ArvanCloud,ChinaMobile,GCS,Hetzner,HuaweiOBS,IBMCOS,IDrive,Intercolo,IONOS,Liara,Linode,LyveCloud,Magalu,OVHcloud,Petabox,Qiniu,Rabata,RackCorp,Scaleway,Selectel,StackPath,Storj,Synology,TencentCOS,Zata",
Examples: []fs.OptionExample{{
Value: "objects-us-east-1.dream.io",
Help: "Dream Objects endpoint",
@@ -1791,6 +1884,17 @@ func init() {
Value: "us-gov-west-1",
Help: "AWS GovCloud (US) Region",
}},
+ }, {
+ Name: "location_constraint",
+ Help: "Location constraint - must match endpoint.\n\nUsed when creating buckets only.",
+ Provider: "ArvanCloud",
+ Examples: []fs.OptionExample{{
+ Value: "ir-thr-at1",
+ Help: "Tehran Iran (Simin)",
+ }, {
+ Value: "ir-tbz-sh1",
+ Help: "Tabriz Iran (Shahriar)",
+ }},
}, {
Name: "location_constraint",
Help: "Location constraint - must match endpoint.\n\nUsed when creating buckets only.",
@@ -1845,7 +1949,7 @@ func init() {
Help: "Southwest China (Guiyang)",
}, {
Value: "xian1",
- Help: "Nouthwest China (Xian)",
+ Help: "Northwest China (Xian)",
}, {
Value: "yunnan",
Help: "Yunnan China (Kunming)",
@@ -1886,17 +1990,6 @@ func init() {
Value: "anhui1",
Help: "Anhui China (Huainan)",
}},
- }, {
- Name: "location_constraint",
- Help: "Location constraint - must match endpoint.\n\nUsed when creating buckets only.",
- Provider: "ArvanCloud",
- Examples: []fs.OptionExample{{
- Value: "ir-thr-at1",
- Help: "Tehran Iran (Simin)",
- }, {
- Value: "ir-tbz-sh1",
- Help: "Tabriz Iran (Shahriar)",
- }},
}, {
Name: "location_constraint",
Help: "Location constraint - must match endpoint when using IBM Cloud Public.\n\nFor on-prem COS, do not make a selection from this list, hit enter.",
@@ -1998,6 +2091,46 @@ func init() {
Value: "tor01-flex",
Help: "Toronto Flex",
}},
+ }, {
+ Name: "location_constraint",
+ Help: "Location constraint - must be set to match the Region.\n\nUsed when creating buckets only.",
+ Provider: "Qiniu",
+ Examples: []fs.OptionExample{{
+ Value: "cn-east-1",
+ Help: "East China Region 1",
+ }, {
+ Value: "cn-east-2",
+ Help: "East China Region 2",
+ }, {
+ Value: "cn-north-1",
+ Help: "North China Region 1",
+ }, {
+ Value: "cn-south-1",
+ Help: "South China Region 1",
+ }, {
+ Value: "us-north-1",
+ Help: "North America Region 1",
+ }, {
+ Value: "ap-southeast-1",
+ Help: "Southeast Asia Region 1",
+ }, {
+ Value: "ap-northeast-1",
+ Help: "Northeast Asia Region 1",
+ }},
+ }, {
+ Name: "location_constraint",
+ Help: "location where your bucket will be created and your data stored.\n",
+ Provider: "Rabata",
+ Examples: []fs.OptionExample{{
+ Value: "us-east-1",
+ Help: "US East (N. Virginia)",
+ }, {
+ Value: "eu-west-1",
+ Help: "EU (Ireland)",
+ }, {
+ Value: "eu-west-2",
+ Help: "EU (London)",
+ }},
}, {
Name: "location_constraint",
Help: "Location constraint - the location where your bucket will be located and your data stored.\n",
@@ -2055,41 +2188,15 @@ func init() {
Help: "New York (USA) Region",
}, {
Value: "us-west-1",
- Help: "Freemont (USA) Region",
+ Help: "Fremont (USA) Region",
}, {
Value: "nz",
Help: "Auckland (New Zealand) Region",
}},
- }, {
- Name: "location_constraint",
- Help: "Location constraint - must be set to match the Region.\n\nUsed when creating buckets only.",
- Provider: "Qiniu",
- Examples: []fs.OptionExample{{
- Value: "cn-east-1",
- Help: "East China Region 1",
- }, {
- Value: "cn-east-2",
- Help: "East China Region 2",
- }, {
- Value: "cn-north-1",
- Help: "North China Region 1",
- }, {
- Value: "cn-south-1",
- Help: "South China Region 1",
- }, {
- Value: "us-north-1",
- Help: "North America Region 1",
- }, {
- Value: "ap-southeast-1",
- Help: "Southeast Asia Region 1",
- }, {
- Value: "ap-northeast-1",
- Help: "Northeast Asia Region 1",
- }},
}, {
Name: "location_constraint",
Help: "Location constraint - must be set to match the Region.\n\nLeave blank if not sure. Used when creating buckets only.",
- Provider: "!AWS,Alibaba,ArvanCloud,HuaweiOBS,ChinaMobile,Cloudflare,FlashBlade,IBMCOS,IDrive,Intercolo,IONOS,Leviia,Liara,Linode,Magalu,Outscale,OVHcloud,Qiniu,RackCorp,Scaleway,Selectel,SpectraLogic,StackPath,Storj,TencentCOS,Petabox,Mega",
+ Provider: "!AWS,Alibaba,ArvanCloud,ChinaMobile,Cloudflare,FlashBlade,FileLu,HuaweiOBS,IBMCOS,IDrive,Intercolo,IONOS,Leviia,Liara,Linode,Magalu,Mega,Outscale,OVHcloud,Petabox,Qiniu,Rabata,RackCorp,Scaleway,Selectel,SpectraLogic,StackPath,Storj,TencentCOS",
}, {
Name: "acl",
Help: `Canned ACL used when creating buckets and storing or copying objects.
@@ -2104,7 +2211,7 @@ doesn't copy the ACL from the source but rather writes a fresh one.
If the acl is an empty string then no X-Amz-Acl: header is added and
the default (private) will be used.
`,
- Provider: "!Storj,Selectel,SpectraLogic,Synology,Cloudflare,FlashBlade,Mega",
+ Provider: "!Cloudflare,FlashBlade,Mega,Rabata,Selectel,SpectraLogic,Storj,Synology",
Examples: []fs.OptionExample{{
Value: "default",
Help: "Owner gets Full_CONTROL.\nNo one else has access rights (default).",
@@ -2128,11 +2235,11 @@ the default (private) will be used.
}, {
Value: "bucket-owner-read",
Help: "Object owner gets FULL_CONTROL.\nBucket owner gets READ access.\nIf you specify this canned ACL when creating a bucket, Amazon S3 ignores it.",
- Provider: "!IBMCOS,ChinaMobile",
+ Provider: "!ChinaMobile,IBMCOS",
}, {
Value: "bucket-owner-full-control",
Help: "Both the object owner and the bucket owner get FULL_CONTROL over the object.\nIf you specify this canned ACL when creating a bucket, Amazon S3 ignores it.",
- Provider: "!IBMCOS,ChinaMobile",
+ Provider: "!ChinaMobile,IBMCOS",
}, {
Value: "private",
Help: "Owner gets FULL_CONTROL.\nNo one else has access rights (default).\nThis acl is available on IBM Cloud (Infra), IBM Cloud (Storage), On-Premise COS.",
@@ -2162,7 +2269,7 @@ isn't set then "acl" is used instead.
If the "acl" and "bucket_acl" are empty strings then no X-Amz-Acl:
header is added and the default (private) will be used.
`,
- Provider: "!Storj,Selectel,SpectraLogic,Synology,Cloudflare,FlashBlade",
+ Provider: "!Cloudflare,FlashBlade,Rabata,Selectel,SpectraLogic,Storj,Synology",
Advanced: true,
Examples: []fs.OptionExample{{
Value: "private",
@@ -2291,6 +2398,15 @@ If you leave it blank, this is calculated automatically from the sse_customer_ke
Value: "GLACIER_IR",
Help: "Glacier Instant Retrieval storage class",
}},
+ }, {
+ // Mapping from here: https://www.arvancloud.ir/en/products/cloud-storage
+ Name: "storage_class",
+ Help: "The storage class to use when storing new objects in ArvanCloud.",
+ Provider: "ArvanCloud",
+ Examples: []fs.OptionExample{{
+ Value: "STANDARD",
+ Help: "Standard storage class",
+ }},
}, {
// Mapping from here: https://www.alibabacloud.com/help/doc-detail/64919.htm
Name: "storage_class",
@@ -2336,15 +2452,6 @@ If you leave it blank, this is calculated automatically from the sse_customer_ke
Value: "STANDARD",
Help: "Standard storage class",
}},
- }, {
- // Mapping from here: https://www.arvancloud.ir/en/products/cloud-storage
- Name: "storage_class",
- Help: "The storage class to use when storing new objects in ArvanCloud.",
- Provider: "ArvanCloud",
- Examples: []fs.OptionExample{{
- Value: "STANDARD",
- Help: "Standard storage class",
- }},
}, {
// Mapping from here: https://docs.magalu.cloud/docs/storage/object-storage/Classes-de-Armazenamento/standard
Name: "storage_class",
@@ -2358,22 +2465,22 @@ If you leave it blank, this is calculated automatically from the sse_customer_ke
Help: "Glacier Instant Retrieval storage class",
}},
}, {
- // Mapping from here: https://intl.cloud.tencent.com/document/product/436/30925
+ // Mapping from here: https://developer.qiniu.com/kodo/5906/storage-type
Name: "storage_class",
- Help: "The storage class to use when storing new objects in Tencent COS.",
- Provider: "TencentCOS",
+ Help: "The storage class to use when storing new objects in Qiniu.",
+ Provider: "Qiniu",
Examples: []fs.OptionExample{{
- Value: "",
- Help: "Default",
- }, {
Value: "STANDARD",
Help: "Standard storage class",
}, {
- Value: "ARCHIVE",
+ Value: "LINE",
+ Help: "Infrequent access storage mode",
+ }, {
+ Value: "GLACIER",
Help: "Archive storage mode",
}, {
- Value: "STANDARD_IA",
- Help: "Infrequent access storage mode",
+ Value: "DEEP_ARCHIVE",
+ Help: "Deep archive storage mode",
}},
}, {
// Mapping from here: https://www.scaleway.com/en/docs/storage/object/quickstart/
@@ -2394,22 +2501,22 @@ If you leave it blank, this is calculated automatically from the sse_customer_ke
Help: "One Zone - Infrequent Access.\nA good choice for storing secondary backup copies or easily re-creatable data.\nAvailable in the FR-PAR region only.",
}},
}, {
- // Mapping from here: https://developer.qiniu.com/kodo/5906/storage-type
+ // Mapping from here: https://intl.cloud.tencent.com/document/product/436/30925
Name: "storage_class",
- Help: "The storage class to use when storing new objects in Qiniu.",
- Provider: "Qiniu",
+ Help: "The storage class to use when storing new objects in Tencent COS.",
+ Provider: "TencentCOS",
Examples: []fs.OptionExample{{
+ Value: "",
+ Help: "Default",
+ }, {
Value: "STANDARD",
Help: "Standard storage class",
}, {
- Value: "LINE",
- Help: "Infrequent access storage mode",
- }, {
- Value: "GLACIER",
+ Value: "ARCHIVE",
Help: "Archive storage mode",
}, {
- Value: "DEEP_ARCHIVE",
- Help: "Deep archive storage mode",
+ Value: "STANDARD_IA",
+ Help: "Infrequent access storage mode",
}},
}, {
Name: "upload_cutoff",
@@ -3658,6 +3765,8 @@ func setQuirks(opt *Options) {
case "Alibaba":
useMultipartEtag = false // Alibaba seems to calculate multipart Etags differently from AWS
useAlreadyExists = true // returns 200 OK
+ case "Hetzner":
+ useAlreadyExists = false
case "HuaweiOBS":
// Huawei OBS PFS is not support listObjectV2, and if turn on the urlEncodeListing, marker will not work and keep list same page forever.
urlEncodeListings = false
@@ -3690,6 +3799,11 @@ func setQuirks(opt *Options) {
case "FlashBlade":
mightGzip = false // Never auto gzips objects
virtualHostStyle = false // supports vhost but defaults to paths
+ case "FileLu":
+ listObjectsV2 = false
+ virtualHostStyle = false
+ urlEncodeListings = false
+ useMultipartEtag = false
case "IBMCOS":
listObjectsV2 = false // untested
virtualHostStyle = false
@@ -3744,6 +3858,8 @@ func setQuirks(opt *Options) {
virtualHostStyle = false
case "OVHcloud":
// No quirks
+ case "Rabata":
+ // server side copy not supported
case "RackCorp":
// No quirks
useMultipartEtag = false // untested
@@ -4024,6 +4140,12 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e
if opt.Provider == "AWS" {
f.features.DoubleSlash = true
}
+ if opt.Provider == "Rabata" {
+ f.features.Copy = nil
+ }
+ if opt.Provider == "Hetzner" {
+ f.features.SetTier = false
+ }
if opt.DirectoryMarkers {
f.features.CanHaveEmptyDirectories = true
}
@@ -6159,6 +6281,10 @@ func (o *Object) ModTime(ctx context.Context) time.Time {
// SetModTime sets the modification time of the local fs object
func (o *Object) SetModTime(ctx context.Context, modTime time.Time) error {
+ if o.fs.opt.Provider == "Rabata" {
+ // Rabata does not support copying objects
+ return fs.ErrorCantSetModTime
+ }
err := o.readMetaData(ctx)
if err != nil {
return err
@@ -6247,8 +6373,8 @@ func (o *Object) downloadFromURL(ctx context.Context, bucketPath string, options
metaData := make(map[string]string)
for key, value := range resp.Header {
key = strings.ToLower(key)
- if strings.HasPrefix(key, "x-amz-meta-") {
- metaKey := strings.TrimPrefix(key, "x-amz-meta-")
+ if after, ok := strings.CutPrefix(key, "x-amz-meta-"); ok {
+ metaKey := after
metaData[metaKey] = value[0]
}
}
diff --git a/backend/s3/s3_internal_test.go b/backend/s3/s3_internal_test.go
index cdca649ae..fb9297e84 100644
--- a/backend/s3/s3_internal_test.go
+++ b/backend/s3/s3_internal_test.go
@@ -1,4 +1,4 @@
-package s3
+package s3
import (
"bytes"
diff --git a/backend/s3/s3_test.go b/backend/s3/s3_test.go
index 6d00d8761..d05952d20 100644
--- a/backend/s3/s3_test.go
+++ b/backend/s3/s3_test.go
@@ -1,4 +1,4 @@
-// Test S3 filesystem interface
+// Test S3 filesystem interface
package s3
import (
diff --git a/backend/s3/setfrom.go b/backend/s3/setfrom.go
index 439e8b6b0..6d0e910a5 100644
--- a/backend/s3/setfrom.go
+++ b/backend/s3/setfrom.go
@@ -1,4 +1,4 @@
-// Code generated by "go run gen_setfrom.go"; DO NOT EDIT.
+// Code generated by "go run gen_setfrom.go"; DO NOT EDIT.
package s3
diff --git a/backend/s3/v2sign.go b/backend/s3/v2sign.go
index 45b1bf7f3..cc84bbe12 100644
--- a/backend/s3/v2sign.go
+++ b/backend/s3/v2sign.go
@@ -1,4 +1,4 @@
-// v2 signing
+// v2 signing
package s3
diff --git a/backend/seafile/api/types.go b/backend/seafile/api/types.go
index d6224709f..c9ae19beb 100644
--- a/backend/seafile/api/types.go
+++ b/backend/seafile/api/types.go
@@ -1,4 +1,4 @@
-// Package api provides types used by the Seafile API.
+// Package api provides types used by the Seafile API.
package api
// Some api objects are duplicated with only small differences,
diff --git a/backend/seafile/object.go b/backend/seafile/object.go
index 25257825a..42a8b4f10 100644
--- a/backend/seafile/object.go
+++ b/backend/seafile/object.go
@@ -1,4 +1,4 @@
-package seafile
+package seafile
import (
"context"
diff --git a/backend/seafile/pacer.go b/backend/seafile/pacer.go
index 55680193e..923369bcb 100644
--- a/backend/seafile/pacer.go
+++ b/backend/seafile/pacer.go
@@ -1,4 +1,4 @@
-package seafile
+package seafile
import (
"context"
diff --git a/backend/seafile/renew.go b/backend/seafile/renew.go
index 166f0f5db..0805b2b28 100644
--- a/backend/seafile/renew.go
+++ b/backend/seafile/renew.go
@@ -1,4 +1,4 @@
-package seafile
+package seafile
import (
"sync"
diff --git a/backend/seafile/renew_test.go b/backend/seafile/renew_test.go
index 845693e44..c58cfa1f6 100644
--- a/backend/seafile/renew_test.go
+++ b/backend/seafile/renew_test.go
@@ -1,4 +1,4 @@
-package seafile
+package seafile
import (
"sync/atomic"
diff --git a/backend/seafile/seafile.go b/backend/seafile/seafile.go
index 1151def5f..0f8796e9e 100644
--- a/backend/seafile/seafile.go
+++ b/backend/seafile/seafile.go
@@ -1,4 +1,4 @@
-// Package seafile provides an interface to the Seafile storage system.
+// Package seafile provides an interface to the Seafile storage system.
package seafile
import (
diff --git a/backend/seafile/seafile_internal_test.go b/backend/seafile/seafile_internal_test.go
index c8e3c7410..71380c165 100644
--- a/backend/seafile/seafile_internal_test.go
+++ b/backend/seafile/seafile_internal_test.go
@@ -1,4 +1,4 @@
-package seafile
+package seafile
import (
"context"
diff --git a/backend/seafile/seafile_test.go b/backend/seafile/seafile_test.go
index 669478548..726bcac01 100644
--- a/backend/seafile/seafile_test.go
+++ b/backend/seafile/seafile_test.go
@@ -1,4 +1,4 @@
-// Test Seafile filesystem interface
+// Test Seafile filesystem interface
package seafile_test
import (
diff --git a/backend/seafile/webapi.go b/backend/seafile/webapi.go
index 967dbabf0..b52650086 100644
--- a/backend/seafile/webapi.go
+++ b/backend/seafile/webapi.go
@@ -1,4 +1,4 @@
-package seafile
+package seafile
import (
"bytes"
diff --git a/backend/sftp/sftp.go b/backend/sftp/sftp.go
index e3d5242fb..4d425cd60 100644
--- a/backend/sftp/sftp.go
+++ b/backend/sftp/sftp.go
@@ -1,4 +1,4 @@
-//go:build !plan9
+//go:build !plan9
// Package sftp provides a filesystem interface using github.com/pkg/sftp
package sftp
diff --git a/backend/sftp/sftp_internal_test.go b/backend/sftp/sftp_internal_test.go
index e799f6654..ddd7b2986 100644
--- a/backend/sftp/sftp_internal_test.go
+++ b/backend/sftp/sftp_internal_test.go
@@ -1,4 +1,4 @@
-//go:build !plan9
+//go:build !plan9
package sftp
diff --git a/backend/sftp/sftp_test.go b/backend/sftp/sftp_test.go
index 2e9ad9f20..46dcf1253 100644
--- a/backend/sftp/sftp_test.go
+++ b/backend/sftp/sftp_test.go
@@ -1,4 +1,4 @@
-// Test Sftp filesystem interface
+// Test Sftp filesystem interface
//go:build !plan9
diff --git a/backend/sftp/sftp_unsupported.go b/backend/sftp/sftp_unsupported.go
index 67794bc4f..c588825f4 100644
--- a/backend/sftp/sftp_unsupported.go
+++ b/backend/sftp/sftp_unsupported.go
@@ -1,4 +1,4 @@
-// Build for sftp for unsupported platforms to stop go complaining
+// Build for sftp for unsupported platforms to stop go complaining
// about "no buildable Go source files "
//go:build plan9
diff --git a/backend/sftp/ssh.go b/backend/sftp/ssh.go
index d0a25e29e..ee8098558 100644
--- a/backend/sftp/ssh.go
+++ b/backend/sftp/ssh.go
@@ -1,4 +1,4 @@
-//go:build !plan9
+//go:build !plan9
package sftp
diff --git a/backend/sftp/ssh_external.go b/backend/sftp/ssh_external.go
index a42cdd7cc..e11aa2861 100644
--- a/backend/sftp/ssh_external.go
+++ b/backend/sftp/ssh_external.go
@@ -1,4 +1,4 @@
-//go:build !plan9
+//go:build !plan9
package sftp
diff --git a/backend/sftp/ssh_internal.go b/backend/sftp/ssh_internal.go
index 2508bbf31..4b9faf86d 100644
--- a/backend/sftp/ssh_internal.go
+++ b/backend/sftp/ssh_internal.go
@@ -1,4 +1,4 @@
-//go:build !plan9
+//go:build !plan9
package sftp
diff --git a/backend/sftp/stringlock.go b/backend/sftp/stringlock.go
index df658f961..911da932c 100644
--- a/backend/sftp/stringlock.go
+++ b/backend/sftp/stringlock.go
@@ -1,4 +1,4 @@
-//go:build !plan9
+//go:build !plan9
package sftp
diff --git a/backend/sftp/stringlock_test.go b/backend/sftp/stringlock_test.go
index ded8f3cfd..937accfeb 100644
--- a/backend/sftp/stringlock_test.go
+++ b/backend/sftp/stringlock_test.go
@@ -1,4 +1,4 @@
-//go:build !plan9
+//go:build !plan9
package sftp
diff --git a/backend/sharefile/api/types.go b/backend/sharefile/api/types.go
index 7b48bbec7..66bd2a639 100644
--- a/backend/sharefile/api/types.go
+++ b/backend/sharefile/api/types.go
@@ -1,4 +1,4 @@
-// Package api contains definitions for using the premiumize.me API
+// Package api contains definitions for using the premiumize.me API
package api
import (
diff --git a/backend/sharefile/generate_tzdata.go b/backend/sharefile/generate_tzdata.go
index 90cb32bd5..6e484bcd5 100644
--- a/backend/sharefile/generate_tzdata.go
+++ b/backend/sharefile/generate_tzdata.go
@@ -1,4 +1,4 @@
-//go:build ignore
+//go:build ignore
package main
diff --git a/backend/sharefile/sharefile.go b/backend/sharefile/sharefile.go
index e470a795a..dda0c6c2b 100644
--- a/backend/sharefile/sharefile.go
+++ b/backend/sharefile/sharefile.go
@@ -1,4 +1,4 @@
-// Package sharefile provides an interface to the Citrix Sharefile
+// Package sharefile provides an interface to the Citrix Sharefile
// object storage system.
package sharefile
diff --git a/backend/sharefile/sharefile_test.go b/backend/sharefile/sharefile_test.go
index 0004610a6..f0f1c4b4d 100644
--- a/backend/sharefile/sharefile_test.go
+++ b/backend/sharefile/sharefile_test.go
@@ -1,4 +1,4 @@
-// Test filesystem interface
+// Test filesystem interface
package sharefile
import (
diff --git a/backend/sharefile/tzdata_vfsdata.go b/backend/sharefile/tzdata_vfsdata.go
index 23d2120f9..29b2448cc 100644
--- a/backend/sharefile/tzdata_vfsdata.go
+++ b/backend/sharefile/tzdata_vfsdata.go
@@ -1,4 +1,4 @@
-// Code generated by vfsgen; DO NOT EDIT.
+// Code generated by vfsgen; DO NOT EDIT.
//go:build !dev
// +build !dev
diff --git a/backend/sharefile/upload.go b/backend/sharefile/upload.go
index 0e9691972..679d2778b 100644
--- a/backend/sharefile/upload.go
+++ b/backend/sharefile/upload.go
@@ -1,4 +1,4 @@
-// Upload large files for sharefile
+// Upload large files for sharefile
//
// Docs - https://api.sharefile.com/rest/docs/resource.aspx?name=Items#Upload_File
diff --git a/backend/sia/api/types.go b/backend/sia/api/types.go
index eb046ab22..0292a3d85 100644
--- a/backend/sia/api/types.go
+++ b/backend/sia/api/types.go
@@ -1,4 +1,4 @@
-// Package api provides types used by the Sia API.
+// Package api provides types used by the Sia API.
package api
import (
diff --git a/backend/sia/sia.go b/backend/sia/sia.go
index 9f4e02bf0..d0b779084 100644
--- a/backend/sia/sia.go
+++ b/backend/sia/sia.go
@@ -1,4 +1,4 @@
-// Package sia provides an interface to the Sia storage system.
+// Package sia provides an interface to the Sia storage system.
package sia
import (
diff --git a/backend/sia/sia_test.go b/backend/sia/sia_test.go
index 96d42224a..5f57a5c4d 100644
--- a/backend/sia/sia_test.go
+++ b/backend/sia/sia_test.go
@@ -1,4 +1,4 @@
-// Test Sia filesystem interface
+// Test Sia filesystem interface
package sia_test
import (
diff --git a/backend/smb/connpool.go b/backend/smb/connpool.go
index e040881d9..14bfb515d 100644
--- a/backend/smb/connpool.go
+++ b/backend/smb/connpool.go
@@ -1,4 +1,4 @@
-package smb
+package smb
import (
"context"
diff --git a/backend/smb/filepool.go b/backend/smb/filepool.go
index 0061ec1ff..577b8c051 100644
--- a/backend/smb/filepool.go
+++ b/backend/smb/filepool.go
@@ -1,4 +1,4 @@
-package smb
+package smb
import (
"context"
diff --git a/backend/smb/filepool_test.go b/backend/smb/filepool_test.go
index 7cf90988b..c8b546935 100644
--- a/backend/smb/filepool_test.go
+++ b/backend/smb/filepool_test.go
@@ -1,4 +1,4 @@
-package smb
+package smb
import (
"context"
@@ -200,7 +200,7 @@ func TestFilePool_ConcurrentAccess(t *testing.T) {
pool := newFilePool(ctx, fs, "testshare", "/test/path")
const numGoroutines = 10
- for i := 0; i < numGoroutines; i++ {
+ for range numGoroutines {
mockFile := newMockFile()
pool.pool = append(pool.pool, mockFile)
}
@@ -208,7 +208,7 @@ func TestFilePool_ConcurrentAccess(t *testing.T) {
// Test concurrent get operations
done := make(chan bool, numGoroutines)
- for i := 0; i < numGoroutines; i++ {
+ for range numGoroutines {
go func() {
defer func() { done <- true }()
@@ -219,7 +219,7 @@ func TestFilePool_ConcurrentAccess(t *testing.T) {
}()
}
- for i := 0; i < numGoroutines; i++ {
+ for range numGoroutines {
<-done
}
diff --git a/backend/smb/kerberos.go b/backend/smb/kerberos.go
index fdbb19ddd..c6997dd76 100644
--- a/backend/smb/kerberos.go
+++ b/backend/smb/kerberos.go
@@ -1,4 +1,4 @@
-package smb
+package smb
import (
"fmt"
diff --git a/backend/smb/kerberos_test.go b/backend/smb/kerberos_test.go
index dc78b3bbf..55cf616e8 100644
--- a/backend/smb/kerberos_test.go
+++ b/backend/smb/kerberos_test.go
@@ -1,4 +1,4 @@
-package smb
+package smb
import (
"os"
diff --git a/backend/smb/smb.go b/backend/smb/smb.go
index 993b2ec7d..6bbdf1aac 100644
--- a/backend/smb/smb.go
+++ b/backend/smb/smb.go
@@ -1,4 +1,4 @@
-// Package smb provides an interface to SMB servers
+// Package smb provides an interface to SMB servers
package smb
import (
@@ -192,6 +192,9 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e
return nil, err
}
+ // if root is empty or ends with / (must be a directory)
+ isRootDir := isPathDir(root)
+
root = strings.Trim(root, "/")
f := &Fs{
@@ -218,6 +221,11 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e
if share == "" || dir == "" {
return f, nil
}
+
+ // Skip stat check if root is already a directory
+ if isRootDir {
+ return f, nil
+ }
cn, err := f.getConnection(ctx, share)
if err != nil {
return nil, err
@@ -894,6 +902,11 @@ func ensureSuffix(s, suffix string) string {
return s + suffix
}
+// isPathDir determines if a path represents a directory based on trailing slash
+func isPathDir(path string) bool {
+ return path == "" || strings.HasSuffix(path, "/")
+}
+
func trimPathPrefix(s, prefix string) string {
// we need to clean the paths to make tests pass!
s = betterPathClean(s)
diff --git a/backend/smb/smb_internal_test.go b/backend/smb/smb_internal_test.go
new file mode 100644
index 000000000..dffc4a58a
--- /dev/null
+++ b/backend/smb/smb_internal_test.go
@@ -0,0 +1,41 @@
+// Unit tests for internal SMB functions
+package smb
+
+import "testing"
+
+// TestIsPathDir tests the isPathDir function logic
+func TestIsPathDir(t *testing.T) {
+ tests := []struct {
+ path string
+ expected bool
+ }{
+ // Empty path should be considered a directory
+ {"", true},
+
+ // Paths with trailing slash should be directories
+ {"/", true},
+ {"share/", true},
+ {"share/dir/", true},
+ {"share/dir/subdir/", true},
+
+ // Paths without trailing slash should not be directories
+ {"share", false},
+ {"share/dir", false},
+ {"share/dir/file", false},
+ {"share/dir/subdir/file", false},
+
+ // Edge cases
+ {"share//", true},
+ {"share///", true},
+ {"share/dir//", true},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.path, func(t *testing.T) {
+ result := isPathDir(tt.path)
+ if result != tt.expected {
+ t.Errorf("isPathDir(%q) = %v, want %v", tt.path, result, tt.expected)
+ }
+ })
+ }
+}
diff --git a/backend/smb/smb_test.go b/backend/smb/smb_test.go
index 1f77dd404..09c9e132c 100644
--- a/backend/smb/smb_test.go
+++ b/backend/smb/smb_test.go
@@ -1,4 +1,4 @@
-// Test smb filesystem interface
+// Test smb filesystem interface
package smb_test
import (
diff --git a/backend/storj/fs.go b/backend/storj/fs.go
index 84be8bc68..1d7fb4c9c 100644
--- a/backend/storj/fs.go
+++ b/backend/storj/fs.go
@@ -1,4 +1,4 @@
-//go:build !plan9
+//go:build !plan9
// Package storj provides an interface to Storj decentralized object storage.
package storj
diff --git a/backend/storj/object.go b/backend/storj/object.go
index 479d54a5a..05b079ce6 100644
--- a/backend/storj/object.go
+++ b/backend/storj/object.go
@@ -1,4 +1,4 @@
-//go:build !plan9
+//go:build !plan9
package storj
diff --git a/backend/storj/storj_test.go b/backend/storj/storj_test.go
index c1678baa0..d4c4d2fe7 100644
--- a/backend/storj/storj_test.go
+++ b/backend/storj/storj_test.go
@@ -1,4 +1,4 @@
-//go:build !plan9
+//go:build !plan9
// Test Storj filesystem interface
package storj_test
diff --git a/backend/storj/storj_unsupported.go b/backend/storj/storj_unsupported.go
index c3c731594..3f60719c1 100644
--- a/backend/storj/storj_unsupported.go
+++ b/backend/storj/storj_unsupported.go
@@ -1,4 +1,4 @@
-//go:build plan9
+//go:build plan9
// Package storj provides an interface to Storj decentralized object storage.
package storj
diff --git a/backend/sugarsync/api/types.go b/backend/sugarsync/api/types.go
index 5edda687b..74330ce1a 100644
--- a/backend/sugarsync/api/types.go
+++ b/backend/sugarsync/api/types.go
@@ -1,4 +1,4 @@
-// Package api has type definitions for sugarsync
+// Package api has type definitions for sugarsync
//
// Converted from the API docs with help from https://www.onlinetool.io/xmltogo/
package api
diff --git a/backend/sugarsync/sugarsync.go b/backend/sugarsync/sugarsync.go
index 72a13710f..c528acdf3 100644
--- a/backend/sugarsync/sugarsync.go
+++ b/backend/sugarsync/sugarsync.go
@@ -1,4 +1,4 @@
-// Package sugarsync provides an interface to the Sugarsync
+// Package sugarsync provides an interface to the Sugarsync
// object storage system.
package sugarsync
diff --git a/backend/sugarsync/sugarsync_internal_test.go b/backend/sugarsync/sugarsync_internal_test.go
index 59093be2f..f0c004470 100644
--- a/backend/sugarsync/sugarsync_internal_test.go
+++ b/backend/sugarsync/sugarsync_internal_test.go
@@ -1,4 +1,4 @@
-package sugarsync
+package sugarsync
import (
"bytes"
diff --git a/backend/sugarsync/sugarsync_test.go b/backend/sugarsync/sugarsync_test.go
index 619d9f883..a54e072f7 100644
--- a/backend/sugarsync/sugarsync_test.go
+++ b/backend/sugarsync/sugarsync_test.go
@@ -1,4 +1,4 @@
-// Test Sugarsync filesystem interface
+// Test Sugarsync filesystem interface
package sugarsync_test
import (
diff --git a/backend/swift/auth.go b/backend/swift/auth.go
index 64adc37d8..e3e8f2c49 100644
--- a/backend/swift/auth.go
+++ b/backend/swift/auth.go
@@ -1,4 +1,4 @@
-package swift
+package swift
import (
"context"
diff --git a/backend/swift/swift.go b/backend/swift/swift.go
index d4d5c5fc9..6bf09d89d 100644
--- a/backend/swift/swift.go
+++ b/backend/swift/swift.go
@@ -1,4 +1,4 @@
-// Package swift provides an interface to the Swift object storage system
+// Package swift provides an interface to the Swift object storage system
package swift
import (
@@ -561,6 +561,21 @@ func (f *Fs) setRoot(root string) {
f.rootContainer, f.rootDirectory = bucket.Split(f.root)
}
+// Fetch the base container's policy to be used if/when we need to create a
+// segments container to ensure we use the same policy.
+func (f *Fs) fetchStoragePolicy(ctx context.Context, container string) (fs.Fs, error) {
+ err := f.pacer.Call(func() (bool, error) {
+ var rxHeaders swift.Headers
+ _, rxHeaders, err := f.c.Container(ctx, container)
+
+ f.opt.StoragePolicy = rxHeaders["X-Storage-Policy"]
+ fs.Debugf(f, "Auto set StoragePolicy to %s", f.opt.StoragePolicy)
+
+ return shouldRetryHeaders(ctx, rxHeaders, err)
+ })
+ return nil, err
+}
+
// NewFsWithConnection constructs an Fs from the path, container:path
// and authenticated connection.
//
@@ -590,6 +605,7 @@ func NewFsWithConnection(ctx context.Context, opt *Options, name, root string, c
f.opt.UseSegmentsContainer.Valid = true
fs.Debugf(f, "Auto set use_segments_container to %v", f.opt.UseSegmentsContainer.Value)
}
+
if f.rootContainer != "" && f.rootDirectory != "" {
// Check to see if the object exists - ignoring directory markers
var info swift.Object
@@ -1132,6 +1148,13 @@ func (f *Fs) newSegmentedUpload(ctx context.Context, dstContainer string, dstPat
container: dstContainer,
}
if f.opt.UseSegmentsContainer.Value {
+ if f.opt.StoragePolicy == "" {
+ _, err = f.fetchStoragePolicy(ctx, dstContainer)
+ if err != nil {
+ return nil, err
+ }
+ }
+
su.container += segmentsContainerSuffix
err = f.makeContainer(ctx, su.container)
if err != nil {
diff --git a/backend/swift/swift_internal_test.go b/backend/swift/swift_internal_test.go
index 49963fc61..a35af0d46 100644
--- a/backend/swift/swift_internal_test.go
+++ b/backend/swift/swift_internal_test.go
@@ -1,4 +1,4 @@
-package swift
+package swift
import (
"context"
diff --git a/backend/swift/swift_test.go b/backend/swift/swift_test.go
index f58f8f4b6..0f82c3297 100644
--- a/backend/swift/swift_test.go
+++ b/backend/swift/swift_test.go
@@ -1,4 +1,4 @@
-// Test Swift filesystem interface
+// Test Swift filesystem interface
package swift
import (
@@ -76,6 +76,7 @@ func (f *Fs) testNoChunk(t *testing.T) {
// Additional tests that aren't in the framework
func (f *Fs) InternalTest(t *testing.T) {
+ t.Run("PolicyDiscovery", f.testPolicyDiscovery)
t.Run("NoChunk", f.testNoChunk)
t.Run("WithChunk", f.testWithChunk)
t.Run("WithChunkFail", f.testWithChunkFail)
@@ -195,4 +196,50 @@ func (f *Fs) testCopyLargeObject(t *testing.T) {
require.Equal(t, obj.Size(), objTarget.Size())
}
+func (f *Fs) testPolicyDiscovery(t *testing.T) {
+ ctx := context.TODO()
+ container := "testPolicyDiscovery-1"
+ // Reset the policy so we can test if it is populated.
+ f.opt.StoragePolicy = ""
+ err := f.makeContainer(ctx, container)
+ require.NoError(t, err)
+ _, err = f.fetchStoragePolicy(ctx, container)
+ require.NoError(t, err)
+
+ // Default policy for SAIO image is 1replica.
+ assert.Equal(t, "1replica", f.opt.StoragePolicy)
+
+ // Create a container using a non-default policy, and check to ensure
+ // that the created segments container uses the same non-default policy.
+ policy := "Policy-1"
+ container = "testPolicyDiscovery-2"
+
+ f.opt.StoragePolicy = policy
+ err = f.makeContainer(ctx, container)
+ require.NoError(t, err)
+
+ // Reset the policy so we can test if it is populated, and set to the
+ // non-default policy.
+ f.opt.StoragePolicy = ""
+ _, err = f.fetchStoragePolicy(ctx, container)
+ require.NoError(t, err)
+ assert.Equal(t, policy, f.opt.StoragePolicy)
+
+ // Test that when a segmented upload container is made, the newly
+ // created container inherits the non-default policy of the base
+ // container.
+ f.opt.StoragePolicy = ""
+ f.opt.UseSegmentsContainer.Value = true
+ su, err := f.newSegmentedUpload(ctx, container, "")
+ require.NoError(t, err)
+ // The container name we expected?
+ segmentsContainer := container + segmentsContainerSuffix
+ assert.Equal(t, segmentsContainer, su.container)
+ // The policy we expected?
+ f.opt.StoragePolicy = ""
+ _, err = f.fetchStoragePolicy(ctx, su.container)
+ require.NoError(t, err)
+ assert.Equal(t, policy, f.opt.StoragePolicy)
+}
+
var _ fstests.InternalTester = (*Fs)(nil)
diff --git a/backend/teldrive/api/types.go b/backend/teldrive/api/types.go
index cbe2f611a..1ac8f40b6 100644
--- a/backend/teldrive/api/types.go
+++ b/backend/teldrive/api/types.go
@@ -1,4 +1,4 @@
-// Package api provides types used by the Teldrive API.
+// Package api provides types used by the Teldrive API.
package api
import "time"
@@ -33,6 +33,7 @@ type FileInfo struct {
ParentId string `json:"parentId"`
Type string `json:"type"`
ModTime time.Time `json:"updatedAt"`
+ Hash string `json:"hash"`
}
type Meta struct {
@@ -46,7 +47,6 @@ type ReadMetadataResponse struct {
Meta Meta `json:"meta"`
}
-// MetadataRequestOptions represents all the options when listing folder contents
type MetadataRequestOptions struct {
Page int64
Limit int64
@@ -82,7 +82,8 @@ type CreateFileRequest struct {
Encrypted bool `json:"encrypted,omitempty"`
Parts []FilePart `json:"parts,omitempty"`
ParentId string `json:"parentId,omitempty"`
- ModTime time.Time `json:"updatedAt,omitempty"`
+ ModTime time.Time `json:"updatedAt"`
+ UploadId string `json:"uploadId"`
}
type MoveFileRequest struct {
@@ -113,7 +114,7 @@ type RemoveFileRequest struct {
type CopyFile struct {
Newname string `json:"newName"`
Destination string `json:"destination"`
- ModTime time.Time `json:"updatedAt,omitempty"`
+ ModTime time.Time `json:"updatedAt"`
}
type Session struct {
diff --git a/backend/teldrive/tdhash/tdhash.go b/backend/teldrive/tdhash/tdhash.go
new file mode 100644
index 000000000..ff7094d55
--- /dev/null
+++ b/backend/teldrive/tdhash/tdhash.go
@@ -0,0 +1,123 @@
+// Package tdhash implements the Teldrive hashing algorithm.
+// Files are split into 16MB blocks (fixed size).
+// Each block is hashed with BLAKE3.
+// Block hashes are concatenated and hashed together to produce the final tree hash.
+package tdhash
+
+import (
+ "encoding/hex"
+ "hash"
+
+ "github.com/zeebo/blake3"
+)
+
+const (
+ // BlockSize is the fixed block size for tree hashing (16MB).
+ BlockSize = 16 * 1024 * 1024
+ // Size is the expected hex-encoded string length (64 chars for 32-byte blake3 hash).
+ // Note: Sum() returns 32 raw bytes, rclone will hex-encode for display.
+ Size = 64
+ // rawSize is the actual number of bytes returned by Sum().
+ rawSize = 32
+)
+
+type digest struct {
+ chunkHash hash.Hash // Hash for current block
+ totalHash hash.Hash // Tree hash (hash of block hashes)
+ n int64 // bytes written into current block
+ sumCalled bool
+ writtenMore bool
+}
+
+// New creates a new tree hash (uses BLAKE3 with fixed 16MB blocks)
+func New() hash.Hash {
+ d := &digest{}
+ d.Reset()
+ return d
+}
+
+// Reset resets the hash to its initial state.
+func (d *digest) Reset() {
+ d.n = 0
+ d.sumCalled = false
+ d.writtenMore = false
+
+ // Always use BLAKE3
+ d.chunkHash = blake3.New()
+ d.totalHash = blake3.New()
+}
+
+// writeChunkHash writes the current chunk hash into the total hash
+func (d *digest) writeChunkHash() {
+ chunkHashBytes := d.chunkHash.Sum(nil)
+ d.totalHash.Write(chunkHashBytes)
+ // Reset chunk hasher for next chunk
+ d.n = 0
+ d.chunkHash.Reset()
+}
+
+// Write adds more data to the running hash.
+// It processes data in chunks and accumulates chunk hashes.
+func (d *digest) Write(p []byte) (n int, err error) {
+ n = len(p)
+ d.writtenMore = true
+
+ for len(p) > 0 {
+ // Calculate how much we can write to current block
+ remainingInBlock := BlockSize - d.n
+ toWrite := min(int64(len(p)), remainingInBlock)
+
+ // Write to current block hash
+ d.chunkHash.Write(p[:toWrite])
+ d.n += toWrite
+ p = p[toWrite:]
+
+ // If block is full, finalize it and start new block
+ if d.n >= BlockSize {
+ d.writeChunkHash()
+ }
+ }
+
+ return n, nil
+}
+
+// Sum returns the current hash as raw bytes (32 bytes for BLAKE3).
+// Note: rclone's hash system will hex-encode this for display/comparison.
+func (d *digest) Sum(b []byte) []byte {
+ if d.sumCalled && d.writtenMore {
+ // If Sum was already called and we wrote more data, we need to
+ // finalize the last chunk if it has data
+ if d.n > 0 {
+ d.writeChunkHash()
+ }
+ }
+
+ // Finalize last chunk if it has data
+ if d.n > 0 {
+ d.writeChunkHash()
+ }
+
+ d.sumCalled = true
+ d.writtenMore = false
+
+ // Return raw tree hash bytes (not hex-encoded)
+ treeHashBytes := d.totalHash.Sum(nil)
+ return append(b, treeHashBytes...)
+}
+
+// Size returns the number of bytes Sum will return (32 for BLAKE3).
+func (d *digest) Size() int {
+ return rawSize
+}
+
+// BlockSize returns the hash's underlying block size.
+func (d *digest) BlockSize() int {
+ return BlockSize
+}
+
+// SumString computes the tree hash of the entire file and returns it as a hex string
+func SumString(data []byte) string {
+ d := New()
+ d.Write(data)
+ return hex.EncodeToString(d.Sum(nil))
+}
diff --git a/backend/teldrive/teldrive.go b/backend/teldrive/teldrive.go
index d27f2101a..486b591cc 100644
--- a/backend/teldrive/teldrive.go
+++ b/backend/teldrive/teldrive.go
@@ -1,9 +1,10 @@
-// Package teldrive provides an interface to the teldrive storage system.
+// Package teldrive provides an interface to the teldrive storage system.
package teldrive
import (
"bufio"
"context"
+ "encoding/json"
"errors"
"fmt"
"io"
@@ -16,6 +17,7 @@ import (
"time"
"github.com/rclone/rclone/backend/teldrive/api"
+ "github.com/rclone/rclone/backend/teldrive/tdhash"
"github.com/rclone/rclone/fs"
"github.com/rclone/rclone/fs/config"
"github.com/rclone/rclone/fs/config/configmap"
@@ -23,6 +25,7 @@ import (
"github.com/rclone/rclone/fs/fserrors"
"github.com/rclone/rclone/fs/fshttp"
"github.com/rclone/rclone/fs/hash"
+ "github.com/rclone/rclone/fs/object"
"github.com/rclone/rclone/lib/dircache"
"github.com/rclone/rclone/lib/encoder"
"github.com/rclone/rclone/lib/pacer"
@@ -32,15 +35,13 @@ import (
const (
timeFormat = time.RFC3339
- maxChunkSize = 2000 * fs.Mebi
- defaultChunkSize = 500 * fs.Mebi
- minChunkSize = 100 * fs.Mebi
+ maxChunkSize = 2000 * fs.Mebi // 125 × 16MB (Telegram limit)
+ defaultChunkSize = 512 * fs.Mebi // 32 × 16MB for optimal BLAKE3 tree hashing
+ minChunkSize = 64 * fs.Mebi // 4 × 16MB
authCookieName = "access_token"
)
-var (
- errCanNotUploadFileWithUnknownSize = errors.New("teldrive can't upload files with unknown size")
-)
+var telDriveHash hash.Type
func init() {
fs.Register(&fs.RegInfo{
@@ -93,14 +94,21 @@ func init() {
Name: "encrypt_files",
Default: false,
Help: "Enable Native Teldrive Encryption",
- }, {
-
+ },
+ {
+ Name: "hash_enabled",
+ Default: true,
+ Help: "Enable Blake3 Tree Hashing",
+ },
+ {
Name: config.ConfigEncoding,
Help: config.ConfigEncodingHelp,
Advanced: true,
Default: encoder.Standard | encoder.EncodeInvalidUtf8,
}},
})
+
+ telDriveHash = hash.RegisterHash("teldrive", "TelDriveHash", tdhash.Size, tdhash.New)
}
// Options defines the configuration for this backend
@@ -115,7 +123,7 @@ type Options struct {
ChannelID int64 `config:"channel_id"`
EncryptFiles bool `config:"encrypt_files"`
PageSize int64 `config:"page_size"`
- ThreadedStreams bool `config:"threaded_streams"`
+ HashEnabled bool `config:"hash_enabled"`
Enc encoder.MultiEncoder `config:"encoding"`
}
@@ -127,6 +135,7 @@ type Fs struct {
features *fs.Features
srv *rest.Client
pacer *fs.Pacer
+ ssePacer *fs.Pacer // Dedicated pacer for SSE connection retries
userId int64
dirCache *dircache.DirCache
rootFolderID string
@@ -142,6 +151,7 @@ type Object struct {
name string
modTime time.Time
mimeType string
+ hash string // BLAKE3 tree hash from server
}
// Name of the remote (as passed into NewFs)
@@ -165,8 +175,13 @@ func (f *Fs) Precision() time.Duration {
}
// Hashes returns the supported hash types of the filesystem
+// TelDrive uses BLAKE3 tree hashing only (16MB fixed blocks)
func (f *Fs) Hashes() hash.Set {
- return hash.Set(hash.None)
+ if f.opt.HashEnabled {
+ return hash.Set(telDriveHash)
+ }
+ return hash.NewHashSet(hash.None)
+
}
// Features returns the optional features of this Fs
@@ -193,14 +208,16 @@ func shouldRetry(ctx context.Context, resp *http.Response, err error) (bool, err
return fserrors.ShouldRetry(err) || fserrors.ShouldRetryHTTP(resp, retryErrorCodes), err
}
-func checkUploadChunkSize(cs fs.SizeSuffix) error {
- if cs < minChunkSize {
- return fmt.Errorf("ChunkSize: %s is less than %s", cs, minChunkSize)
- }
- if cs > maxChunkSize {
- return fmt.Errorf("ChunkSize: %s is greater than %s", cs, maxChunkSize)
- }
- return nil
+// alignChunkSize rounds the chunk size to the nearest 16MB multiple
+// and clamps it to min/max bounds
+func alignChunkSize(cs fs.SizeSuffix) fs.SizeSuffix {
+ blockSize := int64(16 * 1024 * 1024) // 16MB
+ chunkSizeBytes := min(max(int64(cs), int64(minChunkSize)), int64(maxChunkSize))
+ // Round to nearest 16MB multiple
+ // Ensure we don't exceed max after rounding
+ alignedSize := min(((chunkSizeBytes+blockSize/2)/blockSize)*blockSize, int64(maxChunkSize))
+
+ return fs.SizeSuffix(alignedSize)
}
func Ptr[T any](t T) *T {
@@ -223,14 +240,18 @@ func NewFs(ctx context.Context, name string, root string, config configmap.Mappe
return nil, err
}
- err = checkUploadChunkSize(opt.ChunkSize)
- if err != nil {
- return nil, err
- }
+ // Align chunk size to 16MB multiple for optimal BLAKE3 tree hashing
+ opt.ChunkSize = alignChunkSize(opt.ChunkSize)
if opt.ChannelID < 0 {
- channnelId := strconv.FormatInt(opt.ChannelID, 10)
- opt.ChannelID, _ = strconv.ParseInt(strings.TrimPrefix(channnelId, "-100"), 10, 64)
+ channelIDStr := strconv.FormatInt(opt.ChannelID, 10)
+ // teldrive API expects channel ID without the -100 prefix for supergroups/channels
+ trimmedIDStr := strings.TrimPrefix(channelIDStr, "-100")
+ newID, err := strconv.ParseInt(trimmedIDStr, 10, 64)
+ if err != nil {
+ return nil, fmt.Errorf("invalid channel_id: %w", err)
+ }
+ opt.ChannelID = newID
}
f := &Fs{
@@ -238,6 +259,12 @@ func NewFs(ctx context.Context, name string, root string, config configmap.Mappe
root: root,
opt: *opt,
pacer: fs.NewPacer(ctx, pacer.NewDefault()),
+ // Dedicated SSE pacer with optimized settings for connection retries
+ ssePacer: fs.NewPacer(ctx, pacer.NewDefault(
+ pacer.MinSleep(1*time.Second),
+ pacer.MaxSleep(30*time.Second),
+ pacer.DecayConstant(2),
+ )),
}
f.root = strings.Trim(root, "/")
@@ -498,6 +525,7 @@ func (f *Fs) newObjectWithInfo(_ context.Context, remote string, info *api.FileI
name: info.Name,
modTime: info.ModTime,
mimeType: info.MimeType,
+ hash: info.Hash,
}
if info.Type == "folder" {
return o, fs.ErrorIsDir
@@ -601,14 +629,28 @@ func (f *Fs) updateFileInformation(ctx context.Context, update *api.UpdateFileIn
}
func (f *Fs) putUnchecked(ctx context.Context, in io.Reader, src fs.ObjectInfo, _ ...fs.OpenOption) error {
-
o := &Object{
fs: f,
}
- uploadInfo, err := o.uploadMultipart(ctx, bufio.NewReader(in), src)
- if err != nil {
- return err
+ var uploadInfo *uploadInfo
+ var err error
+ size := src.Size()
+
+ if size < 0 {
+ // Unknown size - buffer to memory/temp file first
+ fs.Debugf(f, "putUnchecked: unknown size, buffering to memory (threshold: %d bytes)", memoryBufferThreshold)
+ uploadInfo, size, err = o.uploadWithBuffering(ctx, in, src)
+ if err != nil {
+ return err
+ }
+ // Create new src with known size for createFile
+ src = object.NewStaticObjectInfo(src.Remote(), src.ModTime(ctx), size, false, nil, f)
+ } else if size > 0 {
+ uploadInfo, err = o.uploadMultipart(ctx, in, src)
+ if err != nil {
+ return err
+ }
}
return o.createFile(ctx, src, uploadInfo)
@@ -639,9 +681,6 @@ func (f *Fs) FindLeaf(ctx context.Context, pathID, leaf string) (pathIDOut strin
// will return the object and the error, otherwise will return
// nil and the error
func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
- if src.Size() < 0 {
- return nil, errCanNotUploadFileWithUnknownSize
- }
existingObj, err := f.NewObject(ctx, src.Remote())
switch err {
case nil:
@@ -672,13 +711,7 @@ func (f *Fs) PutUnchecked(ctx context.Context, in io.Reader, src fs.ObjectInfo,
//
// The new object may have been created if an error is returned
func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) error {
-
- if src.Size() < 0 {
- return errCanNotUploadFileWithUnknownSize
- }
-
remote := o.Remote()
-
modTime := src.ModTime(ctx)
leaf, directoryID, err := o.fs.dirCache.FindPath(ctx, remote, true)
@@ -687,9 +720,17 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
}
var uploadInfo *uploadInfo
+ size := src.Size()
- if src.Size() > 0 {
- uploadInfo, err = o.uploadMultipart(ctx, bufio.NewReader(in), src)
+ if size < 0 {
+ // Unknown size - buffer to memory/temp file first
+ fs.Debugf(o, "Update: unknown size, buffering to memory (threshold: %d bytes)", memoryBufferThreshold)
+ uploadInfo, size, err = o.uploadWithBuffering(ctx, in, src)
+ if err != nil {
+ return err
+ }
+ } else if size > 0 {
+ uploadInfo, err = o.uploadMultipart(ctx, in, src)
if err != nil {
return err
}
@@ -697,21 +738,20 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
payload := &api.UpdateFileInformation{
ModTime: Ptr(modTime.UTC()),
- Size: src.Size(),
+ Size: size,
ParentID: directoryID,
Name: leaf,
}
if uploadInfo != nil {
- payload.Parts = uploadInfo.fileChunks
payload.UploadId = uploadInfo.uploadID
- payload.ChannelID = o.fs.opt.ChannelID
+ payload.ChannelID = uploadInfo.channelID
payload.Encrypted = uploadInfo.encryptFile
}
opts := rest.Opts{
- Method: "PUT",
- Path: "/api/files/" + o.id + "/parts",
+ Method: "PATCH",
+ Path: "/api/files/" + o.id,
NoResponse: true,
}
@@ -725,105 +765,196 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
}
o.modTime = modTime
-
- o.size = src.Size()
+ o.size = size
return nil
}
// ChangeNotify calls the passed function with a path that has had changes.
-// If the implementation uses polling, it should adhere to the given interval.
-//
-// Automatically restarts itself in case of unexpected behavior of the remote.
-//
-// Close the returned channel to stop being notified.
func (f *Fs) ChangeNotify(ctx context.Context, notifyFunc func(string, fs.EntryType), pollIntervalChan <-chan time.Duration) {
go func() {
- processedEventIDs := make(map[string]time.Time)
- var ticker *time.Ticker
- var tickerC <-chan time.Time
for {
select {
+ case <-ctx.Done():
+ return
case pollInterval, ok := <-pollIntervalChan:
if !ok {
- if ticker != nil {
- ticker.Stop()
- }
+ fs.Debugf(f, "ChangeNotify: channel closed, stopping")
return
}
- if ticker != nil {
- ticker.Stop()
- ticker, tickerC = nil, nil
- }
- if pollInterval != 0 {
- ticker = time.NewTicker(pollInterval)
- tickerC = ticker.C
+ if pollInterval > 0 {
+ fs.Debugf(f, "ChangeNotify: poll interval set but SSE is active, ignoring")
}
- case <-tickerC:
- fs.Debugf(f, "Checking for changes on remote")
- for eventID, timestamp := range processedEventIDs {
- if time.Since(timestamp) > 5*time.Minute {
- delete(processedEventIDs, eventID)
- }
- }
- err := f.changeNotifyRunner(ctx, notifyFunc, processedEventIDs)
+ default:
+ fs.Debugf(f, "Starting SSE event stream")
+ err := f.changeNotifySSE(ctx, notifyFunc)
if err != nil {
- fs.Infof(f, "Change notify listener failure: %s", err)
+ fs.Infof(f, "SSE connection failed permanently: %s", err)
+ return
}
}
}
}()
}
-func (f *Fs) changeNotifyRunner(ctx context.Context, notifyFunc func(string, fs.EntryType), processedEventIDs map[string]time.Time) error {
+func isFatalError(err error) bool {
+ if err == nil {
+ return false
+ }
+ errStr := err.Error()
+ return strings.Contains(errStr, "401") ||
+ strings.Contains(errStr, "403") ||
+ strings.Contains(errStr, "404")
+}
+
+func (f *Fs) changeNotifySSE(ctx context.Context, notifyFunc func(string, fs.EntryType)) error {
+ for {
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ default:
+ }
- var changes []api.Event
+ var connErr error
+ err := f.ssePacer.Call(func() (bool, error) {
+ connErr = f.connectAndProcessSSE(ctx, notifyFunc)
+ if connErr == nil {
+ return false, nil
+ }
+ if fserrors.ContextError(ctx, &connErr) {
+ return false, connErr
+ }
+ if isFatalError(connErr) {
+ return false, connErr
+ }
+ return true, connErr
+ })
- opts := rest.Opts{
- Method: "GET",
- Path: "/api/events",
+ if err != nil {
+ return err
+ }
+
+ fs.Debugf(f, "SSE connection ended, will retry")
}
+}
- err := f.pacer.Call(func() (bool, error) {
- resp, err := f.srv.CallJSON(ctx, &opts, nil, &changes)
- return shouldRetry(ctx, resp, err)
- })
+func (f *Fs) connectAndProcessSSE(ctx context.Context, notifyFunc func(string, fs.EntryType)) error {
+ opts := rest.Opts{
+ Method: "GET",
+ Path: "/api/events/stream",
+ ContentType: "text/event-stream",
+ ExtraHeaders: map[string]string{
+ "Accept": "text/event-stream",
+ "Cache-Control": "no-cache",
+ },
+ }
+ resp, err := f.srv.Call(ctx, &opts)
if err != nil {
- return err
+ return fmt.Errorf("failed to connect to SSE endpoint: %w", err)
+ }
+ if resp == nil || resp.Body == nil {
+ return fmt.Errorf("no response from SSE endpoint")
}
+ defer resp.Body.Close()
- var pathsToClear []string
- for _, change := range changes {
- if _, ok := processedEventIDs[change.ID]; ok {
- continue
+ contentType := resp.Header.Get("Content-Type")
+ if !strings.Contains(contentType, "text/event-stream") {
+ return fmt.Errorf("unexpected content type: %s", contentType)
+ }
+
+ fs.Debugf(f, "SSE connection established")
+ reader := bufio.NewReader(resp.Body)
+ var eventData strings.Builder
+
+ for {
+
+ select {
+ case <-ctx.Done():
+ return nil
+ default:
}
- addPathToClear := func(parentID string) {
- if path, ok := f.dirCache.GetInv(parentID); ok {
- pathsToClear = append(pathsToClear, path)
+
+ line, err := reader.ReadString('\n')
+ if err != nil {
+ if err == io.EOF {
+ return fmt.Errorf("SSE stream closed by server")
}
+ return fmt.Errorf("error reading SSE stream: %w", err)
}
- // Check original parent location
- addPathToClear(change.Source.ParentId)
+ line = strings.TrimRight(line, "\r\n")
+
+ if line == "" {
+ if eventData.Len() > 0 {
+ data := eventData.String()
+ eventData.Reset()
+
+ if err := f.processSSEEvent(data, notifyFunc); err != nil {
+ fs.Debugf(f, "Failed to process SSE event: %s", err)
+ }
+ }
+ continue
+ }
- // Check destination parent location if file was moved
- if change.Source.DestParentId != "" {
- addPathToClear(change.Source.DestParentId)
+ if strings.HasPrefix(line, "data: ") {
+ eventData.WriteString(line[6:])
}
- processedEventIDs[change.ID] = time.Now()
}
- notifiedPaths := make(map[string]bool)
- for _, path := range pathsToClear {
- if _, ok := notifiedPaths[path]; ok {
- continue
+}
+
+func (f *Fs) processSSEEvent(data string, notifyFunc func(string, fs.EntryType)) error {
+ var event api.Event
+ if err := json.Unmarshal([]byte(data), &event); err != nil {
+ return fmt.Errorf("failed to unmarshal event: %w", err)
+ }
+
+ // Get parent path from cache
+ parentPath, ok := f.dirCache.GetInv(event.Source.ParentId)
+ if !ok {
+ fs.Debugf(f, "SSE: skipping event for uncached parent %s", event.Source.ParentId)
+ return nil
+ }
+
+ fullPath := path.Join(parentPath, event.Source.Name)
+
+ // Filter: only notify for paths within root
+ if !strings.HasPrefix(fullPath, f.root) {
+ return nil
+ }
+
+ var entryType fs.EntryType
+ switch event.Source.Type {
+ case "folder":
+ entryType = fs.EntryDirectory
+ case "file":
+ entryType = fs.EntryObject
+ default:
+ entryType = fs.EntryObject
+ }
+
+ // Handle move events - notify both old and new locations
+ if event.Type == "file_move" && event.Source.DestParentId != "" {
+ if oldParentPath, ok := f.dirCache.GetInv(event.Source.DestParentId); ok {
+ oldPath := path.Join(oldParentPath, event.Source.Name)
+ if strings.HasPrefix(oldPath, f.root) {
+ fs.Debugf(f, "SSE move event: old path %s", oldPath)
+ notifyFunc(oldPath, entryType)
+ }
}
- notifiedPaths[path] = true
- notifyFunc(path, fs.EntryDirectory)
}
+
+ fs.Debugf(f, "SSE event: %s (%v, type=%s)", fullPath, entryType, event.Type)
+ notifyFunc(fullPath, entryType)
+
return nil
}
+// PutStream uploads to the remote path with the modTime given of indeterminate size
+func (f *Fs) PutStream(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
+ return f.Put(ctx, in, src, options...)
+}
+
// OpenChunkWriter returns the chunk size and a ChunkWriter
//
// Pass in the remote and the src object
@@ -834,15 +965,23 @@ func (f *Fs) OpenChunkWriter(
src fs.ObjectInfo,
options ...fs.OpenOption) (info fs.ChunkWriterInfo, writer fs.ChunkWriter, err error) {
- if src.Size() <= 0 {
- return info, nil, errCanNotUploadFileWithUnknownSize
- }
-
o := &Object{
fs: f,
remote: remote,
}
+ // If size is unknown, use bufferingChunkWriter that supports out-of-order chunks
+ if src.Size() <= 0 {
+ fs.Debugf(f, "OpenChunkWriter: unknown size, using buffering chunk writer")
+ return fs.ChunkWriterInfo{}, &bufferingChunkWriter{
+ f: f,
+ o: o,
+ src: src,
+ remote: remote,
+ chunks: make(map[int]*chunkFile),
+ }, nil
+ }
+
uploadInfo, err := o.prepareUpload(ctx, src)
if err != nil {
@@ -1096,8 +1235,6 @@ func (f *Fs) PublicLink(ctx context.Context, remote string, expire fs.Duration,
func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (in io.ReadCloser, err error) {
var resp *http.Response
- http := o.fs.srv
-
fs.FixRangeOption(options, o.size)
opts := rest.Opts{
@@ -1105,14 +1242,9 @@ func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (in io.Read
Path: fmt.Sprintf("/api/files/%s/%s", o.id, url.QueryEscape(o.name)),
Options: options,
}
- if !o.fs.opt.ThreadedStreams {
- opts.Parameters = url.Values{
- "download": []string{"1"},
- }
- }
err = o.fs.pacer.Call(func() (bool, error) {
- resp, err = http.Call(ctx, &opts)
+ resp, err = o.fs.srv.Call(ctx, &opts)
return shouldRetry(ctx, resp, err)
})
@@ -1228,8 +1360,36 @@ func (o *Object) Size() int64 {
return o.size
}
-// Hash returns the Md5sum of an object returning a lowercase hex string
func (o *Object) Hash(ctx context.Context, t hash.Type) (string, error) {
+ if t != telDriveHash {
+ return "", hash.ErrUnsupported
+ }
+
+ if o.hash != "" {
+ return o.hash, nil
+ }
+
+ // Fetch from server if not cached
+ var file api.FileInfo
+ opts := rest.Opts{
+ Method: "GET",
+ Path: "/api/files/" + o.id,
+ }
+
+ err := o.fs.pacer.Call(func() (bool, error) {
+ resp, err := o.fs.srv.CallJSON(ctx, &opts, nil, &file)
+ return shouldRetry(ctx, resp, err)
+ })
+
+ if err != nil {
+ return "", fmt.Errorf("failed to get file hash: %w", err)
+ }
+
+ if file.Hash != "" {
+ o.hash = file.Hash
+ return o.hash, nil
+ }
+
return "", hash.ErrUnsupported
}
diff --git a/backend/teldrive/teldrive_test.go b/backend/teldrive/teldrive_test.go
index 98576b13d..e2374d619 100644
--- a/backend/teldrive/teldrive_test.go
+++ b/backend/teldrive/teldrive_test.go
@@ -1,4 +1,4 @@
-package teldrive
+package teldrive
import (
"testing"
diff --git a/backend/teldrive/upload.go b/backend/teldrive/upload.go
index c5ab79846..061b0ae19 100644
--- a/backend/teldrive/upload.go
+++ b/backend/teldrive/upload.go
@@ -1,6 +1,7 @@
-package teldrive
+package teldrive
import (
+ "bytes"
"context"
"crypto/md5"
"encoding/hex"
@@ -8,12 +9,12 @@ import (
"fmt"
"io"
"net/url"
- "sort"
+ "os"
"strconv"
- "sync"
"github.com/google/uuid"
"github.com/rclone/rclone/backend/teldrive/api"
+ "github.com/rclone/rclone/fs/object"
"github.com/rclone/rclone/fs/operations"
"github.com/rclone/rclone/lib/pool"
"github.com/rclone/rclone/lib/rest"
@@ -21,6 +22,10 @@ import (
"github.com/rclone/rclone/fs"
)
+// memoryBufferThreshold is the size limit for memory buffering
+// Files smaller than this will be buffered in memory, larger files use temp file
+const memoryBufferThreshold = 10 * 1024 * 1024 // 10MB
+
type uploadInfo struct {
existingChunks map[int]api.PartFile
uploadID string
@@ -28,19 +33,16 @@ type uploadInfo struct {
encryptFile bool
chunkSize int64
totalChunks int64
- fileChunks []api.FilePart
fileName string
dir string
}
type objectChunkWriter struct {
- size int64
- f *Fs
- src fs.ObjectInfo
- partsToCommitMu sync.Mutex
- partsToCommit []api.PartFile
- o *Object
- uploadInfo *uploadInfo
+ size int64
+ f *Fs
+ src fs.ObjectInfo
+ o *Object
+ uploadInfo *uploadInfo
}
func getMD5Hash(text string) string {
@@ -65,14 +67,10 @@ func (w *objectChunkWriter) WriteChunk(ctx context.Context, chunkNumber int, rea
r.Account(int(existing.Size))
default:
}
- w.addCompletedPart(existing)
return existing.Size, nil
}
- var (
- response api.PartFile
- partName string
- )
+ var partName string
err = w.f.pacer.Call(func() (bool, error) {
@@ -101,6 +99,7 @@ func (w *objectChunkWriter) WriteChunk(ctx context.Context, chunkNumber int, rea
Method: "POST",
Body: reader,
ContentLength: &size,
+ NoResponse: true,
ContentType: "application/octet-stream",
Parameters: url.Values{
"partName": []string{partName},
@@ -111,6 +110,10 @@ func (w *objectChunkWriter) WriteChunk(ctx context.Context, chunkNumber int, rea
},
}
+ if w.f.opt.HashEnabled {
+ opts.Parameters.Set("hashing", "true")
+ }
+
if w.f.opt.UploadHost != "" {
opts.RootURL = w.f.opt.UploadHost + "/api/uploads/" + w.uploadInfo.uploadID
@@ -118,16 +121,13 @@ func (w *objectChunkWriter) WriteChunk(ctx context.Context, chunkNumber int, rea
opts.Path = "/api/uploads/" + w.uploadInfo.uploadID
}
- resp, err := w.f.srv.CallJSON(ctx, &opts, nil, &response)
+ resp, err := w.f.srv.Call(ctx, &opts)
retry, err := shouldRetry(ctx, resp, err)
if err != nil {
fs.Debugf(w.o, "Error sending chunk %d (retry=%v): %v: %#v", chunkNumber, retry, err, err)
}
- if response.PartId == 0 {
- return true, fmt.Errorf("error sending chunk %d", chunkNumber)
- }
return retry, err
@@ -137,45 +137,269 @@ func (w *objectChunkWriter) WriteChunk(ctx context.Context, chunkNumber int, rea
return 0, fmt.Errorf("error sending chunk %d: %v", chunkNumber, err)
}
- w.addCompletedPart(response)
fs.Debugf(w.o, "Done sending chunk %d", chunkNumber)
return size, err
}
-// add a part number and etag to the completed parts
-func (w *objectChunkWriter) addCompletedPart(part api.PartFile) {
- w.partsToCommitMu.Lock()
- defer w.partsToCommitMu.Unlock()
- w.partsToCommit = append(w.partsToCommit, part)
+func (w *objectChunkWriter) Close(ctx context.Context) error {
+
+ return w.o.createFile(ctx, w.src, w.uploadInfo)
}
-func (w *objectChunkWriter) Close(ctx context.Context) error {
+func (*objectChunkWriter) Abort(ctx context.Context) error {
+ return nil
+}
- if w.uploadInfo.totalChunks != int64(len(w.partsToCommit)) {
- return fmt.Errorf("uploaded failed")
+// chunkFile stores a single chunk's temp file path and size
+type chunkFile struct {
+ tempPath string
+ size int64
+}
+
+// bufferingChunkWriter handles uploads with unknown size by buffering chunks to temp files
+// Used by OpenChunkWriter for streaming uploads
+// Supports out-of-order chunks - stores each chunk in separate temp file and reassembles in order at Close()
+type bufferingChunkWriter struct {
+ f *Fs
+ o *Object
+ src fs.ObjectInfo
+ remote string
+ chunks map[int]*chunkFile // Store temp file paths by chunk number
+ totalSize int64
+ maxChunk int // Track highest chunk number seen
+}
+
+// WriteChunk stores a chunk to a temp file by its chunk number (supports out-of-order writes)
+func (w *bufferingChunkWriter) WriteChunk(ctx context.Context, chunkNumber int, reader io.ReadSeeker) (size int64, err error) {
+ // Create temp file for this chunk
+ tempFile, err := os.CreateTemp("", fmt.Sprintf("rclone-teldrive-chunk-%d-*", chunkNumber))
+ if err != nil {
+ return 0, fmt.Errorf("failed to create temp file for chunk %d: %w", chunkNumber, err)
}
- sort.Slice(w.partsToCommit, func(i, j int) bool {
- return w.partsToCommit[i].PartNo < w.partsToCommit[j].PartNo
- })
+ tempPath := tempFile.Name()
- fileChunks := []api.FilePart{}
+ // Copy data to temp file
+ size, err = io.Copy(tempFile, reader)
+ if err != nil {
+ tempFile.Close()
+ _ = os.Remove(tempPath)
+ return 0, fmt.Errorf("failed to write chunk %d to temp file: %w", chunkNumber, err)
+ }
- for _, part := range w.partsToCommit {
- fileChunks = append(fileChunks, api.FilePart{ID: part.PartId, Salt: part.Salt})
+ // Close temp file (we'll reopen for reading in Close)
+ if err := tempFile.Close(); err != nil {
+ _ = os.Remove(tempPath)
+ return 0, fmt.Errorf("failed to close temp file for chunk %d: %w", chunkNumber, err)
}
- w.uploadInfo.fileChunks = fileChunks
+ // Store chunk info (file will be deleted in Close or Abort)
+ w.chunks[chunkNumber] = &chunkFile{
+ tempPath: tempPath,
+ size: size,
+ }
+ w.totalSize += size
- return w.o.createFile(ctx, w.src, w.uploadInfo)
+ // Track highest chunk number
+ if chunkNumber > w.maxChunk {
+ w.maxChunk = chunkNumber
+ }
+
+ fs.Debugf(w.f, "Buffered chunk %d: %d bytes to temp file %s", chunkNumber, size, tempPath)
+ return size, nil
}
-func (*objectChunkWriter) Abort(ctx context.Context) error {
+// Close reassembles all chunks in order and uploads to TelDrive
+func (w *bufferingChunkWriter) Close(ctx context.Context) error {
+ fs.Debugf(w.f, "Closing bufferingChunkWriter: %d chunks, total size %d bytes", len(w.chunks), w.totalSize)
+
+ // Create a reader that yields chunks in order (0, 1, 2, ...)
+ chunkReader := &orderedChunkReader{
+ chunks: w.chunks,
+ maxChunk: w.maxChunk,
+ current: 0,
+ }
+
+ // Create source info with known size
+ src := object.NewStaticObjectInfo(w.remote, w.src.ModTime(ctx), w.totalSize, false, nil, w.f)
+
+ // Upload using the ordered chunk reader
+ uploadInfo, err := w.o.uploadMultipart(ctx, chunkReader, src)
+ if err != nil {
+ return fmt.Errorf("failed to upload buffered chunks: %w", err)
+ }
+
+ // Clean up temp files
+ chunkReader.cleanup()
+ w.chunks = nil
+
+ return w.o.createFile(ctx, src, uploadInfo)
+}
+
+// Abort cleans up temp files
+func (w *bufferingChunkWriter) Abort(ctx context.Context) error {
+ for _, chunk := range w.chunks {
+ if chunk.tempPath != "" {
+ _ = os.Remove(chunk.tempPath)
+ }
+ }
+ w.chunks = nil
return nil
}
+// orderedChunkReader reads chunks in order (0, 1, 2, ...) from temp files
+type orderedChunkReader struct {
+ chunks map[int]*chunkFile
+ maxChunk int
+ current int
+ file *os.File
+ remaining int64
+}
+
+func (r *orderedChunkReader) Read(p []byte) (n int, err error) {
+ for r.current <= r.maxChunk {
+ // Open next chunk file if needed
+ if r.file == nil {
+ chunk, ok := r.chunks[r.current]
+ if !ok {
+ // Skip missing chunks (shouldn't happen in normal operation)
+ r.current++
+ continue
+ }
+
+ file, err := os.Open(chunk.tempPath)
+ if err != nil {
+ return 0, fmt.Errorf("failed to open chunk %d temp file: %w", r.current, err)
+ }
+ r.file = file
+ r.remaining = chunk.size
+ }
+
+ // Read from current chunk file
+ n, err = r.file.Read(p)
+ if n > 0 {
+ r.remaining -= int64(n)
+ if r.remaining <= 0 {
+ // Finished this chunk, close and move to next
+ r.file.Close()
+ r.file = nil
+ r.current++
+ }
+ return n, nil
+ }
+ if err != nil && err != io.EOF {
+ return 0, err
+ }
+
+ // EOF on this chunk, close and move to next
+ r.file.Close()
+ r.file = nil
+ r.current++
+ }
+
+ return 0, io.EOF
+}
+
+func (r *orderedChunkReader) cleanup() {
+ if r.file != nil {
+ r.file.Close()
+ }
+ for _, chunk := range r.chunks {
+ if chunk.tempPath != "" {
+ _ = os.Remove(chunk.tempPath)
+ }
+ }
+}
+
+// uploadWithBuffering buffers data to memory or temp file for unknown-sized uploads
+// Returns the uploadInfo, final size, and any error
+func (o *Object) uploadWithBuffering(ctx context.Context, in io.Reader, src fs.ObjectInfo) (*uploadInfo, int64, error) {
+ var buffer bytes.Buffer
+ var tempFile *os.File
+ var written int64
+
+ // Read data in chunks
+ buf := make([]byte, 64*1024) // 64KB read buffer
+ for {
+ n, err := in.Read(buf)
+ if n > 0 {
+ // Check if we need to switch to temp file
+ if tempFile == nil && written+int64(n) > memoryBufferThreshold {
+ fs.Debugf(o, "Buffering: switching to temp file at %d bytes", written+int64(n))
+ tempFile, err = os.CreateTemp("", "rclone-teldrive-*")
+ if err != nil {
+ return nil, 0, fmt.Errorf("failed to create temp file: %w", err)
+ }
+ _ = os.Remove(tempFile.Name()) // Delete immediately (Unix trick)
+
+ // Copy existing buffer to temp file
+ if buffer.Len() > 0 {
+ _, err = tempFile.Write(buffer.Bytes())
+ if err != nil {
+ tempFile.Close()
+ return nil, 0, fmt.Errorf("failed to copy buffer to temp file: %w", err)
+ }
+ buffer = bytes.Buffer{} // Free memory
+ }
+ }
+
+ // Write to appropriate target
+ if tempFile != nil {
+ _, err = tempFile.Write(buf[:n])
+ if err != nil {
+ tempFile.Close()
+ return nil, 0, fmt.Errorf("failed to write to temp file: %w", err)
+ }
+ } else {
+ buffer.Write(buf[:n])
+ }
+ written += int64(n)
+ }
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ if tempFile != nil {
+ tempFile.Close()
+ }
+ return nil, 0, fmt.Errorf("failed to read input: %w", err)
+ }
+ }
+
+ // Now upload with known size
+ var uploadInfo *uploadInfo
+ var err error
+
+ if tempFile != nil {
+ // Upload from temp file
+ defer func() {
+ _ = tempFile.Close()
+ }()
+
+ _, err = tempFile.Seek(0, io.SeekStart)
+ if err != nil {
+ return nil, 0, fmt.Errorf("failed to seek temp file: %w", err)
+ }
+
+ fs.Debugf(o, "Uploading %d bytes from temp file", written)
+ srcWithSize := object.NewStaticObjectInfo(src.Remote(), src.ModTime(ctx), written, false, nil, o.fs)
+ uploadInfo, err = o.uploadMultipart(ctx, tempFile, srcWithSize)
+ } else {
+ // Upload from memory buffer
+ fs.Debugf(o, "Uploading %d bytes from memory buffer", written)
+ srcWithSize := object.NewStaticObjectInfo(src.Remote(), src.ModTime(ctx), written, false, nil, o.fs)
+ uploadInfo, err = o.uploadMultipart(ctx, bytes.NewReader(buffer.Bytes()), srcWithSize)
+ }
+
+ if err != nil {
+ return nil, 0, err
+ }
+
+ return uploadInfo, written, nil
+}
+
func (o *Object) prepareUpload(ctx context.Context, src fs.ObjectInfo) (*uploadInfo, error) {
leaf, directoryID, err := o.fs.dirCache.FindPath(ctx, src.Remote(), true)
@@ -291,6 +515,7 @@ func (o *Object) uploadMultipart(ctx context.Context, in io.Reader, src fs.Objec
opts := rest.Opts{
Method: "POST",
Body: partReader,
+ NoResponse: true,
ContentLength: &n,
ContentType: "application/octet-stream",
Parameters: url.Values{
@@ -302,39 +527,24 @@ func (o *Object) uploadMultipart(ctx context.Context, in io.Reader, src fs.Objec
},
}
+ if o.fs.opt.HashEnabled {
+ opts.Parameters.Set("hashing", "true")
+ }
+
if o.fs.opt.UploadHost != "" {
opts.RootURL = o.fs.opt.UploadHost + "/api/uploads/" + uploadInfo.uploadID
} else {
opts.Path = "/api/uploads/" + uploadInfo.uploadID
}
-
- var partInfo api.PartFile
-
- _, err := o.fs.srv.CallJSON(ctx, &opts, nil, &partInfo)
-
+ _, err := o.fs.srv.Call(ctx, &opts)
if err != nil {
return nil, err
}
-
uploadedSize += n
-
- partsToCommit = append(partsToCommit, partInfo)
}
- sort.Slice(partsToCommit, func(i, j int) bool {
- return partsToCommit[i].PartNo < partsToCommit[j].PartNo
- })
-
- fileChunks := []api.FilePart{}
-
- for _, part := range partsToCommit {
- fileChunks = append(fileChunks, api.FilePart{ID: part.PartId, Salt: part.Salt})
- }
-
- uploadInfo.fileChunks = fileChunks
}
-
return uploadInfo, nil
}
@@ -353,7 +563,7 @@ func (o *Object) createFile(ctx context.Context, src fs.ObjectInfo, uploadInfo *
ParentId: uploadInfo.dir,
MimeType: fs.MimeType(ctx, src),
Size: src.Size(),
- Parts: uploadInfo.fileChunks,
+ UploadId: uploadInfo.uploadID,
ChannelID: uploadInfo.channelID,
Encrypted: uploadInfo.encryptFile,
ModTime: src.ModTime(ctx).UTC(),
@@ -367,20 +577,6 @@ func (o *Object) createFile(ctx context.Context, src fs.ObjectInfo, uploadInfo *
if err != nil {
return err
}
- if src.Size() > 0 {
- opts = rest.Opts{
- Method: "DELETE",
- Path: "/api/uploads/" + uploadInfo.uploadID,
- NoResponse: true,
- }
- err = o.fs.pacer.Call(func() (bool, error) {
- resp, err := o.fs.srv.Call(ctx, &opts)
- return shouldRetry(ctx, resp, err)
- })
- if err != nil {
- return err
- }
- }
return nil
}
diff --git a/backend/terabox/api.go b/backend/terabox/api.go
index e2bd9d58c..01c91b7df 100644
--- a/backend/terabox/api.go
+++ b/backend/terabox/api.go
@@ -1,4 +1,4 @@
-package terabox
+package terabox
import (
"bytes"
diff --git a/backend/terabox/api/errors.go b/backend/terabox/api/errors.go
index 77dc466b9..655a4186d 100644
--- a/backend/terabox/api/errors.go
+++ b/backend/terabox/api/errors.go
@@ -1,4 +1,4 @@
-package api
+package api
import (
"fmt"
diff --git a/backend/terabox/api/types.go b/backend/terabox/api/types.go
index 5335aecfe..9943448bf 100644
--- a/backend/terabox/api/types.go
+++ b/backend/terabox/api/types.go
@@ -1,4 +1,4 @@
-// Package api provides types used by the Terabox API.
+// Package api provides types used by the Terabox API.
package api
// ResponseDefault - check only error
diff --git a/backend/terabox/terabox.go b/backend/terabox/terabox.go
index d6ee62187..79ea32c0f 100644
--- a/backend/terabox/terabox.go
+++ b/backend/terabox/terabox.go
@@ -1,4 +1,4 @@
-// Package terabox provides an interface to the Terabox storage system.
+// Package terabox provides an interface to the Terabox storage system.
//
// resources for implementation:
// https://github.com/ivansaul/terabox_downloader
diff --git a/backend/terabox/terabox_test.go b/backend/terabox/terabox_test.go
index 2de5bc88c..b50655b05 100644
--- a/backend/terabox/terabox_test.go
+++ b/backend/terabox/terabox_test.go
@@ -1,4 +1,4 @@
-// Test Terabox filesystem interface
+// Test Terabox filesystem interface
package terabox_test
import (
diff --git a/backend/terabox/util.go b/backend/terabox/util.go
index c251a753d..7e6947b74 100644
--- a/backend/terabox/util.go
+++ b/backend/terabox/util.go
@@ -1,4 +1,4 @@
-package terabox
+package terabox
import (
"encoding/base64"
diff --git a/backend/ulozto/api/types.go b/backend/ulozto/api/types.go
index f649f1146..b6be0b68a 100644
--- a/backend/ulozto/api/types.go
+++ b/backend/ulozto/api/types.go
@@ -1,4 +1,4 @@
-// Package api has type definitions for uloz.to
+// Package api has type definitions for uloz.to
package api
import (
diff --git a/backend/ulozto/ulozto.go b/backend/ulozto/ulozto.go
index ec2f2f6b7..0206a336a 100644
--- a/backend/ulozto/ulozto.go
+++ b/backend/ulozto/ulozto.go
@@ -1,4 +1,4 @@
-// Package ulozto provides an interface to the Uloz.to storage system.
+// Package ulozto provides an interface to the Uloz.to storage system.
package ulozto
import (
diff --git a/backend/ulozto/ulozto_test.go b/backend/ulozto/ulozto_test.go
index 8a7a54173..e90fc0a80 100644
--- a/backend/ulozto/ulozto_test.go
+++ b/backend/ulozto/ulozto_test.go
@@ -1,4 +1,4 @@
-package ulozto
+package ulozto
import (
"context"
diff --git a/backend/union/common/options.go b/backend/union/common/options.go
index 05d6cb8e4..d47d63985 100644
--- a/backend/union/common/options.go
+++ b/backend/union/common/options.go
@@ -1,4 +1,4 @@
-// Package common defines code common to the union and the policies
+// Package common defines code common to the union and the policies
//
// These need to be defined in a separate package to avoid import loops
package common //nolint:revive // Don't include revive when running golangci-lint because this triggers var-naming: avoid meaningless package names
diff --git a/backend/union/entry.go b/backend/union/entry.go
index 20a7d246c..6e130a4a2 100644
--- a/backend/union/entry.go
+++ b/backend/union/entry.go
@@ -1,4 +1,4 @@
-package union
+package union
import (
"context"
diff --git a/backend/union/errors.go b/backend/union/errors.go
index ddfd2866d..371e24b3b 100644
--- a/backend/union/errors.go
+++ b/backend/union/errors.go
@@ -1,4 +1,4 @@
-package union
+package union
import (
"bytes"
diff --git a/backend/union/errors_test.go b/backend/union/errors_test.go
index 9cd6a2a4b..f345484b0 100644
--- a/backend/union/errors_test.go
+++ b/backend/union/errors_test.go
@@ -1,4 +1,4 @@
-package union
+package union
import (
"errors"
diff --git a/backend/union/policy/all.go b/backend/union/policy/all.go
index fe43654ff..3384229fb 100644
--- a/backend/union/policy/all.go
+++ b/backend/union/policy/all.go
@@ -1,4 +1,4 @@
-// Package policy provides utilities for the union implementation.
+// Package policy provides utilities for the union implementation.
package policy
import (
diff --git a/backend/union/policy/epall.go b/backend/union/policy/epall.go
index 24ce7c6b2..aaa4de2c6 100644
--- a/backend/union/policy/epall.go
+++ b/backend/union/policy/epall.go
@@ -1,4 +1,4 @@
-package policy
+package policy
import (
"context"
diff --git a/backend/union/policy/epff.go b/backend/union/policy/epff.go
index e984bf2fd..eed8308a4 100644
--- a/backend/union/policy/epff.go
+++ b/backend/union/policy/epff.go
@@ -1,4 +1,4 @@
-package policy
+package policy
import (
"context"
@@ -21,7 +21,6 @@ func (p *EpFF) epff(ctx context.Context, upstreams []*upstream.Fs, filePath stri
ctx, cancel := context.WithCancel(ctx)
defer cancel()
for _, u := range upstreams {
- u := u // Closure
go func() {
rfs := u.RootFs
remote := path.Join(u.RootPath, filePath)
diff --git a/backend/union/policy/eplfs.go b/backend/union/policy/eplfs.go
index 28d84ff5c..604965836 100644
--- a/backend/union/policy/eplfs.go
+++ b/backend/union/policy/eplfs.go
@@ -1,4 +1,4 @@
-package policy
+package policy
import (
"context"
diff --git a/backend/union/policy/eplno.go b/backend/union/policy/eplno.go
index bb74ba500..2e85e1d19 100644
--- a/backend/union/policy/eplno.go
+++ b/backend/union/policy/eplno.go
@@ -1,4 +1,4 @@
-package policy
+package policy
import (
"context"
diff --git a/backend/union/policy/eplus.go b/backend/union/policy/eplus.go
index 57f97c231..c09dcd3a1 100644
--- a/backend/union/policy/eplus.go
+++ b/backend/union/policy/eplus.go
@@ -1,4 +1,4 @@
-package policy
+package policy
import (
"context"
diff --git a/backend/union/policy/epmfs.go b/backend/union/policy/epmfs.go
index 915f9a02a..f4a45afe3 100644
--- a/backend/union/policy/epmfs.go
+++ b/backend/union/policy/epmfs.go
@@ -1,4 +1,4 @@
-package policy
+package policy
import (
"context"
diff --git a/backend/union/policy/eprand.go b/backend/union/policy/eprand.go
index dea64b76e..321f3735e 100644
--- a/backend/union/policy/eprand.go
+++ b/backend/union/policy/eprand.go
@@ -1,4 +1,4 @@
-package policy
+package policy
import (
"context"
diff --git a/backend/union/policy/ff.go b/backend/union/policy/ff.go
index 9899d5d74..8fe6aa65a 100644
--- a/backend/union/policy/ff.go
+++ b/backend/union/policy/ff.go
@@ -1,4 +1,4 @@
-package policy
+package policy
import (
"context"
diff --git a/backend/union/policy/lfs.go b/backend/union/policy/lfs.go
index 972906ee1..70de39dab 100644
--- a/backend/union/policy/lfs.go
+++ b/backend/union/policy/lfs.go
@@ -1,4 +1,4 @@
-package policy
+package policy
import (
"context"
diff --git a/backend/union/policy/lno.go b/backend/union/policy/lno.go
index 7d9a99d8a..6c18ff18f 100644
--- a/backend/union/policy/lno.go
+++ b/backend/union/policy/lno.go
@@ -1,4 +1,4 @@
-package policy
+package policy
import (
"context"
diff --git a/backend/union/policy/lus.go b/backend/union/policy/lus.go
index 763df8edf..40b0d0ca9 100644
--- a/backend/union/policy/lus.go
+++ b/backend/union/policy/lus.go
@@ -1,4 +1,4 @@
-package policy
+package policy
import (
"context"
diff --git a/backend/union/policy/mfs.go b/backend/union/policy/mfs.go
index 162545a97..25d8cd0ef 100644
--- a/backend/union/policy/mfs.go
+++ b/backend/union/policy/mfs.go
@@ -1,4 +1,4 @@
-package policy
+package policy
import (
"context"
diff --git a/backend/union/policy/newest.go b/backend/union/policy/newest.go
index 8ac8ff639..556250269 100644
--- a/backend/union/policy/newest.go
+++ b/backend/union/policy/newest.go
@@ -1,4 +1,4 @@
-package policy
+package policy
import (
"context"
diff --git a/backend/union/policy/policy.go b/backend/union/policy/policy.go
index cc7623a4d..e6e3daa30 100644
--- a/backend/union/policy/policy.go
+++ b/backend/union/policy/policy.go
@@ -1,4 +1,4 @@
-package policy
+package policy
import (
"context"
diff --git a/backend/union/policy/rand.go b/backend/union/policy/rand.go
index 5c900c0d6..83c375e1d 100644
--- a/backend/union/policy/rand.go
+++ b/backend/union/policy/rand.go
@@ -1,4 +1,4 @@
-package policy
+package policy
import (
"context"
diff --git a/backend/union/union.go b/backend/union/union.go
index 1cb0942da..3e8d49c11 100644
--- a/backend/union/union.go
+++ b/backend/union/union.go
@@ -1,4 +1,4 @@
-// Package union implements a virtual provider to join existing remotes.
+// Package union implements a virtual provider to join existing remotes.
package union
import (
diff --git a/backend/union/union_internal_test.go b/backend/union/union_internal_test.go
index ccc130e8f..6f8acefd2 100644
--- a/backend/union/union_internal_test.go
+++ b/backend/union/union_internal_test.go
@@ -1,4 +1,4 @@
-package union
+package union
import (
"bytes"
diff --git a/backend/union/union_test.go b/backend/union/union_test.go
index 48ab82052..22e3da7a2 100644
--- a/backend/union/union_test.go
+++ b/backend/union/union_test.go
@@ -1,4 +1,4 @@
-// Test Union filesystem interface
+// Test Union filesystem interface
package union_test
import (
diff --git a/backend/union/upstream/upstream.go b/backend/union/upstream/upstream.go
index 747de830d..322735676 100644
--- a/backend/union/upstream/upstream.go
+++ b/backend/union/upstream/upstream.go
@@ -1,4 +1,4 @@
-// Package upstream provides utility functionality to union.
+// Package upstream provides utility functionality to union.
package upstream
import (
diff --git a/backend/uptobox/api/types.go b/backend/uptobox/api/types.go
index 8cf197754..dec084bb7 100644
--- a/backend/uptobox/api/types.go
+++ b/backend/uptobox/api/types.go
@@ -1,4 +1,4 @@
-// Package api provides types used by the Uptobox API.
+// Package api provides types used by the Uptobox API.
package api
import "fmt"
diff --git a/backend/uptobox/uptobox.go b/backend/uptobox/uptobox.go
index ee13d0c67..ce779d723 100644
--- a/backend/uptobox/uptobox.go
+++ b/backend/uptobox/uptobox.go
@@ -1,4 +1,4 @@
-// Package uptobox provides an interface to the Uptobox storage system.
+// Package uptobox provides an interface to the Uptobox storage system.
package uptobox
import (
diff --git a/backend/uptobox/uptobox_test.go b/backend/uptobox/uptobox_test.go
index 4210b884d..8a8b89e6d 100644
--- a/backend/uptobox/uptobox_test.go
+++ b/backend/uptobox/uptobox_test.go
@@ -1,4 +1,4 @@
-// Test Uptobox filesystem interface
+// Test Uptobox filesystem interface
package uptobox_test
import (
diff --git a/backend/webdav/api/types.go b/backend/webdav/api/types.go
index cef1cd701..33c60fc32 100644
--- a/backend/webdav/api/types.go
+++ b/backend/webdav/api/types.go
@@ -1,4 +1,4 @@
-// Package api has type definitions for webdav
+// Package api has type definitions for webdav
package api
import (
@@ -123,7 +123,7 @@ func (p *Prop) Hashes() (hashes map[hash.Type]string) {
hashes = make(map[hash.Type]string)
for _, checksums := range p.Checksums {
checksums = strings.ToLower(checksums)
- for _, checksum := range strings.Split(checksums, " ") {
+ for checksum := range strings.SplitSeq(checksums, " ") {
switch {
case strings.HasPrefix(checksum, "sha1:"):
hashes[hash.SHA1] = checksum[5:]
diff --git a/backend/webdav/chunking.go b/backend/webdav/chunking.go
index ce29507c9..36c0caf85 100644
--- a/backend/webdav/chunking.go
+++ b/backend/webdav/chunking.go
@@ -1,4 +1,4 @@
-package webdav
+package webdav
/*
chunked update for Nextcloud
diff --git a/backend/webdav/odrvcookie/fetch.go b/backend/webdav/odrvcookie/fetch.go
index a1ba6fb73..1fd6866c2 100644
--- a/backend/webdav/odrvcookie/fetch.go
+++ b/backend/webdav/odrvcookie/fetch.go
@@ -1,4 +1,4 @@
-// Package odrvcookie can fetch authentication cookies for a sharepoint webdav endpoint
+// Package odrvcookie can fetch authentication cookies for a sharepoint webdav endpoint
package odrvcookie
import (
diff --git a/backend/webdav/odrvcookie/renew.go b/backend/webdav/odrvcookie/renew.go
index 063bee423..95669eacc 100644
--- a/backend/webdav/odrvcookie/renew.go
+++ b/backend/webdav/odrvcookie/renew.go
@@ -1,4 +1,4 @@
-package odrvcookie
+package odrvcookie
import (
"time"
diff --git a/backend/webdav/tus-errors.go b/backend/webdav/tus-errors.go
index aac7b95a4..9ba5c1823 100644
--- a/backend/webdav/tus-errors.go
+++ b/backend/webdav/tus-errors.go
@@ -1,4 +1,4 @@
-package webdav
+package webdav
import (
"errors"
diff --git a/backend/webdav/tus-upload.go b/backend/webdav/tus-upload.go
index 4b4cc2272..b7b892431 100644
--- a/backend/webdav/tus-upload.go
+++ b/backend/webdav/tus-upload.go
@@ -1,4 +1,4 @@
-package webdav
+package webdav
import (
"bytes"
diff --git a/backend/webdav/tus-uploader.go b/backend/webdav/tus-uploader.go
index 4a0233ccc..1dda175ff 100644
--- a/backend/webdav/tus-uploader.go
+++ b/backend/webdav/tus-uploader.go
@@ -1,4 +1,4 @@
-package webdav
+package webdav
import (
"bytes"
diff --git a/backend/webdav/tus.go b/backend/webdav/tus.go
index 3f6a843bb..36760e97a 100644
--- a/backend/webdav/tus.go
+++ b/backend/webdav/tus.go
@@ -1,4 +1,4 @@
-package webdav
+package webdav
/*
Chunked upload based on the tus protocol for ownCloud Infinite Scale
diff --git a/backend/webdav/webdav.go b/backend/webdav/webdav.go
index 46a91ad01..7e79bd26e 100644
--- a/backend/webdav/webdav.go
+++ b/backend/webdav/webdav.go
@@ -1,4 +1,4 @@
-// Package webdav provides an interface to the Webdav
+// Package webdav provides an interface to the Webdav
// object storage system.
package webdav
diff --git a/backend/webdav/webdav_internal_test.go b/backend/webdav/webdav_internal_test.go
index c2c2d1dfa..bbd4afde5 100644
--- a/backend/webdav/webdav_internal_test.go
+++ b/backend/webdav/webdav_internal_test.go
@@ -1,4 +1,4 @@
-package webdav_test
+package webdav_test
import (
"context"
diff --git a/backend/webdav/webdav_test.go b/backend/webdav/webdav_test.go
index 1949a41c6..4ea8dec80 100644
--- a/backend/webdav/webdav_test.go
+++ b/backend/webdav/webdav_test.go
@@ -1,4 +1,4 @@
-// Test Webdav filesystem interface
+// Test Webdav filesystem interface
package webdav
import (
diff --git a/backend/yandex/api/types.go b/backend/yandex/api/types.go
index b1da038da..042880804 100644
--- a/backend/yandex/api/types.go
+++ b/backend/yandex/api/types.go
@@ -1,4 +1,4 @@
-// Package api provides types used by the Yandex API.
+// Package api provides types used by the Yandex API.
package api
import (
diff --git a/backend/yandex/yandex.go b/backend/yandex/yandex.go
index fc40aa88b..6f90f6e41 100644
--- a/backend/yandex/yandex.go
+++ b/backend/yandex/yandex.go
@@ -1,4 +1,4 @@
-// Package yandex provides an interface to the Yandex storage system.
+// Package yandex provides an interface to the Yandex storage system.
package yandex
import (
diff --git a/backend/yandex/yandex_test.go b/backend/yandex/yandex_test.go
index fa3827d61..78175a830 100644
--- a/backend/yandex/yandex_test.go
+++ b/backend/yandex/yandex_test.go
@@ -1,4 +1,4 @@
-// Test Yandex filesystem interface
+// Test Yandex filesystem interface
package yandex_test
import (
diff --git a/backend/zoho/api/types.go b/backend/zoho/api/types.go
index efbb4c564..0eab2da06 100644
--- a/backend/zoho/api/types.go
+++ b/backend/zoho/api/types.go
@@ -1,4 +1,4 @@
-// Package api provides types used by the Zoho API.
+// Package api provides types used by the Zoho API.
package api
import (
diff --git a/backend/zoho/zoho.go b/backend/zoho/zoho.go
index 30adb413e..d5de14bc6 100644
--- a/backend/zoho/zoho.go
+++ b/backend/zoho/zoho.go
@@ -1,4 +1,4 @@
-// Package zoho provides an interface to the Zoho Workdrive
+// Package zoho provides an interface to the Zoho Workdrive
// storage system.
package zoho
diff --git a/backend/zoho/zoho_test.go b/backend/zoho/zoho_test.go
index 75eb4a95b..dd2edc483 100644
--- a/backend/zoho/zoho_test.go
+++ b/backend/zoho/zoho_test.go
@@ -1,4 +1,4 @@
-// Test Zoho filesystem interface
+// Test Zoho filesystem interface
package zoho_test
import (
diff --git a/bin/check-merged.go b/bin/check-merged.go
index 0132f1263..62299c226 100644
--- a/bin/check-merged.go
+++ b/bin/check-merged.go
@@ -1,4 +1,4 @@
-//go:build ignore
+//go:build ignore
// Attempt to work out if branches have already been merged
package main
diff --git a/bin/convert_go_to_utf8.py b/bin/convert_go_to_utf8.py
new file mode 100644
index 000000000..701bdb87f
--- /dev/null
+++ b/bin/convert_go_to_utf8.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python3
+import subprocess, os, sys
+root = os.getcwd()
+files = subprocess.check_output(["git","ls-files","*.go"], cwd=root).decode().splitlines()
+converted = []
+failed = []
+for f in files:
+ path = os.path.join(root, f)
+ try:
+ data = open(path, 'rb').read()
+ except Exception as e:
+ failed.append((f, str(e)))
+ continue
+ if b'\x00' not in data:
+ continue
+ # try utf-8 first
+ try:
+ text = data.decode('utf-8')
+ # if decoded utf-8 contains NUL characters, treat as failure
+ if "\x00" in text:
+ raise UnicodeDecodeError('utf-8','',0,1,'contains NUL')
+ # already valid utf-8
+ continue
+ except Exception:
+ pass
+ dec_success = False
+ for enc in ('utf-16','utf-16-le','utf-16-be'):
+ try:
+ text = data.decode(enc)
+ # strip trailing BOM if any
+ if text and text[0] == '\ufeff':
+ text = text[1:]
+ # write back as utf-8
+ with open(path, 'w', encoding='utf-8', newline='\n') as w:
+ w.write(text)
+ converted.append(f)
+ dec_success = True
+ break
+ except Exception:
+ continue
+ if not dec_success:
+ failed.append((f, 'could not decode as utf-16'))
+
+print('Converted files:', len(converted))
+for c in converted:
+ print(c)
+if failed:
+ print('\nFailed conversions:', len(failed))
+ for f,err in failed:
+ print(f, err)
diff --git a/bin/cross-compile.go b/bin/cross-compile.go
index 8779eff02..fa6d17ae1 100644
--- a/bin/cross-compile.go
+++ b/bin/cross-compile.go
@@ -1,4 +1,4 @@
-//go:build ignore
+//go:build ignore
// Cross compile rclone - in go because I hate bash ;-)
diff --git a/bin/get-github-release.go b/bin/get-github-release.go
index 64a256b90..71fa6103d 100644
--- a/bin/get-github-release.go
+++ b/bin/get-github-release.go
@@ -1,4 +1,4 @@
-//go:build ignore
+//go:build ignore
// Get the latest release from a github project
//
diff --git a/bin/make_bisync_docs.go b/bin/make_bisync_docs.go
index 3f0dc125c..fbef876c6 100644
--- a/bin/make_bisync_docs.go
+++ b/bin/make_bisync_docs.go
@@ -1,4 +1,4 @@
-//go:build ignore
+//go:build ignore
package main
diff --git a/bin/normalize_go_to_utf8.py b/bin/normalize_go_to_utf8.py
new file mode 100755
index 000000000..79c4f11be
--- /dev/null
+++ b/bin/normalize_go_to_utf8.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python3
+import subprocess,os,sys
+root=os.getcwd()
+files=subprocess.check_output(['git','ls-files','*.go'],cwd=root).decode().splitlines()
+changed=[]
+for f in files:
+ p=os.path.join(root,f)
+ with open(p,'rb') as fh:
+ data=fh.read()
+ text=None
+ try:
+ text=data.decode('utf-8')
+ except Exception:
+ # try utf-16 variants
+ for enc in ('utf-16','utf-16-le','utf-16-be'):
+ try:
+ text=data.decode(enc)
+ break
+ except Exception:
+ continue
+ if text is None:
+ print('Skipping (cannot decode):',f)
+ continue
+ # strip BOM
+ if text and text[0]=='\ufeff':
+ text=text[1:]
+ # normalize newlines to \n
+ text=text.replace('\r\n','\n').replace('\r','\n')
+ # write back as utf-8
+ with open(p,'wb') as fh:
+ fh.write(text.encode('utf-8'))
+ changed.append(f)
+print('Processed files:',len(files),'Rewritten:',len(changed))
+for c in changed[:200]:
+ print(c)
diff --git a/bin/not-in-stable.go b/bin/not-in-stable.go
index 5f988af82..9bc1c6e93 100644
--- a/bin/not-in-stable.go
+++ b/bin/not-in-stable.go
@@ -1,4 +1,4 @@
-// This shows the commits not yet in the stable branch
+// This shows the commits not yet in the stable branch
package main
import (
diff --git a/bin/resource_windows.go b/bin/resource_windows.go
index fab7dcafc..030c0b637 100644
--- a/bin/resource_windows.go
+++ b/bin/resource_windows.go
@@ -1,4 +1,4 @@
-// Utility program to generate Rclone-specific Windows resource system object
+// Utility program to generate Rclone-specific Windows resource system object
// file (.syso), that can be picked up by a following go build for embedding
// version information and icon resources into a rclone binary.
//
diff --git a/bin/rules.go b/bin/rules.go
index 05645adf5..c47b029e1 100644
--- a/bin/rules.go
+++ b/bin/rules.go
@@ -1,4 +1,4 @@
-// Ruleguard file implementing custom linting rules.
+// Ruleguard file implementing custom linting rules.
//
// Note that when used from golangci-lint (using the gocritic linter configured
// with the ruleguard check), because rule files are not handled by
diff --git a/bin/test_independence.go b/bin/test_independence.go
index efe4b7975..9a8cc6c99 100644
--- a/bin/test_independence.go
+++ b/bin/test_independence.go
@@ -1,4 +1,4 @@
-//go:build ignore
+//go:build ignore
// Test that the tests in the suite passed in are independent
diff --git a/cmd/about/about.go b/cmd/about/about.go
index 10b71ac97..585e83dc7 100644
--- a/cmd/about/about.go
+++ b/cmd/about/about.go
@@ -1,4 +1,4 @@
-// Package about provides the about command.
+// Package about provides the about command.
package about
import (
diff --git a/cmd/all/all.go b/cmd/all/all.go
index 00bdbfa8b..7866d218f 100644
--- a/cmd/all/all.go
+++ b/cmd/all/all.go
@@ -1,4 +1,4 @@
-// Package all imports all the commands
+// Package all imports all the commands
package all
import (
diff --git a/cmd/authorize/authorize.go b/cmd/authorize/authorize.go
index 4284aa286..3bc026095 100644
--- a/cmd/authorize/authorize.go
+++ b/cmd/authorize/authorize.go
@@ -1,4 +1,4 @@
-// Package authorize provides the authorize command.
+// Package authorize provides the authorize command.
package authorize
import (
@@ -23,15 +23,15 @@ func init() {
}
var commandDefinition = &cobra.Command{
- Use: "authorize [base64_json_blob | client_id client_secret]",
+ Use: "authorize [base64_json_blob | client_id client_secret]",
Short: `Remote authorization.`,
Long: `Remote authorization. Used to authorize a remote or headless
-rclone from a machine with a browser - use as instructed by
-rclone config.
+rclone from a machine with a browser. Use as instructed by rclone config.
+See also the [remote setup documentation](/remote_setup).
The command requires 1-3 arguments:
-- fs name (e.g., "drive", "s3", etc.)
+- Name of a backend (e.g. "drive", "s3")
- Either a base64 encoded JSON blob obtained from a previous rclone config session
- Or a client_id and client_secret pair obtained from the remote service
diff --git a/cmd/authorize/authorize_test.go b/cmd/authorize/authorize_test.go
index 364da5ed1..af3a16305 100644
--- a/cmd/authorize/authorize_test.go
+++ b/cmd/authorize/authorize_test.go
@@ -1,4 +1,4 @@
-package authorize
+package authorize
import (
"bytes"
@@ -10,7 +10,7 @@ import (
func TestAuthorizeCommand(t *testing.T) {
// Test that the Use string is correctly formatted
- if commandDefinition.Use != "authorize [base64_json_blob | client_id client_secret]" {
+ if commandDefinition.Use != "authorize [base64_json_blob | client_id client_secret]" {
t.Errorf("Command Use string doesn't match expected format: %s", commandDefinition.Use)
}
@@ -26,7 +26,7 @@ func TestAuthorizeCommand(t *testing.T) {
}
helpOutput := buf.String()
- if !strings.Contains(helpOutput, "authorize ") {
+ if !strings.Contains(helpOutput, "authorize ") {
t.Errorf("Help output doesn't contain correct usage information")
}
}
diff --git a/cmd/backend/backend.go b/cmd/backend/backend.go
index 1780dd36b..d0b46d575 100644
--- a/cmd/backend/backend.go
+++ b/cmd/backend/backend.go
@@ -1,4 +1,4 @@
-// Package backend provides the backend command.
+// Package backend provides the backend command.
package backend
import (
diff --git a/cmd/bisync/bilib/canonical.go b/cmd/bisync/bilib/canonical.go
index dac147670..5251e5449 100644
--- a/cmd/bisync/bilib/canonical.go
+++ b/cmd/bisync/bilib/canonical.go
@@ -1,4 +1,4 @@
-// Package bilib provides common stuff for bisync and bisync_test
+// Package bilib provides common stuff for bisync and bisync_test
package bilib
import (
diff --git a/cmd/bisync/bilib/files.go b/cmd/bisync/bilib/files.go
index 0d0134635..73f0457a7 100644
--- a/cmd/bisync/bilib/files.go
+++ b/cmd/bisync/bilib/files.go
@@ -1,4 +1,4 @@
-// Package bilib provides common stuff for bisync and bisync_test
+// Package bilib provides common stuff for bisync and bisync_test
// Here it's got local file/directory helpers (nice to have in lib/file)
package bilib
diff --git a/cmd/bisync/bilib/names.go b/cmd/bisync/bilib/names.go
index cb266eb63..bee2b13a9 100644
--- a/cmd/bisync/bilib/names.go
+++ b/cmd/bisync/bilib/names.go
@@ -1,4 +1,4 @@
-package bilib
+package bilib
import (
"bytes"
diff --git a/cmd/bisync/bilib/output.go b/cmd/bisync/bilib/output.go
index 00ac8d78e..65aeb6057 100644
--- a/cmd/bisync/bilib/output.go
+++ b/cmd/bisync/bilib/output.go
@@ -1,18 +1,22 @@
-// Package bilib provides common stuff for bisync and bisync_test
+// Package bilib provides common stuff for bisync and bisync_test
package bilib
import (
"bytes"
"log/slog"
+ "sync"
"github.com/rclone/rclone/fs/log"
)
// CaptureOutput runs a function capturing its output at log level INFO.
func CaptureOutput(fun func()) []byte {
+ var mu sync.Mutex
buf := &bytes.Buffer{}
oldLevel := log.Handler.SetLevel(slog.LevelInfo)
log.Handler.SetOutput(func(level slog.Level, text string) {
+ mu.Lock()
+ defer mu.Unlock()
buf.WriteString(text)
})
defer func() {
@@ -20,5 +24,7 @@ func CaptureOutput(fun func()) []byte {
log.Handler.SetLevel(oldLevel)
}()
fun()
+ mu.Lock()
+ defer mu.Unlock()
return buf.Bytes()
}
diff --git a/cmd/bisync/bisync_debug_test.go b/cmd/bisync/bisync_debug_test.go
index 45134730d..e5e00993f 100644
--- a/cmd/bisync/bisync_debug_test.go
+++ b/cmd/bisync/bisync_debug_test.go
@@ -1,4 +1,4 @@
-package bisync_test
+package bisync_test
import (
"fmt"
diff --git a/cmd/bisync/bisync_test.go b/cmd/bisync/bisync_test.go
index 0fc680e82..c90fd7cfd 100644
--- a/cmd/bisync/bisync_test.go
+++ b/cmd/bisync/bisync_test.go
@@ -1,4 +1,4 @@
-// TestBisync is a test engine for bisync test cases.
+// TestBisync is a test engine for bisync test cases.
// See https://rclone.org/bisync/#testing for documentation.
// Test cases are organized in subdirs beneath ./testdata
// Results are compared against golden listings and log file.
@@ -522,7 +522,7 @@ func (b *bisyncTest) runTestCase(ctx context.Context, t *testing.T, testCase str
require.NoError(b.t, err)
b.step = 0
b.stopped = false
- for _, line := range strings.Split(string(scenBuf), "\n") {
+ for line := range strings.SplitSeq(string(scenBuf), "\n") {
comment := strings.Index(line, "#")
if comment != -1 {
line = line[:comment]
@@ -936,7 +936,7 @@ func (b *bisyncTest) runTestStep(ctx context.Context, line string) (err error) {
// splitLine splits scenario line into tokens and performs
// substitutions that involve whitespace or control chars.
func splitLine(line string) (args []string) {
- for _, s := range strings.Fields(line) {
+ for s := range strings.FieldsSeq(line) {
b := []byte(whitespaceReplacer.Replace(s))
b = regexChar.ReplaceAllFunc(b, func(b []byte) []byte {
c, _ := strconv.ParseUint(string(b[5:7]), 16, 8)
@@ -1513,7 +1513,7 @@ func (b *bisyncTest) compareResults() int {
fs.Log(nil, divider)
fs.Logf(nil, color(terminal.RedFg, "| MISCOMPARE -Golden vs +Results for %s"), file)
- for _, line := range strings.Split(strings.TrimSpace(text), "\n") {
+ for line := range strings.SplitSeq(strings.TrimSpace(text), "\n") {
fs.Logf(nil, "| %s", strings.TrimSpace(line))
}
}
diff --git a/cmd/bisync/checkfn.go b/cmd/bisync/checkfn.go
index 94fb35471..adc41efbe 100644
--- a/cmd/bisync/checkfn.go
+++ b/cmd/bisync/checkfn.go
@@ -1,4 +1,4 @@
-package bisync
+package bisync
import (
"bytes"
diff --git a/cmd/bisync/cmd.go b/cmd/bisync/cmd.go
index 996ec442c..09194766d 100644
--- a/cmd/bisync/cmd.go
+++ b/cmd/bisync/cmd.go
@@ -1,4 +1,4 @@
-// Package bisync implements bisync
+// Package bisync implements bisync
// Copyright (c) 2017-2020 Chris Nelson
package bisync
diff --git a/cmd/bisync/compare.go b/cmd/bisync/compare.go
index cf69b5d1c..280f73c95 100644
--- a/cmd/bisync/compare.go
+++ b/cmd/bisync/compare.go
@@ -1,4 +1,4 @@
-package bisync
+package bisync
import (
"context"
@@ -219,8 +219,8 @@ func (b *bisyncRun) setFromCompareFlag(ctx context.Context) error {
return nil
}
var CompareFlag CompareOpt // for exclusions
- opts := strings.Split(b.opt.CompareFlag, ",")
- for _, opt := range opts {
+ opts := strings.SplitSeq(b.opt.CompareFlag, ",")
+ for opt := range opts {
switch strings.ToLower(strings.TrimSpace(opt)) {
case "size":
b.opt.Compare.Size = true
diff --git a/cmd/bisync/deltas.go b/cmd/bisync/deltas.go
index fa5ea785e..57f334001 100644
--- a/cmd/bisync/deltas.go
+++ b/cmd/bisync/deltas.go
@@ -1,4 +1,4 @@
-// Package bisync implements bisync
+// Package bisync implements bisync
// Copyright (c) 2017-2020 Chris Nelson
package bisync
diff --git a/cmd/bisync/help.go b/cmd/bisync/help.go
index a9699a848..9eccde8cd 100644
--- a/cmd/bisync/help.go
+++ b/cmd/bisync/help.go
@@ -1,4 +1,4 @@
-package bisync
+package bisync
import (
"strconv"
diff --git a/cmd/bisync/listing.go b/cmd/bisync/listing.go
index b316ceb0d..0588c116f 100644
--- a/cmd/bisync/listing.go
+++ b/cmd/bisync/listing.go
@@ -1,4 +1,4 @@
-// Package bisync implements bisync
+// Package bisync implements bisync
// Copyright (c) 2017-2020 Chris Nelson
package bisync
@@ -707,8 +707,7 @@ func (b *bisyncRun) modifyListing(ctx context.Context, src fs.Fs, dst fs.Fs, res
prettyprint(dstList.list, "dstList", fs.LogLevelDebug)
// clear stats so we only do this once
- accounting.MaxCompletedTransfers = 0
- accounting.Stats(ctx).PruneTransfers()
+ accounting.Stats(ctx).RemoveDoneTransfers()
}
if b.DebugName != "" {
diff --git a/cmd/bisync/lockfile.go b/cmd/bisync/lockfile.go
index e294ff0e8..e392fbec2 100644
--- a/cmd/bisync/lockfile.go
+++ b/cmd/bisync/lockfile.go
@@ -1,4 +1,4 @@
-package bisync
+package bisync
import (
"encoding/json"
diff --git a/cmd/bisync/log.go b/cmd/bisync/log.go
index 64fda49ef..3751de6b2 100644
--- a/cmd/bisync/log.go
+++ b/cmd/bisync/log.go
@@ -1,4 +1,4 @@
-package bisync
+package bisync
import (
"encoding/json"
diff --git a/cmd/bisync/march.go b/cmd/bisync/march.go
index 10e22dc38..ca29bfcc9 100644
--- a/cmd/bisync/march.go
+++ b/cmd/bisync/march.go
@@ -1,4 +1,4 @@
-package bisync
+package bisync
import (
"context"
diff --git a/cmd/bisync/operations.go b/cmd/bisync/operations.go
index e99ead6f8..36de98c18 100644
--- a/cmd/bisync/operations.go
+++ b/cmd/bisync/operations.go
@@ -1,4 +1,4 @@
-// Package bisync implements bisync
+// Package bisync implements bisync
// Copyright (c) 2017-2020 Chris Nelson
// Contributions to original python version: Hildo G. Jr., e2t, kalemas, silenceleaf
package bisync
diff --git a/cmd/bisync/queue.go b/cmd/bisync/queue.go
index ed0257b5d..18e94d78c 100644
--- a/cmd/bisync/queue.go
+++ b/cmd/bisync/queue.go
@@ -1,4 +1,4 @@
-package bisync
+package bisync
import (
"context"
@@ -245,10 +245,8 @@ func (b *bisyncRun) fastCopy(ctx context.Context, fsrc, fdst fs.Fs, files bilib.
}
}
- b.SyncCI = fs.GetConfig(ctxCopy) // allows us to request graceful shutdown
- if accounting.MaxCompletedTransfers != -1 {
- accounting.MaxCompletedTransfers = -1 // we need a complete list in the event of graceful shutdown
- }
+ b.SyncCI = fs.GetConfig(ctxCopy) // allows us to request graceful shutdown
+ accounting.Stats(ctxCopy).SetMaxCompletedTransfers(-1) // we need a complete list in the event of graceful shutdown
ctxCopy, b.CancelSync = context.WithCancel(ctxCopy)
b.testFn()
err := sync.Sync(ctxCopy, fdst, fsrc, b.opt.CreateEmptySrcDirs)
diff --git a/cmd/bisync/rc.go b/cmd/bisync/rc.go
index c0d36a372..bad0c64e9 100644
--- a/cmd/bisync/rc.go
+++ b/cmd/bisync/rc.go
@@ -1,4 +1,4 @@
-package bisync
+package bisync
import (
"context"
diff --git a/cmd/bisync/resolve.go b/cmd/bisync/resolve.go
index 0263201e2..7a1f4a8fe 100644
--- a/cmd/bisync/resolve.go
+++ b/cmd/bisync/resolve.go
@@ -1,4 +1,4 @@
-package bisync
+package bisync
import (
"context"
diff --git a/cmd/bisync/resync.go b/cmd/bisync/resync.go
index 4ed50133e..3d7a5dc69 100644
--- a/cmd/bisync/resync.go
+++ b/cmd/bisync/resync.go
@@ -1,4 +1,4 @@
-package bisync
+package bisync
import (
"context"
diff --git a/cmd/cachestats/cachestats.go b/cmd/cachestats/cachestats.go
index 115bbd534..e13f2b838 100644
--- a/cmd/cachestats/cachestats.go
+++ b/cmd/cachestats/cachestats.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !js
+//go:build !plan9 && !js
// Package cachestats provides the cachestats command.
package cachestats
diff --git a/cmd/cachestats/cachestats_unsupported.go b/cmd/cachestats/cachestats_unsupported.go
index 1ab469e91..12dff9816 100644
--- a/cmd/cachestats/cachestats_unsupported.go
+++ b/cmd/cachestats/cachestats_unsupported.go
@@ -1,4 +1,4 @@
-// Build for cache for unsupported platforms to stop go complaining
+// Build for cache for unsupported platforms to stop go complaining
// about "no buildable Go source files "
//go:build plan9 || js
diff --git a/cmd/cat/cat.go b/cmd/cat/cat.go
index 00701d8df..1271d5bf2 100644
--- a/cmd/cat/cat.go
+++ b/cmd/cat/cat.go
@@ -1,4 +1,4 @@
-// Package cat provides the cat command.
+// Package cat provides the cat command.
package cat
import (
diff --git a/cmd/check/check.go b/cmd/check/check.go
index 3ab5c06ab..de4af2b0d 100644
--- a/cmd/check/check.go
+++ b/cmd/check/check.go
@@ -1,4 +1,4 @@
-// Package check provides the check command.
+// Package check provides the check command.
package check
import (
diff --git a/cmd/checksum/checksum.go b/cmd/checksum/checksum.go
index 9826a3afd..692e86712 100644
--- a/cmd/checksum/checksum.go
+++ b/cmd/checksum/checksum.go
@@ -1,4 +1,4 @@
-// Package checksum provides the checksum command.
+// Package checksum provides the checksum command.
package checksum
import (
diff --git a/cmd/cleanup/cleanup.go b/cmd/cleanup/cleanup.go
index 91a2c7b25..c3c1caae0 100644
--- a/cmd/cleanup/cleanup.go
+++ b/cmd/cleanup/cleanup.go
@@ -1,4 +1,4 @@
-// Package cleanup provides the cleanup command.
+// Package cleanup provides the cleanup command.
package cleanup
import (
diff --git a/cmd/cmd.go b/cmd/cmd.go
index cc485dd4f..f187f2096 100644
--- a/cmd/cmd.go
+++ b/cmd/cmd.go
@@ -1,4 +1,4 @@
-// Package cmd implements the rclone command
+// Package cmd implements the rclone command
//
// It is in a sub package so it's internals can be reused elsewhere
package cmd
diff --git a/cmd/cmount/arch.go b/cmd/cmount/arch.go
index b6503b246..ec9b88e15 100644
--- a/cmd/cmount/arch.go
+++ b/cmd/cmount/arch.go
@@ -1,4 +1,4 @@
-package cmount
+package cmount
// ProvidedBy returns true if the rclone build for the given OS
// provides support for lib/cgo-fuse
diff --git a/cmd/cmount/fs.go b/cmd/cmount/fs.go
index 0dcde3289..512faa84b 100644
--- a/cmd/cmount/fs.go
+++ b/cmd/cmount/fs.go
@@ -1,4 +1,4 @@
-//go:build cmount && ((linux && cgo) || (darwin && cgo) || (freebsd && cgo) || windows)
+//go:build cmount && ((linux && cgo) || (darwin && cgo) || (freebsd && cgo) || windows)
package cmount
diff --git a/cmd/cmount/mount.go b/cmd/cmount/mount.go
index a2ccd4b85..08af1d195 100644
--- a/cmd/cmount/mount.go
+++ b/cmd/cmount/mount.go
@@ -1,4 +1,4 @@
-//go:build cmount && ((linux && cgo) || (darwin && cgo) || (freebsd && cgo) || windows)
+//go:build cmount && ((linux && cgo) || (darwin && cgo) || (freebsd && cgo) || windows)
// Package cmount implements a FUSE mounting system for rclone remotes.
//
diff --git a/cmd/cmount/mount_brew.go b/cmd/cmount/mount_brew.go
index 9a837de7f..dd3b714f4 100644
--- a/cmd/cmount/mount_brew.go
+++ b/cmd/cmount/mount_brew.go
@@ -1,4 +1,4 @@
-//go:build brew && darwin
+//go:build brew && darwin
// Package cmount implements a FUSE mounting system for rclone remotes.
//
diff --git a/cmd/cmount/mount_test.go b/cmd/cmount/mount_test.go
index e0cc1c4ca..2e17758b1 100644
--- a/cmd/cmount/mount_test.go
+++ b/cmd/cmount/mount_test.go
@@ -1,4 +1,4 @@
-//go:build cmount && ((linux && cgo) || (darwin && cgo) || (freebsd && cgo) || windows) && (!race || !windows)
+//go:build cmount && ((linux && cgo) || (darwin && cgo) || (freebsd && cgo) || windows) && (!race || !windows)
// Package cmount implements a FUSE mounting system for rclone remotes.
//
diff --git a/cmd/cmount/mount_unsupported.go b/cmd/cmount/mount_unsupported.go
index 6677132d7..9b90a1599 100644
--- a/cmd/cmount/mount_unsupported.go
+++ b/cmd/cmount/mount_unsupported.go
@@ -1,4 +1,4 @@
-//go:build !((linux && cgo && cmount) || (darwin && cgo && cmount) || (freebsd && cgo && cmount) || (windows && cmount))
+//go:build !((linux && cgo && cmount) || (darwin && cgo && cmount) || (freebsd && cgo && cmount) || (windows && cmount))
// Package cmount implements a FUSE mounting system for rclone remotes.
//
diff --git a/cmd/cmount/mountpoint_other.go b/cmd/cmount/mountpoint_other.go
index e1dc66e2c..c4a2192ca 100644
--- a/cmd/cmount/mountpoint_other.go
+++ b/cmd/cmount/mountpoint_other.go
@@ -1,4 +1,4 @@
-//go:build cmount && cgo && !windows
+//go:build cmount && cgo && !windows
package cmount
diff --git a/cmd/cmount/mountpoint_windows.go b/cmd/cmount/mountpoint_windows.go
index ee7a3bebd..fbfd2bdd9 100644
--- a/cmd/cmount/mountpoint_windows.go
+++ b/cmd/cmount/mountpoint_windows.go
@@ -1,4 +1,4 @@
-//go:build cmount && windows
+//go:build cmount && windows
package cmount
diff --git a/cmd/completion.go b/cmd/completion.go
index 59295fa3b..0eaab3a6d 100644
--- a/cmd/completion.go
+++ b/cmd/completion.go
@@ -1,4 +1,4 @@
-package cmd
+package cmd
import (
"context"
diff --git a/cmd/config/config.go b/cmd/config/config.go
index e9874c29f..5f8c02fdd 100644
--- a/cmd/config/config.go
+++ b/cmd/config/config.go
@@ -1,4 +1,4 @@
-// Package config provides the config command.
+// Package config provides the config command.
package config
import (
@@ -37,6 +37,7 @@ func init() {
configCommand.AddCommand(configDisconnectCommand)
configCommand.AddCommand(configUserInfoCommand)
configCommand.AddCommand(configEncryptionCommand)
+ configCommand.AddCommand(configStringCommand)
}
var configCommand = &cobra.Command{
@@ -613,3 +614,55 @@ If the config file is not encrypted it will return a non zero exit code.`, "|",
return nil
},
}
+
+var configStringCommand = &cobra.Command{
+ Use: "string ",
+ Short: `Print connection string for a single remote.`,
+ Long: strings.ReplaceAll(`Print a connection string for a single remote.
+
+The [connection strings](/docs/#connection-strings) can be used
+wherever a remote is needed and can be more convenient than using the
+config file, especially if using the RC API.
+
+Backend parameters may be provided to the command also.
+
+Example:
+
+|||sh
+$ rclone config string s3:rclone --s3-no-check-bucket
+:s3,access_key_id=XXX,no_check_bucket,provider=AWS,region=eu-west-2,secret_access_key=YYY:rclone
+|||
+
+**NB** the strings are not quoted for use in shells (eg bash,
+powershell, windows cmd). Most will work if enclosed in "double
+quotes", however connection strings that contain double quotes will
+require further quoting which is very shell dependent.
+
+`, "|", "`"),
+ Annotations: map[string]string{
+ "versionIntroduced": "v1.72",
+ },
+ RunE: func(command *cobra.Command, args []string) error {
+ cmd.CheckArgs(1, 1, command, args)
+ remote := args[0]
+ fsInfo, _, fsPath, m, err := fs.ConfigFs(remote)
+ if err != nil {
+ return err
+ }
+
+ // Find the overridden options and construct the string
+ overridden := fsInfo.Options.NonDefault(m)
+ var out strings.Builder
+ out.WriteRune(':')
+ out.WriteString(fsInfo.Name)
+ config := overridden.Human()
+ if config != "" {
+ out.WriteRune(',')
+ out.WriteString(config)
+ }
+ out.WriteRune(':')
+ out.WriteString(fsPath)
+ fmt.Println(out.String())
+ return nil
+ },
+}
diff --git a/cmd/config/config_test.go b/cmd/config/config_test.go
index bc90ff5eb..9eddcf5c3 100644
--- a/cmd/config/config_test.go
+++ b/cmd/config/config_test.go
@@ -1,4 +1,4 @@
-package config
+package config
import (
"fmt"
diff --git a/cmd/convmv/convmv.go b/cmd/convmv/convmv.go
index bb140cfa0..77a4115a1 100644
--- a/cmd/convmv/convmv.go
+++ b/cmd/convmv/convmv.go
@@ -1,4 +1,4 @@
-// Package convmv provides the convmv command.
+// Package convmv provides the convmv command.
package convmv
import (
diff --git a/cmd/convmv/convmv_test.go b/cmd/convmv/convmv_test.go
index 8e0ec4d5d..3dd062332 100644
--- a/cmd/convmv/convmv_test.go
+++ b/cmd/convmv/convmv_test.go
@@ -1,4 +1,4 @@
-// Package convmv provides the convmv command.
+// Package convmv provides the convmv command.
package convmv
import (
@@ -152,7 +152,7 @@ func makeTestFiles(t *testing.T, r *fstest.Run, dir string) []fstest.Item {
items := []fstest.Item{}
for _, c := range alphabet {
var out strings.Builder
- for i := rune(0); i < 7; i++ {
+ for i := range rune(7) {
out.WriteRune(c + i)
}
fileName := path.Join(dir, fmt.Sprintf("%04d-%s.txt", n, out.String()))
diff --git a/cmd/copy/copy.go b/cmd/copy/copy.go
index 217886460..abbbe68c5 100644
--- a/cmd/copy/copy.go
+++ b/cmd/copy/copy.go
@@ -1,4 +1,4 @@
-// Package copy provides the copy command.
+// Package copy provides the copy command.
package copy
import (
diff --git a/cmd/copyto/copyto.go b/cmd/copyto/copyto.go
index 85a533d00..6f82f8712 100644
--- a/cmd/copyto/copyto.go
+++ b/cmd/copyto/copyto.go
@@ -1,4 +1,4 @@
-// Package copyto provides the copyto command.
+// Package copyto provides the copyto command.
package copyto
import (
diff --git a/cmd/copyurl/copyurl.go b/cmd/copyurl/copyurl.go
index 56e51ff03..23f6012e3 100644
--- a/cmd/copyurl/copyurl.go
+++ b/cmd/copyurl/copyurl.go
@@ -1,4 +1,4 @@
-// Package copyurl provides the copyurl command.
+// Package copyurl provides the copyurl command.
package copyurl
import (
diff --git a/cmd/copyurl/copyurl_test.go b/cmd/copyurl/copyurl_test.go
index 1c4e32234..705eb9255 100644
--- a/cmd/copyurl/copyurl_test.go
+++ b/cmd/copyurl/copyurl_test.go
@@ -1,4 +1,4 @@
-package copyurl
+package copyurl
import (
"context"
diff --git a/cmd/cryptcheck/cryptcheck.go b/cmd/cryptcheck/cryptcheck.go
index 13360d512..0236f1ff1 100644
--- a/cmd/cryptcheck/cryptcheck.go
+++ b/cmd/cryptcheck/cryptcheck.go
@@ -1,4 +1,4 @@
-// Package cryptcheck provides the cryptcheck command.
+// Package cryptcheck provides the cryptcheck command.
package cryptcheck
import (
diff --git a/cmd/cryptdecode/cryptdecode.go b/cmd/cryptdecode/cryptdecode.go
index 0d66877ff..b652e050b 100644
--- a/cmd/cryptdecode/cryptdecode.go
+++ b/cmd/cryptdecode/cryptdecode.go
@@ -1,4 +1,4 @@
-// Package cryptdecode provides the cryptdecode command.
+// Package cryptdecode provides the cryptdecode command.
package cryptdecode
import (
diff --git a/cmd/dedupe/dedupe.go b/cmd/dedupe/dedupe.go
index 9905004c3..f1a219da6 100644
--- a/cmd/dedupe/dedupe.go
+++ b/cmd/dedupe/dedupe.go
@@ -1,4 +1,4 @@
-// Package dedupe provides the dedupe command.
+// Package dedupe provides the dedupe command.
package dedupe
import (
diff --git a/cmd/delete/delete.go b/cmd/delete/delete.go
index 65f5b4fcf..880a3d54b 100644
--- a/cmd/delete/delete.go
+++ b/cmd/delete/delete.go
@@ -1,4 +1,4 @@
-// Package delete provides the delete command.
+// Package delete provides the delete command.
package delete
import (
diff --git a/cmd/deletefile/deletefile.go b/cmd/deletefile/deletefile.go
index 652b39c2e..9176fb00e 100644
--- a/cmd/deletefile/deletefile.go
+++ b/cmd/deletefile/deletefile.go
@@ -1,4 +1,4 @@
-// Package deletefile provides the deletefile command.
+// Package deletefile provides the deletefile command.
package deletefile
import (
diff --git a/cmd/genautocomplete/genautocomplete.go b/cmd/genautocomplete/genautocomplete.go
index 1b358dac8..432f24a29 100644
--- a/cmd/genautocomplete/genautocomplete.go
+++ b/cmd/genautocomplete/genautocomplete.go
@@ -1,4 +1,4 @@
-// Package genautocomplete provides the completion command.
+// Package genautocomplete provides the completion command.
package genautocomplete
import (
diff --git a/cmd/genautocomplete/genautocomplete_bash.go b/cmd/genautocomplete/genautocomplete_bash.go
index 28f9a1b59..88baabfa9 100644
--- a/cmd/genautocomplete/genautocomplete_bash.go
+++ b/cmd/genautocomplete/genautocomplete_bash.go
@@ -1,4 +1,4 @@
-package genautocomplete
+package genautocomplete
import (
"fmt"
diff --git a/cmd/genautocomplete/genautocomplete_fish.go b/cmd/genautocomplete/genautocomplete_fish.go
index 20191b759..852c2c2f7 100644
--- a/cmd/genautocomplete/genautocomplete_fish.go
+++ b/cmd/genautocomplete/genautocomplete_fish.go
@@ -1,4 +1,4 @@
-package genautocomplete
+package genautocomplete
import (
"fmt"
diff --git a/cmd/genautocomplete/genautocomplete_powershell.go b/cmd/genautocomplete/genautocomplete_powershell.go
index 7ce27657f..a76acf8c1 100644
--- a/cmd/genautocomplete/genautocomplete_powershell.go
+++ b/cmd/genautocomplete/genautocomplete_powershell.go
@@ -1,4 +1,4 @@
-package genautocomplete
+package genautocomplete
import (
"fmt"
diff --git a/cmd/genautocomplete/genautocomplete_test.go b/cmd/genautocomplete/genautocomplete_test.go
index fab32f3d1..28cd42500 100644
--- a/cmd/genautocomplete/genautocomplete_test.go
+++ b/cmd/genautocomplete/genautocomplete_test.go
@@ -1,4 +1,4 @@
-package genautocomplete
+package genautocomplete
import (
"os"
diff --git a/cmd/genautocomplete/genautocomplete_zsh.go b/cmd/genautocomplete/genautocomplete_zsh.go
index e60589083..18bbe696e 100644
--- a/cmd/genautocomplete/genautocomplete_zsh.go
+++ b/cmd/genautocomplete/genautocomplete_zsh.go
@@ -1,4 +1,4 @@
-package genautocomplete
+package genautocomplete
import (
"fmt"
diff --git a/cmd/gendocs/gendocs.go b/cmd/gendocs/gendocs.go
index 7f2aa0b7b..3c9561273 100644
--- a/cmd/gendocs/gendocs.go
+++ b/cmd/gendocs/gendocs.go
@@ -1,4 +1,4 @@
-// Package gendocs provides the gendocs command.
+// Package gendocs provides the gendocs command.
package gendocs
import (
diff --git a/cmd/gitannex/configparse.go b/cmd/gitannex/configparse.go
index af0f627a7..842b27ffa 100644
--- a/cmd/gitannex/configparse.go
+++ b/cmd/gitannex/configparse.go
@@ -1,4 +1,4 @@
-package gitannex
+package gitannex
import (
"fmt"
diff --git a/cmd/gitannex/e2e_test.go b/cmd/gitannex/e2e_test.go
index be386d447..056b1f771 100644
--- a/cmd/gitannex/e2e_test.go
+++ b/cmd/gitannex/e2e_test.go
@@ -1,4 +1,4 @@
-package gitannex
+package gitannex
import (
"bytes"
@@ -229,7 +229,6 @@ func TestEndToEnd(t *testing.T) {
skipE2eTestIfNecessary(t)
for _, mode := range allLayoutModes() {
- mode := mode
t.Run(string(mode), func(t *testing.T) {
t.Parallel()
@@ -258,7 +257,6 @@ func TestEndToEndMigration(t *testing.T) {
}
for _, mode := range allLayoutModes() {
- mode := mode
t.Run(string(mode), func(t *testing.T) {
t.Parallel()
@@ -318,7 +316,6 @@ func TestEndToEndRepoLayoutCompat(t *testing.T) {
}
for _, mode := range allLayoutModes() {
- mode := mode
t.Run(string(mode), func(t *testing.T) {
t.Parallel()
diff --git a/cmd/gitannex/gitannex.go b/cmd/gitannex/gitannex.go
index 7d9185f03..ae7b955ba 100644
--- a/cmd/gitannex/gitannex.go
+++ b/cmd/gitannex/gitannex.go
@@ -1,4 +1,4 @@
-// Package gitannex provides the "gitannex" command, which enables [git-annex]
+// Package gitannex provides the "gitannex" command, which enables [git-annex]
// to communicate with rclone by implementing the [external special remote
// protocol]. The protocol is line delimited and spoken over stdin and stdout.
//
diff --git a/cmd/gitannex/gitannex_test.go b/cmd/gitannex/gitannex_test.go
index 7a45ee522..c860b7c80 100644
--- a/cmd/gitannex/gitannex_test.go
+++ b/cmd/gitannex/gitannex_test.go
@@ -1,4 +1,4 @@
-package gitannex
+package gitannex
import (
"bufio"
diff --git a/cmd/gitannex/layout.go b/cmd/gitannex/layout.go
index 897cdb5e0..a754c57c0 100644
--- a/cmd/gitannex/layout.go
+++ b/cmd/gitannex/layout.go
@@ -1,4 +1,4 @@
-package gitannex
+package gitannex
import (
"fmt"
diff --git a/cmd/hashsum/hashsum.go b/cmd/hashsum/hashsum.go
index 4d024f9d8..a09bfbea9 100644
--- a/cmd/hashsum/hashsum.go
+++ b/cmd/hashsum/hashsum.go
@@ -1,4 +1,4 @@
-// Package hashsum provides the hashsum command.
+// Package hashsum provides the hashsum command.
package hashsum
import (
diff --git a/cmd/help.go b/cmd/help.go
index ff449b8c8..463e0190e 100644
--- a/cmd/help.go
+++ b/cmd/help.go
@@ -1,4 +1,4 @@
-package cmd
+package cmd
import (
"context"
@@ -344,7 +344,7 @@ func showBackend(name string) {
}
for _, ex := range opt.Examples {
fmt.Printf(" - %s\n", quoteString(ex.Value))
- for _, line := range strings.Split(ex.Help, "\n") {
+ for line := range strings.SplitSeq(ex.Help, "\n") {
fmt.Printf(" - %s\n", line)
}
}
diff --git a/cmd/link/link.go b/cmd/link/link.go
index aa65d11b8..aa98bdc55 100644
--- a/cmd/link/link.go
+++ b/cmd/link/link.go
@@ -1,4 +1,4 @@
-// Package link provides the link command.
+// Package link provides the link command.
package link
import (
diff --git a/cmd/listremotes/listremotes.go b/cmd/listremotes/listremotes.go
index 0a5a98c1e..196b02263 100644
--- a/cmd/listremotes/listremotes.go
+++ b/cmd/listremotes/listremotes.go
@@ -1,4 +1,4 @@
-// Package ls provides the ls command.
+// Package ls provides the ls command.
package ls
import (
diff --git a/cmd/ls/ls.go b/cmd/ls/ls.go
index b850762de..a97eb1cde 100644
--- a/cmd/ls/ls.go
+++ b/cmd/ls/ls.go
@@ -1,4 +1,4 @@
-// Package ls provides the ls command.
+// Package ls provides the ls command.
package ls
import (
diff --git a/cmd/ls/lshelp/lshelp.go b/cmd/ls/lshelp/lshelp.go
index f4cae3d19..6c818afe7 100644
--- a/cmd/ls/lshelp/lshelp.go
+++ b/cmd/ls/lshelp/lshelp.go
@@ -1,4 +1,4 @@
-// Package lshelp provides common help for list commands.
+// Package lshelp provides common help for list commands.
package lshelp
import (
diff --git a/cmd/lsd/lsd.go b/cmd/lsd/lsd.go
index 355a4a6f2..65d9d7064 100644
--- a/cmd/lsd/lsd.go
+++ b/cmd/lsd/lsd.go
@@ -1,4 +1,4 @@
-// Package lsd provides the lsd command.
+// Package lsd provides the lsd command.
package lsd
import (
diff --git a/cmd/lsf/lsf.go b/cmd/lsf/lsf.go
index 38e79d62a..c321c5196 100644
--- a/cmd/lsf/lsf.go
+++ b/cmd/lsf/lsf.go
@@ -1,4 +1,4 @@
-// Package lsf provides the lsf command.
+// Package lsf provides the lsf command.
package lsf
import (
diff --git a/cmd/lsf/lsf_test.go b/cmd/lsf/lsf_test.go
index 886acbcf0..ec2685566 100644
--- a/cmd/lsf/lsf_test.go
+++ b/cmd/lsf/lsf_test.go
@@ -1,4 +1,4 @@
-package lsf
+package lsf
import (
"bytes"
diff --git a/cmd/lsjson/lsjson.go b/cmd/lsjson/lsjson.go
index 9f2390d3e..98c6223f4 100644
--- a/cmd/lsjson/lsjson.go
+++ b/cmd/lsjson/lsjson.go
@@ -1,4 +1,4 @@
-// Package lsjson provides the lsjson command.
+// Package lsjson provides the lsjson command.
package lsjson
import (
diff --git a/cmd/lsl/lsl.go b/cmd/lsl/lsl.go
index cabcef28b..3fce5f447 100644
--- a/cmd/lsl/lsl.go
+++ b/cmd/lsl/lsl.go
@@ -1,4 +1,4 @@
-// Package lsl provides the lsl command.
+// Package lsl provides the lsl command.
package lsl
import (
diff --git a/cmd/md5sum/md5sum.go b/cmd/md5sum/md5sum.go
index b8322c2c4..6317c4902 100644
--- a/cmd/md5sum/md5sum.go
+++ b/cmd/md5sum/md5sum.go
@@ -1,4 +1,4 @@
-// Package md5sum provides the md5sum command.
+// Package md5sum provides the md5sum command.
package md5sum
import (
diff --git a/cmd/mkdir/mkdir.go b/cmd/mkdir/mkdir.go
index 37dfc18f8..ed24f784c 100644
--- a/cmd/mkdir/mkdir.go
+++ b/cmd/mkdir/mkdir.go
@@ -1,4 +1,4 @@
-// Package mkdir provides the mkdir command.
+// Package mkdir provides the mkdir command.
package mkdir
import (
diff --git a/cmd/mount/dir.go b/cmd/mount/dir.go
index ad657bc19..ab91e1cac 100644
--- a/cmd/mount/dir.go
+++ b/cmd/mount/dir.go
@@ -1,4 +1,4 @@
-//go:build linux
+//go:build linux
package mount
diff --git a/cmd/mount/file.go b/cmd/mount/file.go
index 713ae3a9e..88c5755ee 100644
--- a/cmd/mount/file.go
+++ b/cmd/mount/file.go
@@ -1,4 +1,4 @@
-//go:build linux
+//go:build linux
package mount
diff --git a/cmd/mount/fs.go b/cmd/mount/fs.go
index 9872a99e2..d0ee9409d 100644
--- a/cmd/mount/fs.go
+++ b/cmd/mount/fs.go
@@ -1,4 +1,4 @@
-// FUSE main Fs
+// FUSE main Fs
//go:build linux
diff --git a/cmd/mount/handle.go b/cmd/mount/handle.go
index 94f8214c7..efb5f6613 100644
--- a/cmd/mount/handle.go
+++ b/cmd/mount/handle.go
@@ -1,4 +1,4 @@
-//go:build linux
+//go:build linux
package mount
diff --git a/cmd/mount/mount.go b/cmd/mount/mount.go
index 0a1f0f4b3..3e73dc38f 100644
--- a/cmd/mount/mount.go
+++ b/cmd/mount/mount.go
@@ -1,4 +1,4 @@
-//go:build linux
+//go:build linux
// Package mount implements a FUSE mounting system for rclone remotes.
package mount
diff --git a/cmd/mount/mount_test.go b/cmd/mount/mount_test.go
index 0e4a0483d..bc149ceeb 100644
--- a/cmd/mount/mount_test.go
+++ b/cmd/mount/mount_test.go
@@ -1,4 +1,4 @@
-//go:build linux
+//go:build linux
package mount
diff --git a/cmd/mount/mount_unsupported.go b/cmd/mount/mount_unsupported.go
index d896768cd..277775bea 100644
--- a/cmd/mount/mount_unsupported.go
+++ b/cmd/mount/mount_unsupported.go
@@ -1,4 +1,4 @@
-//go:build !linux
+//go:build !linux
// Package mount implements a FUSE mounting system for rclone remotes.
//
diff --git a/cmd/mount/test/seek_speed.go b/cmd/mount/test/seek_speed.go
index 525e35510..48e935737 100644
--- a/cmd/mount/test/seek_speed.go
+++ b/cmd/mount/test/seek_speed.go
@@ -1,4 +1,4 @@
-//go:build ignore
+//go:build ignore
// Read blocks out of a single file to time the seeking code
package main
diff --git a/cmd/mount/test/seeker.go b/cmd/mount/test/seeker.go
index d4a58fb09..6f584e57e 100644
--- a/cmd/mount/test/seeker.go
+++ b/cmd/mount/test/seeker.go
@@ -1,4 +1,4 @@
-//go:build ignore
+//go:build ignore
// Read two files with lots of seeking to stress test the seek code
package main
diff --git a/cmd/mount/test/seekers.go b/cmd/mount/test/seekers.go
index a0433891d..9b102876d 100644
--- a/cmd/mount/test/seekers.go
+++ b/cmd/mount/test/seekers.go
@@ -1,4 +1,4 @@
-//go:build ignore
+//go:build ignore
// Read lots files with lots of simultaneous seeking to stress test the seek code
package main
diff --git a/cmd/mount2/file.go b/cmd/mount2/file.go
index 5e225e4e4..56a8d56d6 100644
--- a/cmd/mount2/file.go
+++ b/cmd/mount2/file.go
@@ -1,4 +1,4 @@
-//go:build linux || (darwin && amd64)
+//go:build linux || (darwin && amd64)
package mount2
diff --git a/cmd/mount2/fs.go b/cmd/mount2/fs.go
index cbd72aad1..f725a6c74 100644
--- a/cmd/mount2/fs.go
+++ b/cmd/mount2/fs.go
@@ -1,4 +1,4 @@
-// FUSE main Fs
+// FUSE main Fs
//go:build linux || (darwin && amd64)
diff --git a/cmd/mount2/mount.go b/cmd/mount2/mount.go
index 012142a29..5d30cfc32 100644
--- a/cmd/mount2/mount.go
+++ b/cmd/mount2/mount.go
@@ -1,4 +1,4 @@
-//go:build linux || (darwin && amd64)
+//go:build linux || (darwin && amd64)
// Package mount2 implements a FUSE mounting system for rclone remotes.
package mount2
diff --git a/cmd/mount2/mount_test.go b/cmd/mount2/mount_test.go
index a5bb9950d..ee30e9320 100644
--- a/cmd/mount2/mount_test.go
+++ b/cmd/mount2/mount_test.go
@@ -1,4 +1,4 @@
-//go:build linux
+//go:build linux
package mount2
diff --git a/cmd/mount2/mount_unsupported.go b/cmd/mount2/mount_unsupported.go
index ab8c5bd0a..dbfaf8602 100644
--- a/cmd/mount2/mount_unsupported.go
+++ b/cmd/mount2/mount_unsupported.go
@@ -1,4 +1,4 @@
-//go:build !linux && (!darwin || !amd64)
+//go:build !linux && (!darwin || !amd64)
// Package mount2 implements a FUSE mounting system for rclone remotes.
//
diff --git a/cmd/mount2/node.go b/cmd/mount2/node.go
index 782e8988b..d4e9af135 100644
--- a/cmd/mount2/node.go
+++ b/cmd/mount2/node.go
@@ -1,4 +1,4 @@
-//go:build linux || (darwin && amd64)
+//go:build linux || (darwin && amd64)
package mount2
diff --git a/cmd/mountlib/check_linux.go b/cmd/mountlib/check_linux.go
index 81c144ee3..24d8f4c62 100644
--- a/cmd/mountlib/check_linux.go
+++ b/cmd/mountlib/check_linux.go
@@ -1,4 +1,4 @@
-//go:build linux
+//go:build linux
package mountlib
diff --git a/cmd/mountlib/check_other.go b/cmd/mountlib/check_other.go
index 82cf5f2fb..f31f6fb29 100644
--- a/cmd/mountlib/check_other.go
+++ b/cmd/mountlib/check_other.go
@@ -1,4 +1,4 @@
-//go:build !linux
+//go:build !linux
package mountlib
diff --git a/cmd/mountlib/mount.go b/cmd/mountlib/mount.go
index 7a7974ba4..02e9fe594 100644
--- a/cmd/mountlib/mount.go
+++ b/cmd/mountlib/mount.go
@@ -1,4 +1,4 @@
-// Package mountlib provides the mount command.
+// Package mountlib provides the mount command.
package mountlib
import (
diff --git a/cmd/mountlib/rc.go b/cmd/mountlib/rc.go
index 67bba5efb..e0395a680 100644
--- a/cmd/mountlib/rc.go
+++ b/cmd/mountlib/rc.go
@@ -1,4 +1,4 @@
-package mountlib
+package mountlib
import (
"context"
diff --git a/cmd/mountlib/rc_test.go b/cmd/mountlib/rc_test.go
index 0ac4d6e53..8f7536e93 100644
--- a/cmd/mountlib/rc_test.go
+++ b/cmd/mountlib/rc_test.go
@@ -1,4 +1,4 @@
-package mountlib_test
+package mountlib_test
import (
"context"
diff --git a/cmd/mountlib/utils.go b/cmd/mountlib/utils.go
index bf3a7edf9..e092eabf4 100644
--- a/cmd/mountlib/utils.go
+++ b/cmd/mountlib/utils.go
@@ -1,4 +1,4 @@
-package mountlib
+package mountlib
import (
"fmt"
diff --git a/cmd/move/move.go b/cmd/move/move.go
index 175346432..246af07da 100644
--- a/cmd/move/move.go
+++ b/cmd/move/move.go
@@ -1,4 +1,4 @@
-// Package move provides the move command.
+// Package move provides the move command.
package move
import (
diff --git a/cmd/moveto/moveto.go b/cmd/moveto/moveto.go
index dd68efa0a..bb91fe286 100644
--- a/cmd/moveto/moveto.go
+++ b/cmd/moveto/moveto.go
@@ -1,4 +1,4 @@
-// Package moveto provides the moveto command.
+// Package moveto provides the moveto command.
package moveto
import (
diff --git a/cmd/ncdu/ncdu.go b/cmd/ncdu/ncdu.go
index bae42a0a5..5d09fadf5 100644
--- a/cmd/ncdu/ncdu.go
+++ b/cmd/ncdu/ncdu.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !js
+//go:build !plan9 && !js
// Package ncdu implements a text based user interface for exploring a remote
package ncdu
diff --git a/cmd/ncdu/ncdu_unsupported.go b/cmd/ncdu/ncdu_unsupported.go
index 34d99ed6c..c4d248826 100644
--- a/cmd/ncdu/ncdu_unsupported.go
+++ b/cmd/ncdu/ncdu_unsupported.go
@@ -1,4 +1,4 @@
-// Build for ncdu for unsupported platforms to stop go complaining
+// Build for ncdu for unsupported platforms to stop go complaining
// about "no buildable Go source files "
//go:build plan9 || js
diff --git a/cmd/ncdu/scan/scan.go b/cmd/ncdu/scan/scan.go
index 97018b3eb..f60eb2ba0 100644
--- a/cmd/ncdu/scan/scan.go
+++ b/cmd/ncdu/scan/scan.go
@@ -1,4 +1,4 @@
-// Package scan does concurrent scanning of an Fs building up a directory tree.
+// Package scan does concurrent scanning of an Fs building up a directory tree.
package scan
import (
diff --git a/cmd/nfsmount/nfsmount.go b/cmd/nfsmount/nfsmount.go
index 2931cdac0..0e8ebb832 100644
--- a/cmd/nfsmount/nfsmount.go
+++ b/cmd/nfsmount/nfsmount.go
@@ -1,4 +1,4 @@
-//go:build unix
+//go:build unix
// Package nfsmount implements mounting functionality using serve nfs command
//
diff --git a/cmd/nfsmount/nfsmount_test.go b/cmd/nfsmount/nfsmount_test.go
index a8402e901..125937526 100644
--- a/cmd/nfsmount/nfsmount_test.go
+++ b/cmd/nfsmount/nfsmount_test.go
@@ -1,4 +1,4 @@
-//go:build unix
+//go:build unix
package nfsmount
diff --git a/cmd/nfsmount/nfsmount_unsupported.go b/cmd/nfsmount/nfsmount_unsupported.go
index 7ff9f1aac..c92fd4a2d 100644
--- a/cmd/nfsmount/nfsmount_unsupported.go
+++ b/cmd/nfsmount/nfsmount_unsupported.go
@@ -1,4 +1,4 @@
-// Build for nfsmount for unsupported platforms to stop go complaining
+// Build for nfsmount for unsupported platforms to stop go complaining
// about "no buildable Go source files "
//go:build !unix
diff --git a/cmd/obscure/obscure.go b/cmd/obscure/obscure.go
index 07754f2de..629ed5964 100644
--- a/cmd/obscure/obscure.go
+++ b/cmd/obscure/obscure.go
@@ -1,4 +1,4 @@
-// Package obscure provides the obscure command.
+// Package obscure provides the obscure command.
package obscure
import (
diff --git a/cmd/progress.go b/cmd/progress.go
index 539f0b801..7ed3ee23c 100644
--- a/cmd/progress.go
+++ b/cmd/progress.go
@@ -1,4 +1,4 @@
-// Show the dynamic progress bar
+// Show the dynamic progress bar
package cmd
diff --git a/cmd/purge/purge.go b/cmd/purge/purge.go
index d6161cc50..8325b96ba 100644
--- a/cmd/purge/purge.go
+++ b/cmd/purge/purge.go
@@ -1,4 +1,4 @@
-// Package purge provides the purge command.
+// Package purge provides the purge command.
package purge
import (
diff --git a/cmd/rc/rc.go b/cmd/rc/rc.go
index 328d5a75f..fc1564421 100644
--- a/cmd/rc/rc.go
+++ b/cmd/rc/rc.go
@@ -1,4 +1,4 @@
-// Package rc provides the rc command.
+// Package rc provides the rc command.
package rc
import (
diff --git a/cmd/rcat/rcat.go b/cmd/rcat/rcat.go
index 29ecbc407..c3bcea7e7 100644
--- a/cmd/rcat/rcat.go
+++ b/cmd/rcat/rcat.go
@@ -1,4 +1,4 @@
-// Package rcat provides the rcat command.
+// Package rcat provides the rcat command.
package rcat
import (
diff --git a/cmd/rcd/rcd.go b/cmd/rcd/rcd.go
index 183a26130..ae68768d7 100644
--- a/cmd/rcd/rcd.go
+++ b/cmd/rcd/rcd.go
@@ -1,4 +1,4 @@
-// Package rcd provides the rcd command.
+// Package rcd provides the rcd command.
package rcd
import (
diff --git a/cmd/reveal/reveal.go b/cmd/reveal/reveal.go
index 0839941bd..2f46ae66d 100644
--- a/cmd/reveal/reveal.go
+++ b/cmd/reveal/reveal.go
@@ -1,4 +1,4 @@
-// Package reveal provides the reveal command.
+// Package reveal provides the reveal command.
package reveal
import (
diff --git a/cmd/rmdir/rmdir.go b/cmd/rmdir/rmdir.go
index a5ea37260..e16eb5aef 100644
--- a/cmd/rmdir/rmdir.go
+++ b/cmd/rmdir/rmdir.go
@@ -1,4 +1,4 @@
-// Package rmdir provides the rmdir command.
+// Package rmdir provides the rmdir command.
package rmdir
import (
diff --git a/cmd/rmdirs/rmdirs.go b/cmd/rmdirs/rmdirs.go
index f8fefffc7..d03c74f40 100644
--- a/cmd/rmdirs/rmdirs.go
+++ b/cmd/rmdirs/rmdirs.go
@@ -1,4 +1,4 @@
-// Package rmdir provides the rmdir command.
+// Package rmdir provides the rmdir command.
package rmdir
import (
diff --git a/cmd/selfupdate/noselfupdate.go b/cmd/selfupdate/noselfupdate.go
index 332353681..2588f1af9 100644
--- a/cmd/selfupdate/noselfupdate.go
+++ b/cmd/selfupdate/noselfupdate.go
@@ -1,4 +1,4 @@
-//go:build noselfupdate
+//go:build noselfupdate
package selfupdate
diff --git a/cmd/selfupdate/selfupdate.go b/cmd/selfupdate/selfupdate.go
index 0f5c9694a..147eb9aed 100644
--- a/cmd/selfupdate/selfupdate.go
+++ b/cmd/selfupdate/selfupdate.go
@@ -1,4 +1,4 @@
-//go:build !noselfupdate
+//go:build !noselfupdate
// Package selfupdate provides the selfupdate command.
package selfupdate
diff --git a/cmd/selfupdate/selfupdate_test.go b/cmd/selfupdate/selfupdate_test.go
index 263f8049c..7f5de2358 100644
--- a/cmd/selfupdate/selfupdate_test.go
+++ b/cmd/selfupdate/selfupdate_test.go
@@ -1,4 +1,4 @@
-//go:build !noselfupdate
+//go:build !noselfupdate
package selfupdate
diff --git a/cmd/selfupdate/verify.go b/cmd/selfupdate/verify.go
index 78ba46f3b..7f607fd43 100644
--- a/cmd/selfupdate/verify.go
+++ b/cmd/selfupdate/verify.go
@@ -1,4 +1,4 @@
-//go:build !noselfupdate
+//go:build !noselfupdate
package selfupdate
diff --git a/cmd/selfupdate/verify_test.go b/cmd/selfupdate/verify_test.go
index 2c8519f40..e77a368a2 100644
--- a/cmd/selfupdate/verify_test.go
+++ b/cmd/selfupdate/verify_test.go
@@ -1,4 +1,4 @@
-//go:build !noselfupdate
+//go:build !noselfupdate
package selfupdate
diff --git a/cmd/selfupdate/writable_unix.go b/cmd/selfupdate/writable_unix.go
index 48c355717..3d5a19435 100644
--- a/cmd/selfupdate/writable_unix.go
+++ b/cmd/selfupdate/writable_unix.go
@@ -1,4 +1,4 @@
-//go:build !windows && !plan9 && !js && !noselfupdate
+//go:build !windows && !plan9 && !js && !noselfupdate
package selfupdate
diff --git a/cmd/selfupdate/writable_unsupported.go b/cmd/selfupdate/writable_unsupported.go
index 0f0380c01..c5f2d3c37 100644
--- a/cmd/selfupdate/writable_unsupported.go
+++ b/cmd/selfupdate/writable_unsupported.go
@@ -1,4 +1,4 @@
-//go:build (plan9 || js) && !noselfupdate
+//go:build (plan9 || js) && !noselfupdate
package selfupdate
diff --git a/cmd/selfupdate/writable_windows.go b/cmd/selfupdate/writable_windows.go
index 6699975e6..1813d5ea6 100644
--- a/cmd/selfupdate/writable_windows.go
+++ b/cmd/selfupdate/writable_windows.go
@@ -1,4 +1,4 @@
-//go:build windows && !noselfupdate
+//go:build windows && !noselfupdate
package selfupdate
diff --git a/cmd/selfupdate_disabled.go b/cmd/selfupdate_disabled.go
index f80078761..8d7b13cc8 100644
--- a/cmd/selfupdate_disabled.go
+++ b/cmd/selfupdate_disabled.go
@@ -1,4 +1,4 @@
-//go:build noselfupdate
+//go:build noselfupdate
package cmd
diff --git a/cmd/selfupdate_enabled.go b/cmd/selfupdate_enabled.go
index bc636aa11..5fcb08d5b 100644
--- a/cmd/selfupdate_enabled.go
+++ b/cmd/selfupdate_enabled.go
@@ -1,4 +1,4 @@
-//go:build !noselfupdate
+//go:build !noselfupdate
package cmd
diff --git a/cmd/serve/dlna/cds.go b/cmd/serve/dlna/cds.go
index 22d9d4d32..b8e799f0c 100644
--- a/cmd/serve/dlna/cds.go
+++ b/cmd/serve/dlna/cds.go
@@ -1,4 +1,4 @@
-package dlna
+package dlna
import (
"context"
diff --git a/cmd/serve/dlna/cds_test.go b/cmd/serve/dlna/cds_test.go
index cc9b9fca2..d7027e398 100644
--- a/cmd/serve/dlna/cds_test.go
+++ b/cmd/serve/dlna/cds_test.go
@@ -1,4 +1,4 @@
-package dlna
+package dlna
import (
"context"
diff --git a/cmd/serve/dlna/cms.go b/cmd/serve/dlna/cms.go
index 1edce9c36..314fe9955 100644
--- a/cmd/serve/dlna/cms.go
+++ b/cmd/serve/dlna/cms.go
@@ -1,4 +1,4 @@
-package dlna
+package dlna
import (
"net/http"
diff --git a/cmd/serve/dlna/data/assets_generate.go b/cmd/serve/dlna/data/assets_generate.go
index 015ca45a8..15986eb49 100644
--- a/cmd/serve/dlna/data/assets_generate.go
+++ b/cmd/serve/dlna/data/assets_generate.go
@@ -1,4 +1,4 @@
-//go:generate go run assets_generate.go
+//go:generate go run assets_generate.go
// The "go:generate" directive compiles static assets by running assets_generate.go
//go:build ignore
diff --git a/cmd/serve/dlna/data/assets_vfsdata.go b/cmd/serve/dlna/data/assets_vfsdata.go
index da6fd39ab..b9109f537 100644
--- a/cmd/serve/dlna/data/assets_vfsdata.go
+++ b/cmd/serve/dlna/data/assets_vfsdata.go
@@ -1,4 +1,4 @@
-// Code generated by vfsgen; DO NOT EDIT.
+// Code generated by vfsgen; DO NOT EDIT.
//go:build !dev
// +build !dev
diff --git a/cmd/serve/dlna/data/data.go b/cmd/serve/dlna/data/data.go
index f0ee92f51..2e64df7e3 100644
--- a/cmd/serve/dlna/data/data.go
+++ b/cmd/serve/dlna/data/data.go
@@ -1,4 +1,4 @@
-// Package data provides utilities for DLNA server.
+// Package data provides utilities for DLNA server.
// The "go:generate" directive compiles static assets by running assets_generate.go
//
//go:generate go run assets_generate.go
diff --git a/cmd/serve/dlna/dlna.go b/cmd/serve/dlna/dlna.go
index 97728c0ba..0f9355b6f 100644
--- a/cmd/serve/dlna/dlna.go
+++ b/cmd/serve/dlna/dlna.go
@@ -1,4 +1,4 @@
-// Package dlna provides DLNA server.
+// Package dlna provides DLNA server.
package dlna
import (
diff --git a/cmd/serve/dlna/dlna_test.go b/cmd/serve/dlna/dlna_test.go
index 6dea80919..cc9a62f80 100644
--- a/cmd/serve/dlna/dlna_test.go
+++ b/cmd/serve/dlna/dlna_test.go
@@ -1,4 +1,4 @@
-package dlna
+package dlna
import (
"bytes"
diff --git a/cmd/serve/dlna/dlna_util.go b/cmd/serve/dlna/dlna_util.go
index 7eabdd50e..2a9a4de64 100644
--- a/cmd/serve/dlna/dlna_util.go
+++ b/cmd/serve/dlna/dlna_util.go
@@ -1,4 +1,4 @@
-package dlna
+package dlna
import (
"context"
diff --git a/cmd/serve/dlna/mrrs.go b/cmd/serve/dlna/mrrs.go
index 70061bd71..213b4b3ee 100644
--- a/cmd/serve/dlna/mrrs.go
+++ b/cmd/serve/dlna/mrrs.go
@@ -1,4 +1,4 @@
-package dlna
+package dlna
import (
"net/http"
diff --git a/cmd/serve/dlna/upnpav/upnpav.go b/cmd/serve/dlna/upnpav/upnpav.go
index c6dc9dc4f..cd196d230 100644
--- a/cmd/serve/dlna/upnpav/upnpav.go
+++ b/cmd/serve/dlna/upnpav/upnpav.go
@@ -1,4 +1,4 @@
-// Package upnpav provides utilities for DLNA server.
+// Package upnpav provides utilities for DLNA server.
package upnpav
import (
diff --git a/cmd/serve/docker/api.go b/cmd/serve/docker/api.go
index 394e61281..7cdb26d92 100644
--- a/cmd/serve/docker/api.go
+++ b/cmd/serve/docker/api.go
@@ -1,4 +1,4 @@
-package docker
+package docker
import (
"encoding/json"
diff --git a/cmd/serve/docker/docker.go b/cmd/serve/docker/docker.go
index 8221a1ac0..7d71bc637 100644
--- a/cmd/serve/docker/docker.go
+++ b/cmd/serve/docker/docker.go
@@ -1,4 +1,4 @@
-// Package docker serves a remote suitable for use with docker volume api
+// Package docker serves a remote suitable for use with docker volume api
package docker
import (
diff --git a/cmd/serve/docker/docker_test.go b/cmd/serve/docker/docker_test.go
index 4c0940a7c..ab2bc5899 100644
--- a/cmd/serve/docker/docker_test.go
+++ b/cmd/serve/docker/docker_test.go
@@ -1,4 +1,4 @@
-//go:build !race
+//go:build !race
package docker_test
diff --git a/cmd/serve/docker/driver.go b/cmd/serve/docker/driver.go
index 804a8bc02..745006665 100644
--- a/cmd/serve/docker/driver.go
+++ b/cmd/serve/docker/driver.go
@@ -1,4 +1,4 @@
-package docker
+package docker
import (
"context"
diff --git a/cmd/serve/docker/options.go b/cmd/serve/docker/options.go
index e45820af7..d7dd66325 100644
--- a/cmd/serve/docker/options.go
+++ b/cmd/serve/docker/options.go
@@ -1,4 +1,4 @@
-package docker
+package docker
import (
"fmt"
diff --git a/cmd/serve/docker/options_test.go b/cmd/serve/docker/options_test.go
index cdbe08f67..4971c8dac 100644
--- a/cmd/serve/docker/options_test.go
+++ b/cmd/serve/docker/options_test.go
@@ -1,4 +1,4 @@
-package docker
+package docker
import (
"testing"
diff --git a/cmd/serve/docker/serve.go b/cmd/serve/docker/serve.go
index 9fa6e5642..9f901e0a4 100644
--- a/cmd/serve/docker/serve.go
+++ b/cmd/serve/docker/serve.go
@@ -1,4 +1,4 @@
-package docker
+package docker
import (
"context"
diff --git a/cmd/serve/docker/systemd.go b/cmd/serve/docker/systemd.go
index 0294d7786..faa8ed7c4 100644
--- a/cmd/serve/docker/systemd.go
+++ b/cmd/serve/docker/systemd.go
@@ -1,4 +1,4 @@
-//go:build linux && !android
+//go:build linux && !android
package docker
diff --git a/cmd/serve/docker/systemd_unsupported.go b/cmd/serve/docker/systemd_unsupported.go
index d11ad52d8..877cda330 100644
--- a/cmd/serve/docker/systemd_unsupported.go
+++ b/cmd/serve/docker/systemd_unsupported.go
@@ -1,4 +1,4 @@
-//go:build !linux || android
+//go:build !linux || android
package docker
diff --git a/cmd/serve/docker/unix.go b/cmd/serve/docker/unix.go
index 4b5da70ba..f016f5cae 100644
--- a/cmd/serve/docker/unix.go
+++ b/cmd/serve/docker/unix.go
@@ -1,4 +1,4 @@
-//go:build linux || freebsd
+//go:build linux || freebsd
package docker
diff --git a/cmd/serve/docker/unix_unsupported.go b/cmd/serve/docker/unix_unsupported.go
index 6adc26065..8d102a167 100644
--- a/cmd/serve/docker/unix_unsupported.go
+++ b/cmd/serve/docker/unix_unsupported.go
@@ -1,4 +1,4 @@
-//go:build !linux && !freebsd
+//go:build !linux && !freebsd
package docker
diff --git a/cmd/serve/docker/volume.go b/cmd/serve/docker/volume.go
index 70414d076..01e554229 100644
--- a/cmd/serve/docker/volume.go
+++ b/cmd/serve/docker/volume.go
@@ -1,4 +1,4 @@
-package docker
+package docker
import (
"context"
diff --git a/cmd/serve/ftp/ftp.go b/cmd/serve/ftp/ftp.go
index e5b9897d8..2e2d11a27 100644
--- a/cmd/serve/ftp/ftp.go
+++ b/cmd/serve/ftp/ftp.go
@@ -1,4 +1,4 @@
-//go:build !plan9
+//go:build !plan9
// Package ftp implements an FTP server for rclone
package ftp
diff --git a/cmd/serve/ftp/ftp_test.go b/cmd/serve/ftp/ftp_test.go
index 83255fc77..b506b7dd4 100644
--- a/cmd/serve/ftp/ftp_test.go
+++ b/cmd/serve/ftp/ftp_test.go
@@ -1,4 +1,4 @@
-// Serve ftp tests set up a server and run the integration tests
+// Serve ftp tests set up a server and run the integration tests
// for the ftp remote against it.
//
// We skip tests on platforms with troublesome character mappings
diff --git a/cmd/serve/ftp/ftp_unsupported.go b/cmd/serve/ftp/ftp_unsupported.go
index f4c5415db..c46feee3d 100644
--- a/cmd/serve/ftp/ftp_unsupported.go
+++ b/cmd/serve/ftp/ftp_unsupported.go
@@ -1,4 +1,4 @@
-// Build for unsupported platforms to stop go complaining
+// Build for unsupported platforms to stop go complaining
// about "no buildable Go source files "
//go:build plan9
diff --git a/cmd/serve/http/http.go b/cmd/serve/http/http.go
index 92f1b02f2..c945f4841 100644
--- a/cmd/serve/http/http.go
+++ b/cmd/serve/http/http.go
@@ -1,4 +1,4 @@
-// Package http provides common functionality for http servers
+// Package http provides common functionality for http servers
package http
import (
@@ -41,9 +41,10 @@ var OptionsInfo = fs.Options{}.
// Options required for http server
type Options struct {
- Auth libhttp.AuthConfig
- HTTP libhttp.Config
- Template libhttp.TemplateConfig
+ Auth libhttp.AuthConfig
+ HTTP libhttp.Config
+ Template libhttp.TemplateConfig
+ DisableZip bool
}
// DefaultOpt is the default values used for Options
@@ -69,6 +70,7 @@ func init() {
flags.AddFlagsFromOptions(flagSet, "", OptionsInfo)
vfsflags.AddFlags(flagSet)
proxyflags.AddFlags(flagSet)
+ flagSet.BoolVar(&Opt.DisableZip, "disable-zip", false, "Disable zip download of directories")
cmdserve.Command.AddCommand(Command)
cmdserve.AddRc("http", func(ctx context.Context, f fs.Fs, in rc.Params) (cmdserve.Handle, error) {
// Read VFS Opts
@@ -208,6 +210,7 @@ func newServer(ctx context.Context, f fs.Fs, opt *Options, vfsOpt *vfscommon.Opt
// Serve HTTP until the server is shutdown
func (s *HTTP) Serve() error {
s.server.Serve()
+ fs.Logf(s.f, "HTTP Server started on %s", s.server.URLs())
s.server.Wait()
return nil
}
@@ -256,6 +259,24 @@ func (s *HTTP) serveDir(w http.ResponseWriter, r *http.Request, dirRemote string
return
}
dir := node.(*vfs.Dir)
+
+ if r.URL.Query().Get("download") == "zip" && !s.opt.DisableZip {
+ fs.Infof(dirRemote, "%s: Zipping directory", r.RemoteAddr)
+ zipName := path.Base(dirRemote)
+ if dirRemote == "" {
+ zipName = "root"
+ }
+ w.Header().Set("Content-Disposition", "attachment; filename=\""+zipName+".zip\"")
+ w.Header().Set("Content-Type", "application/zip")
+ w.Header().Set("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
+ err := vfs.CreateZip(ctx, dir, w)
+ if err != nil {
+ serve.Error(ctx, dirRemote, w, "Failed to create zip", err)
+ return
+ }
+ return
+ }
+
dirEntries, err := dir.ReadDirAll()
if err != nil {
serve.Error(ctx, dirRemote, w, "Failed to list directory", err)
@@ -279,6 +300,8 @@ func (s *HTTP) serveDir(w http.ResponseWriter, r *http.Request, dirRemote string
// Set the Last-Modified header to the timestamp
w.Header().Set("Last-Modified", dir.ModTime().UTC().Format(http.TimeFormat))
+ directory.DisableZip = s.opt.DisableZip
+
directory.Serve(w, r)
}
diff --git a/cmd/serve/http/http_test.go b/cmd/serve/http/http_test.go
index 3044c141a..cfa3005c8 100644
--- a/cmd/serve/http/http_test.go
+++ b/cmd/serve/http/http_test.go
@@ -1,9 +1,10 @@
-package http
+package http
import (
"context"
"flag"
"io"
+ stdfs "io/fs"
"net/http"
"os"
"path/filepath"
@@ -75,6 +76,16 @@ func start(ctx context.Context, t *testing.T, f fs.Fs) (s *HTTP, testURL string)
return s, testURL
}
+// setAllModTimes walks root and sets atime/mtime to t for every file & directory.
+func setAllModTimes(root string, t time.Time) error {
+ return filepath.WalkDir(root, func(path string, d stdfs.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+ return os.Chtimes(path, t, t)
+ })
+}
+
var (
datedObject = "two.txt"
expectedTime = time.Date(2000, 1, 2, 3, 4, 5, 0, time.UTC)
@@ -123,6 +134,8 @@ func testGET(t *testing.T, useProxy bool) {
f = nil
} else {
+ // set all the mod times to expectedTime
+ require.NoError(t, setAllModTimes("testdata/files", expectedTime))
// Create a test Fs
var err error
f, err = fs.NewFs(context.Background(), "testdata/files")
@@ -233,6 +246,16 @@ func testGET(t *testing.T, useProxy bool) {
Range: "bytes=3-",
Golden: "testdata/golden/two3-.txt",
},
+ {
+ URL: "/?download=zip",
+ Status: http.StatusOK,
+ Golden: "testdata/golden/root.zip",
+ },
+ {
+ URL: "/three/?download=zip",
+ Status: http.StatusOK,
+ Golden: "testdata/golden/three.zip",
+ },
} {
method := test.Method
if method == "" {
diff --git a/cmd/serve/http/testdata/golden/root.zip b/cmd/serve/http/testdata/golden/root.zip
new file mode 100644
index 000000000..a3a710df4
Binary files /dev/null and b/cmd/serve/http/testdata/golden/root.zip differ
diff --git a/cmd/serve/http/testdata/golden/three.zip b/cmd/serve/http/testdata/golden/three.zip
new file mode 100644
index 000000000..bcffe9819
Binary files /dev/null and b/cmd/serve/http/testdata/golden/three.zip differ
diff --git a/cmd/serve/nfs/cache.go b/cmd/serve/nfs/cache.go
index 278d923dc..da0199fe4 100644
--- a/cmd/serve/nfs/cache.go
+++ b/cmd/serve/nfs/cache.go
@@ -1,4 +1,4 @@
-//go:build unix
+//go:build unix
package nfs
diff --git a/cmd/serve/nfs/cache_test.go b/cmd/serve/nfs/cache_test.go
index 6941fde17..c940b0bdc 100644
--- a/cmd/serve/nfs/cache_test.go
+++ b/cmd/serve/nfs/cache_test.go
@@ -1,4 +1,4 @@
-//go:build unix
+//go:build unix
package nfs
@@ -66,7 +66,6 @@ func testCacheCRUD(t *testing.T, h *Handler, c Cache, fileName string) {
func testCacheThrashDifferent(t *testing.T, h *Handler, c Cache) {
var wg sync.WaitGroup
for i := range 100 {
- i := i
wg.Add(1)
go func() {
defer wg.Done()
@@ -125,7 +124,6 @@ func TestCache(t *testing.T) {
}()
billyFS := &FS{nil} // place holder billyFS
for _, cacheType := range []handleCache{cacheMemory, cacheDisk, cacheSymlink} {
- cacheType := cacheType
t.Run(cacheType.String(), func(t *testing.T) {
h := &Handler{
vfs: vfs.New(object.MemoryFs, nil),
diff --git a/cmd/serve/nfs/filesystem.go b/cmd/serve/nfs/filesystem.go
index a87563171..f4ed9e863 100644
--- a/cmd/serve/nfs/filesystem.go
+++ b/cmd/serve/nfs/filesystem.go
@@ -1,4 +1,4 @@
-//go:build unix
+//go:build unix
package nfs
diff --git a/cmd/serve/nfs/handler.go b/cmd/serve/nfs/handler.go
index 894f573d7..0cac9ba39 100644
--- a/cmd/serve/nfs/handler.go
+++ b/cmd/serve/nfs/handler.go
@@ -1,4 +1,4 @@
-//go:build unix
+//go:build unix
package nfs
diff --git a/cmd/serve/nfs/nfs.go b/cmd/serve/nfs/nfs.go
index 020c92a60..fb2d54ceb 100644
--- a/cmd/serve/nfs/nfs.go
+++ b/cmd/serve/nfs/nfs.go
@@ -1,4 +1,4 @@
-//go:build unix
+//go:build unix
// Package nfs implements a server to serve a VFS remote over the NFSv3 protocol
//
diff --git a/cmd/serve/nfs/nfs_test.go b/cmd/serve/nfs/nfs_test.go
index 70311574e..78ad5cd05 100644
--- a/cmd/serve/nfs/nfs_test.go
+++ b/cmd/serve/nfs/nfs_test.go
@@ -1,4 +1,4 @@
-//go:build unix
+//go:build unix
// The serving is tested in cmd/nfsmount - here we test anything else
package nfs
diff --git a/cmd/serve/nfs/nfs_unsupported.go b/cmd/serve/nfs/nfs_unsupported.go
index c9b9f5bbf..bfe33584e 100644
--- a/cmd/serve/nfs/nfs_unsupported.go
+++ b/cmd/serve/nfs/nfs_unsupported.go
@@ -1,4 +1,4 @@
-// For unsupported architectures
+// For unsupported architectures
//go:build !unix
// Package nfs is not supported on non-Unix platforms
diff --git a/cmd/serve/nfs/server.go b/cmd/serve/nfs/server.go
index 47584ecd0..999180112 100644
--- a/cmd/serve/nfs/server.go
+++ b/cmd/serve/nfs/server.go
@@ -1,4 +1,4 @@
-//go:build unix
+//go:build unix
package nfs
diff --git a/cmd/serve/nfs/symlink_cache_linux.go b/cmd/serve/nfs/symlink_cache_linux.go
index 806de6685..b165795cf 100644
--- a/cmd/serve/nfs/symlink_cache_linux.go
+++ b/cmd/serve/nfs/symlink_cache_linux.go
@@ -1,4 +1,4 @@
-//go:build unix && linux
+//go:build unix && linux
/*
This implements an efficient disk cache for the NFS file handles for
diff --git a/cmd/serve/nfs/symlink_cache_other.go b/cmd/serve/nfs/symlink_cache_other.go
index faa70606e..feb56a25f 100644
--- a/cmd/serve/nfs/symlink_cache_other.go
+++ b/cmd/serve/nfs/symlink_cache_other.go
@@ -1,4 +1,4 @@
-//go:build unix && !linux
+//go:build unix && !linux
package nfs
diff --git a/cmd/serve/proxy/proxy.go b/cmd/serve/proxy/proxy.go
index 7acc0ce7a..0abed2943 100644
--- a/cmd/serve/proxy/proxy.go
+++ b/cmd/serve/proxy/proxy.go
@@ -1,4 +1,4 @@
-// Package proxy implements a programmable proxy for rclone serve
+// Package proxy implements a programmable proxy for rclone serve
package proxy
import (
@@ -182,7 +182,7 @@ func (p *Proxy) run(in map[string]string) (config configmap.Simple, err error) {
// Obscure any values in the config map that need it
obscureFields, ok := config.Get("_obscure")
if ok {
- for _, key := range strings.Split(obscureFields, ",") {
+ for key := range strings.SplitSeq(obscureFields, ",") {
value, ok := config.Get(key)
if ok {
obscuredValue, err := obscure.Obscure(value)
diff --git a/cmd/serve/proxy/proxy_code.go b/cmd/serve/proxy/proxy_code.go
index cc703e209..fae50e59d 100644
--- a/cmd/serve/proxy/proxy_code.go
+++ b/cmd/serve/proxy/proxy_code.go
@@ -1,4 +1,4 @@
-//go:build ignore
+//go:build ignore
// A simple auth proxy for testing purposes
package main
diff --git a/cmd/serve/proxy/proxy_test.go b/cmd/serve/proxy/proxy_test.go
index 574a7aa15..4046b09cb 100644
--- a/cmd/serve/proxy/proxy_test.go
+++ b/cmd/serve/proxy/proxy_test.go
@@ -1,4 +1,4 @@
-package proxy
+package proxy
import (
"context"
diff --git a/cmd/serve/proxy/proxyflags/proxyflags.go b/cmd/serve/proxy/proxyflags/proxyflags.go
index 1e1334d2b..54ee88505 100644
--- a/cmd/serve/proxy/proxyflags/proxyflags.go
+++ b/cmd/serve/proxy/proxyflags/proxyflags.go
@@ -1,4 +1,4 @@
-// Package proxyflags implements command line flags to set up a proxy
+// Package proxyflags implements command line flags to set up a proxy
package proxyflags
import (
diff --git a/cmd/serve/rc.go b/cmd/serve/rc.go
index 47c46590a..4236dbbeb 100644
--- a/cmd/serve/rc.go
+++ b/cmd/serve/rc.go
@@ -1,4 +1,4 @@
-package serve
+package serve
import (
"cmp"
diff --git a/cmd/serve/rc_test.go b/cmd/serve/rc_test.go
index e432ebfce..85e3841c2 100644
--- a/cmd/serve/rc_test.go
+++ b/cmd/serve/rc_test.go
@@ -1,4 +1,4 @@
-package serve
+package serve
import (
"context"
diff --git a/cmd/serve/restic/cache.go b/cmd/serve/restic/cache.go
index 4d2215129..3b8dcd0af 100644
--- a/cmd/serve/restic/cache.go
+++ b/cmd/serve/restic/cache.go
@@ -1,4 +1,4 @@
-package restic
+package restic
import (
"strings"
diff --git a/cmd/serve/restic/cache_test.go b/cmd/serve/restic/cache_test.go
index ffabfa773..c8ac1151e 100644
--- a/cmd/serve/restic/cache_test.go
+++ b/cmd/serve/restic/cache_test.go
@@ -1,4 +1,4 @@
-package restic
+package restic
import (
"sort"
diff --git a/cmd/serve/restic/restic.go b/cmd/serve/restic/restic.go
index e295989c0..8a61b411a 100644
--- a/cmd/serve/restic/restic.go
+++ b/cmd/serve/restic/restic.go
@@ -1,4 +1,4 @@
-// Package restic serves a remote suitable for use with restic
+// Package restic serves a remote suitable for use with restic
package restic
import (
diff --git a/cmd/serve/restic/restic_appendonly_test.go b/cmd/serve/restic/restic_appendonly_test.go
index af5e8e680..fc23191c6 100644
--- a/cmd/serve/restic/restic_appendonly_test.go
+++ b/cmd/serve/restic/restic_appendonly_test.go
@@ -1,4 +1,4 @@
-package restic
+package restic
import (
"context"
diff --git a/cmd/serve/restic/restic_privaterepos_test.go b/cmd/serve/restic/restic_privaterepos_test.go
index 21e1d68ce..fc050be12 100644
--- a/cmd/serve/restic/restic_privaterepos_test.go
+++ b/cmd/serve/restic/restic_privaterepos_test.go
@@ -1,4 +1,4 @@
-package restic
+package restic
import (
"context"
diff --git a/cmd/serve/restic/restic_test.go b/cmd/serve/restic/restic_test.go
index 851e542be..38fe95322 100644
--- a/cmd/serve/restic/restic_test.go
+++ b/cmd/serve/restic/restic_test.go
@@ -1,4 +1,4 @@
-// Serve restic tests set up a server and run the integration tests
+// Serve restic tests set up a server and run the integration tests
// for restic against it.
package restic
diff --git a/cmd/serve/restic/restic_utils_test.go b/cmd/serve/restic/restic_utils_test.go
index 587c7e1ab..3093580c6 100644
--- a/cmd/serve/restic/restic_utils_test.go
+++ b/cmd/serve/restic/restic_utils_test.go
@@ -1,4 +1,4 @@
-package restic
+package restic
import (
"io"
diff --git a/cmd/serve/restic/stdio_conn.go b/cmd/serve/restic/stdio_conn.go
index 3b411eb0e..529c76b78 100644
--- a/cmd/serve/restic/stdio_conn.go
+++ b/cmd/serve/restic/stdio_conn.go
@@ -1,4 +1,4 @@
-package restic
+package restic
import (
"net"
diff --git a/cmd/serve/s3/backend.go b/cmd/serve/s3/backend.go
index d4531e987..a3d8f6b3a 100644
--- a/cmd/serve/s3/backend.go
+++ b/cmd/serve/s3/backend.go
@@ -1,4 +1,4 @@
-// Package s3 implements an s3 server for rclone
+// Package s3 implements an s3 server for rclone
package s3
import (
diff --git a/cmd/serve/s3/ioutils.go b/cmd/serve/s3/ioutils.go
index 9ca5e695d..0431b1b31 100644
--- a/cmd/serve/s3/ioutils.go
+++ b/cmd/serve/s3/ioutils.go
@@ -1,4 +1,4 @@
-package s3
+package s3
import "io"
diff --git a/cmd/serve/s3/list.go b/cmd/serve/s3/list.go
index 559fa14a4..092d2478c 100644
--- a/cmd/serve/s3/list.go
+++ b/cmd/serve/s3/list.go
@@ -1,4 +1,4 @@
-package s3
+package s3
import (
"path"
diff --git a/cmd/serve/s3/logger.go b/cmd/serve/s3/logger.go
index d99e27df7..ae94f6809 100644
--- a/cmd/serve/s3/logger.go
+++ b/cmd/serve/s3/logger.go
@@ -1,7 +1,8 @@
-package s3
+package s3
import (
"fmt"
+ "strings"
"github.com/rclone/gofakes3"
"github.com/rclone/rclone/fs"
@@ -12,25 +13,23 @@ type logger struct{}
// print log message
func (l logger) Print(level gofakes3.LogLevel, v ...any) {
- var s string
- if len(v) == 0 {
- s = ""
- } else {
- var ok bool
- s, ok = v[0].(string)
- if !ok {
- s = fmt.Sprint(v[0])
+ var b strings.Builder
+ for i := range v {
+ if i > 0 {
+ fmt.Fprintf(&b, " ")
}
- v = v[1:]
+ fmt.Fprint(&b, v[i])
}
+ s := b.String()
+
switch level {
default:
fallthrough
case gofakes3.LogErr:
- fs.Errorf("serve s3", s, v...)
+ fs.Errorf("serve s3", s)
case gofakes3.LogWarn:
- fs.Infof("serve s3", s, v...)
+ fs.Infof("serve s3", s)
case gofakes3.LogInfo:
- fs.Debugf("serve s3", s, v...)
+ fs.Debugf("serve s3", s)
}
}
diff --git a/cmd/serve/s3/pager.go b/cmd/serve/s3/pager.go
index 953f947d2..dd4e4e2a8 100644
--- a/cmd/serve/s3/pager.go
+++ b/cmd/serve/s3/pager.go
@@ -1,4 +1,4 @@
-// Package s3 implements a fake s3 server for rclone
+// Package s3 implements a fake s3 server for rclone
package s3
import (
diff --git a/cmd/serve/s3/s3.go b/cmd/serve/s3/s3.go
index d4eb711c2..3c51e27be 100644
--- a/cmd/serve/s3/s3.go
+++ b/cmd/serve/s3/s3.go
@@ -1,4 +1,4 @@
-package s3
+package s3
import (
"context"
diff --git a/cmd/serve/s3/s3_test.go b/cmd/serve/s3/s3_test.go
index 15b19d36d..33825b246 100644
--- a/cmd/serve/s3/s3_test.go
+++ b/cmd/serve/s3/s3_test.go
@@ -1,4 +1,4 @@
-// Serve s3 tests set up a server and run the integration tests
+// Serve s3 tests set up a server and run the integration tests
// for the s3 remote against it.
package s3
diff --git a/cmd/serve/s3/server.go b/cmd/serve/s3/server.go
index 4ef792701..17507b038 100644
--- a/cmd/serve/s3/server.go
+++ b/cmd/serve/s3/server.go
@@ -1,4 +1,4 @@
-// Package s3 implements a fake s3 server for rclone
+// Package s3 implements a fake s3 server for rclone
package s3
import (
diff --git a/cmd/serve/s3/utils.go b/cmd/serve/s3/utils.go
index 5020df63a..ee543fe92 100644
--- a/cmd/serve/s3/utils.go
+++ b/cmd/serve/s3/utils.go
@@ -1,4 +1,4 @@
-package s3
+package s3
import (
"context"
diff --git a/cmd/serve/serve.go b/cmd/serve/serve.go
index b2773ba9c..42e5a7f6d 100644
--- a/cmd/serve/serve.go
+++ b/cmd/serve/serve.go
@@ -1,4 +1,4 @@
-// Package serve provides the serve command.
+// Package serve provides the serve command.
package serve
import (
diff --git a/cmd/serve/servetest/proxy_code.go b/cmd/serve/servetest/proxy_code.go
index 25499b6bc..626d65eeb 100644
--- a/cmd/serve/servetest/proxy_code.go
+++ b/cmd/serve/servetest/proxy_code.go
@@ -1,4 +1,4 @@
-//go:build ignore
+//go:build ignore
// A simple auth proxy for testing purposes
package main
diff --git a/cmd/serve/servetest/rc.go b/cmd/serve/servetest/rc.go
index bd88c173a..81efe8523 100644
--- a/cmd/serve/servetest/rc.go
+++ b/cmd/serve/servetest/rc.go
@@ -1,4 +1,4 @@
-package servetest
+package servetest
import (
"context"
diff --git a/cmd/serve/servetest/servetest.go b/cmd/serve/servetest/servetest.go
index 2b217b3b2..15566d6d6 100644
--- a/cmd/serve/servetest/servetest.go
+++ b/cmd/serve/servetest/servetest.go
@@ -1,4 +1,4 @@
-// Package servetest provides infrastructure for running loopback
+// Package servetest provides infrastructure for running loopback
// tests of "rclone serve backend:" against the backend integration
// tests.
package servetest
diff --git a/cmd/serve/sftp/connection.go b/cmd/serve/sftp/connection.go
index 3fc4396cf..7a63fb9b7 100644
--- a/cmd/serve/sftp/connection.go
+++ b/cmd/serve/sftp/connection.go
@@ -1,4 +1,4 @@
-//go:build !plan9
+//go:build !plan9
package sftp
diff --git a/cmd/serve/sftp/connection_test.go b/cmd/serve/sftp/connection_test.go
index b406e79f4..4f89ceaa7 100644
--- a/cmd/serve/sftp/connection_test.go
+++ b/cmd/serve/sftp/connection_test.go
@@ -1,4 +1,4 @@
-//go:build !plan9
+//go:build !plan9
package sftp
diff --git a/cmd/serve/sftp/handler.go b/cmd/serve/sftp/handler.go
index f7c9c3c47..92476ffff 100644
--- a/cmd/serve/sftp/handler.go
+++ b/cmd/serve/sftp/handler.go
@@ -1,4 +1,4 @@
-//go:build !plan9
+//go:build !plan9
package sftp
diff --git a/cmd/serve/sftp/server.go b/cmd/serve/sftp/server.go
index 17889503d..14714bc92 100644
--- a/cmd/serve/sftp/server.go
+++ b/cmd/serve/sftp/server.go
@@ -1,4 +1,4 @@
-//go:build !plan9
+//go:build !plan9
package sftp
diff --git a/cmd/serve/sftp/sftp.go b/cmd/serve/sftp/sftp.go
index a49d54ba6..017a75b5f 100644
--- a/cmd/serve/sftp/sftp.go
+++ b/cmd/serve/sftp/sftp.go
@@ -1,4 +1,4 @@
-//go:build !plan9
+//go:build !plan9
// Package sftp implements an SFTP server to serve an rclone VFS
package sftp
diff --git a/cmd/serve/sftp/sftp_test.go b/cmd/serve/sftp/sftp_test.go
index e18d6b86d..642023339 100644
--- a/cmd/serve/sftp/sftp_test.go
+++ b/cmd/serve/sftp/sftp_test.go
@@ -1,4 +1,4 @@
-// Serve sftp tests set up a server and run the integration tests
+// Serve sftp tests set up a server and run the integration tests
// for the sftp remote against it.
//
// We skip tests on platforms with troublesome character mappings
diff --git a/cmd/serve/sftp/sftp_unsupported.go b/cmd/serve/sftp/sftp_unsupported.go
index 22e0673a7..fab5a2ae0 100644
--- a/cmd/serve/sftp/sftp_unsupported.go
+++ b/cmd/serve/sftp/sftp_unsupported.go
@@ -1,4 +1,4 @@
-// Build for sftp for unsupported platforms to stop go complaining
+// Build for sftp for unsupported platforms to stop go complaining
// about "no buildable Go source files "
//go:build plan9
diff --git a/cmd/serve/webdav/webdav.go b/cmd/serve/webdav/webdav.go
index 2183420e5..006be4f5f 100644
--- a/cmd/serve/webdav/webdav.go
+++ b/cmd/serve/webdav/webdav.go
@@ -1,4 +1,4 @@
-// Package webdav implements a WebDAV server backed by rclone VFS
+// Package webdav implements a WebDAV server backed by rclone VFS
package webdav
import (
diff --git a/cmd/serve/webdav/webdav_test.go b/cmd/serve/webdav/webdav_test.go
index 4066f6b76..740ad5d92 100644
--- a/cmd/serve/webdav/webdav_test.go
+++ b/cmd/serve/webdav/webdav_test.go
@@ -1,4 +1,4 @@
-// Serve webdav tests set up a server and run the integration tests
+// Serve webdav tests set up a server and run the integration tests
// for the webdav remote against it.
//
// We skip tests on platforms with troublesome character mappings
diff --git a/cmd/settier/settier.go b/cmd/settier/settier.go
index 97480f9fc..3204e1eab 100644
--- a/cmd/settier/settier.go
+++ b/cmd/settier/settier.go
@@ -1,4 +1,4 @@
-// Package settier provides the settier command.
+// Package settier provides the settier command.
package settier
import (
diff --git a/cmd/sha1sum/sha1sum.go b/cmd/sha1sum/sha1sum.go
index a6262f5a4..6932bd172 100644
--- a/cmd/sha1sum/sha1sum.go
+++ b/cmd/sha1sum/sha1sum.go
@@ -1,4 +1,4 @@
-// Package sha1sum provides the sha1sum command.
+// Package sha1sum provides the sha1sum command.
package sha1sum
import (
diff --git a/cmd/siginfo_bsd.go b/cmd/siginfo_bsd.go
index 1144459d6..97351e506 100644
--- a/cmd/siginfo_bsd.go
+++ b/cmd/siginfo_bsd.go
@@ -1,4 +1,4 @@
-//go:build darwin || freebsd || netbsd || dragonfly || openbsd
+//go:build darwin || freebsd || netbsd || dragonfly || openbsd
package cmd
diff --git a/cmd/siginfo_others.go b/cmd/siginfo_others.go
index da71babd9..7610d95f1 100644
--- a/cmd/siginfo_others.go
+++ b/cmd/siginfo_others.go
@@ -1,4 +1,4 @@
-//go:build !darwin && !freebsd && !netbsd && !dragonfly && !openbsd
+//go:build !darwin && !freebsd && !netbsd && !dragonfly && !openbsd
package cmd
diff --git a/cmd/size/size.go b/cmd/size/size.go
index a396ddaa1..d11ffcd29 100644
--- a/cmd/size/size.go
+++ b/cmd/size/size.go
@@ -1,4 +1,4 @@
-// Package size provides the size command.
+// Package size provides the size command.
package size
import (
diff --git a/cmd/sync/sync.go b/cmd/sync/sync.go
index 562b8373a..dc17a15fb 100644
--- a/cmd/sync/sync.go
+++ b/cmd/sync/sync.go
@@ -1,4 +1,4 @@
-// Package sync provides the sync command.
+// Package sync provides the sync command.
package sync
import (
diff --git a/cmd/test/changenotify/changenotify.go b/cmd/test/changenotify/changenotify.go
index 2afb5165d..42fcf117e 100644
--- a/cmd/test/changenotify/changenotify.go
+++ b/cmd/test/changenotify/changenotify.go
@@ -1,4 +1,4 @@
-// Package changenotify tests rclone's changenotify support
+// Package changenotify tests rclone's changenotify support
package changenotify
import (
diff --git a/cmd/test/histogram/histogram.go b/cmd/test/histogram/histogram.go
index 583105816..23a1558c0 100644
--- a/cmd/test/histogram/histogram.go
+++ b/cmd/test/histogram/histogram.go
@@ -1,4 +1,4 @@
-// Package histogram provides the histogram test command.
+// Package histogram provides the histogram test command.
package histogram
import (
diff --git a/cmd/test/info/base32768.go b/cmd/test/info/base32768.go
index 23949a2e1..e74bb0667 100644
--- a/cmd/test/info/base32768.go
+++ b/cmd/test/info/base32768.go
@@ -1,4 +1,4 @@
-package info
+package info
// Create files with all possible base 32768 file names
@@ -34,7 +34,7 @@ func (r *results) checkBase32768() {
// Create test files
for _, c := range safeAlphabet {
var out strings.Builder
- for i := rune(0); i < 32; i++ {
+ for i := range rune(32) {
out.WriteRune(c + i)
}
fileName := filepath.Join(dir, fmt.Sprintf("%04d-%s.txt", n, out.String()))
diff --git a/cmd/test/info/info.go b/cmd/test/info/info.go
index 9f0b9be8d..23260d747 100644
--- a/cmd/test/info/info.go
+++ b/cmd/test/info/info.go
@@ -1,4 +1,4 @@
-// Package info provides the info test command.
+// Package info provides the info test command.
package info
// FIXME once translations are implemented will need a no-escape
@@ -292,7 +292,7 @@ func (r *results) checkControls() {
tokens <- struct{}{}
}
var wg sync.WaitGroup
- for i := rune(0); i < 128; i++ {
+ for i := range rune(128) {
s := string(i)
if i == 0 || i == '/' {
// We're not even going to check NULL or /
diff --git a/cmd/test/info/internal/build_csv/main.go b/cmd/test/info/internal/build_csv/main.go
index fa4b15b53..9dab50c69 100644
--- a/cmd/test/info/internal/build_csv/main.go
+++ b/cmd/test/info/internal/build_csv/main.go
@@ -1,4 +1,4 @@
-// Package main provides utilities for the info test command.
+// Package main provides utilities for the info test command.
package main
import (
diff --git a/cmd/test/info/internal/internal.go b/cmd/test/info/internal/internal.go
index 7af06b87a..8e8857799 100644
--- a/cmd/test/info/internal/internal.go
+++ b/cmd/test/info/internal/internal.go
@@ -1,4 +1,4 @@
-// Package internal provides internal implementation for the info test command.
+// Package internal provides internal implementation for the info test command.
package internal
import (
@@ -95,7 +95,7 @@ func (e *Position) UnmarshalText(text []byte) error {
switch s := strings.ToLower(string(text)); s {
default:
*e = PositionNone
- for _, p := range strings.Split(s, ",") {
+ for p := range strings.SplitSeq(s, ",") {
switch p {
case "left":
*e |= PositionLeft
diff --git a/cmd/test/makefiles/makefiles.go b/cmd/test/makefiles/makefiles.go
index 921d708cf..716054620 100644
--- a/cmd/test/makefiles/makefiles.go
+++ b/cmd/test/makefiles/makefiles.go
@@ -1,4 +1,4 @@
-// Package makefiles builds a directory structure with the required
+// Package makefiles builds a directory structure with the required
// number of files in of the required size.
package makefiles
diff --git a/cmd/test/makefiles/speed.go b/cmd/test/makefiles/speed.go
index fa2ea7808..4b98deae9 100644
--- a/cmd/test/makefiles/speed.go
+++ b/cmd/test/makefiles/speed.go
@@ -1,4 +1,4 @@
-package makefiles
+package makefiles
import (
"context"
diff --git a/cmd/test/memory/memory.go b/cmd/test/memory/memory.go
index 058a5e8a5..183ba0c0f 100644
--- a/cmd/test/memory/memory.go
+++ b/cmd/test/memory/memory.go
@@ -1,4 +1,4 @@
-// Package memory provides the memory test command.
+// Package memory provides the memory test command.
package memory
import (
diff --git a/cmd/test/test.go b/cmd/test/test.go
index 95de40b1b..d48a069f7 100644
--- a/cmd/test/test.go
+++ b/cmd/test/test.go
@@ -1,4 +1,4 @@
-// Package test provides the test command.
+// Package test provides the test command.
package test
import (
diff --git a/cmd/touch/touch.go b/cmd/touch/touch.go
index d5f3f1e9a..eb7fb669c 100644
--- a/cmd/touch/touch.go
+++ b/cmd/touch/touch.go
@@ -1,4 +1,4 @@
-// Package touch provides the touch command.
+// Package touch provides the touch command.
package touch
import (
diff --git a/cmd/touch/touch_test.go b/cmd/touch/touch_test.go
index 47e904549..81f5b31c4 100644
--- a/cmd/touch/touch_test.go
+++ b/cmd/touch/touch_test.go
@@ -1,4 +1,4 @@
-package touch
+package touch
import (
"context"
diff --git a/cmd/tree/tree.go b/cmd/tree/tree.go
index b964f3b2e..1fdda46a1 100644
--- a/cmd/tree/tree.go
+++ b/cmd/tree/tree.go
@@ -1,4 +1,4 @@
-// Package tree provides the tree command.
+// Package tree provides the tree command.
package tree
import (
diff --git a/cmd/tree/tree_test.go b/cmd/tree/tree_test.go
index 7d0f3cbe6..4518eac9e 100644
--- a/cmd/tree/tree_test.go
+++ b/cmd/tree/tree_test.go
@@ -1,4 +1,4 @@
-package tree
+package tree
import (
"bytes"
diff --git a/cmd/version/version.go b/cmd/version/version.go
index e588df721..6636147d9 100644
--- a/cmd/version/version.go
+++ b/cmd/version/version.go
@@ -1,4 +1,4 @@
-// Package version provides the version command.
+// Package version provides the version command.
package version
import (
diff --git a/cmd/version/version_test.go b/cmd/version/version_test.go
index fa20a0a9c..5a40c3dac 100644
--- a/cmd/version/version_test.go
+++ b/cmd/version/version_test.go
@@ -1,4 +1,4 @@
-package version
+package version
import (
"os"
diff --git a/cmdtest/cmdtest.go b/cmdtest/cmdtest.go
index 06a87ebab..67e97a00b 100644
--- a/cmdtest/cmdtest.go
+++ b/cmdtest/cmdtest.go
@@ -1,4 +1,4 @@
-// Package cmdtest creates a testable interface to rclone main
+// Package cmdtest creates a testable interface to rclone main
//
// The interface is used to perform end-to-end test of
// commands, flags, environment variables etc.
diff --git a/cmdtest/cmdtest_test.go b/cmdtest/cmdtest_test.go
index 3f65284b3..1bae1f736 100644
--- a/cmdtest/cmdtest_test.go
+++ b/cmdtest/cmdtest_test.go
@@ -1,4 +1,4 @@
-// cmdtest_test creates a testable interface to rclone main
+// cmdtest_test creates a testable interface to rclone main
//
// The interface is used to perform end-to-end test of
// commands, flags, environment variables etc.
diff --git a/cmdtest/environment_test.go b/cmdtest/environment_test.go
index 86ab0b4de..d9d9c8cf6 100644
--- a/cmdtest/environment_test.go
+++ b/cmdtest/environment_test.go
@@ -1,4 +1,4 @@
-// environment_test tests the use and precedence of environment variables
+// environment_test tests the use and precedence of environment variables
//
// The tests rely on functions defined in cmdtest_test.go
@@ -351,7 +351,7 @@ func TestEnvironmentVariables(t *testing.T) {
parseFileFilters := func(out string) (extensions []string) {
// Match: - (^|/)[^/]*\.jpg$
find := regexp.MustCompile(`^- \(\^\|\/\)\[\^\/\]\*\\\.(.*?)\$$`)
- for _, line := range strings.Split(out, "\n") {
+ for line := range strings.SplitSeq(out, "\n") {
if m := find.FindStringSubmatch(line); m != nil {
extensions = append(extensions, m[1])
}
diff --git a/docs/content/_index.md b/docs/content/_index.md
index 06c200cf3..dcc4b424d 100644
--- a/docs/content/_index.md
+++ b/docs/content/_index.md
@@ -125,6 +125,7 @@ WebDAV or S3, that work out of the box.)
{{< provider name="Exaba" home="https://exaba.com/" config="/s3/#exaba" >}}
{{< provider name="Fastmail Files" home="https://www.fastmail.com/" config="/webdav/#fastmail-files" >}}
{{< provider name="FileLu Cloud Storage" home="https://filelu.com/" config="/filelu/" >}}
+{{< provider name="FileLu S5 (S3-Compatible Object Storage)" home="https://s5lu.com/" config="/s3/#filelu-s5" >}}
{{< provider name="Files.com" home="https://www.files.com/" config="/filescom/" >}}
{{< provider name="FlashBlade" home="https://www.purestorage.com/products/unstructured-data-storage.html" config="/s3/#pure-storage-flashblade" >}}
{{< provider name="FTP" home="https://en.wikipedia.org/wiki/File_Transfer_Protocol" config="/ftp/" >}}
@@ -133,9 +134,11 @@ WebDAV or S3, that work out of the box.)
{{< provider name="Google Drive" home="https://www.google.com/drive/" config="/drive/" >}}
{{< provider name="Google Photos" home="https://www.google.com/photos/about/" config="/googlephotos/" >}}
{{< provider name="HDFS" home="https://hadoop.apache.org/" config="/hdfs/" >}}
+{{< provider name="Hetzner Object Storage" home="https://www.hetzner.com/storage/object-storage/" config="/s3/#hetzner" >}}
{{< provider name="Hetzner Storage Box" home="https://www.hetzner.com/storage/storage-box" config="/sftp/#hetzner-storage-box" >}}
{{< provider name="HiDrive" home="https://www.strato.de/cloud-speicher/" config="/hidrive/" >}}
{{< provider name="HTTP" home="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol" config="/http/" >}}
+{{< provider name="Huawei OBS" home="https://www.huaweicloud.com/intl/en-us/product/obs.html" config="/s3/#huawei-obs" >}}
{{< provider name="iCloud Drive" home="https://icloud.com/" config="/iclouddrive/" >}}
{{< provider name="ImageKit" home="https://imagekit.io" config="/imagekit/" >}}
{{< provider name="Internet Archive" home="https://archive.org/" config="/internetarchive/" >}}
@@ -179,7 +182,10 @@ WebDAV or S3, that work out of the box.)
{{< provider name="QingStor" home="https://www.qingcloud.com/products/storage" config="/qingstor/" >}}
{{< provider name="Qiniu Cloud Object Storage (Kodo)" home="https://www.qiniu.com/en/products/kodo" config="/s3/#qiniu" >}}
{{< provider name="Quatrix by Maytech" home="https://www.maytech.net/products/quatrix-business" config="/quatrix/" >}}
+{{< provider name="Rabata Cloud Storage" home="https://rabata.io" config="/s3/#Rabata" >}}
+{{< provider name="RackCorp Object Storage" home="https://www.rackcorp.com/" config="/s3/#RackCorp" >}}
{{< provider name="Rackspace Cloud Files" home="https://www.rackspace.com/cloud/files" config="/swift/" >}}
+{{< provider name="Rclone Serve S3" home="/commands/rclone_serve_s3/" config="/s3/#rclone" >}}
{{< provider name="rsync.net" home="https://rsync.net/products/rclone.html" config="/sftp/#rsync-net" >}}
{{< provider name="Scaleway" home="https://www.scaleway.com/object-storage/" config="/s3/#scaleway" >}}
{{< provider name="Seafile" home="https://www.seafile.com/" config="/seafile/" >}}
@@ -194,7 +200,6 @@ WebDAV or S3, that work out of the box.)
{{< provider name="Storj" home="https://storj.io/" config="/storj/" >}}
{{< provider name="Synology" home="https://c2.synology.com/en-global/object-storage/overview" config="/s3/#synology-c2" >}}
{{< provider name="SugarSync" home="https://sugarsync.com/" config="/sugarsync/" >}}
-{{< provider name="Terabox" home="https://www.terabox.com/" config="/terabox/" >}}
{{< provider name="Tencent Cloud Object Storage (COS)" home="https://intl.cloud.tencent.com/product/cos" config="/s3/#tencent-cos" >}}
{{< provider name="Uloz.to" home="https://uloz.to" config="/ulozto/" >}}
{{< provider name="Uptobox" home="https://uptobox.com" config="/uptobox/" >}}
diff --git a/docs/content/authors.md b/docs/content/authors.md
index b61a11e90..c46df27b6 100644
--- a/docs/content/authors.md
+++ b/docs/content/authors.md
@@ -980,7 +980,6 @@ put them back in again.` >}}
- Flora Thiebaut
- kingston125
- Ser-Bul <30335009+Ser-Bul@users.noreply.github.com>
- * Benji Silver
- jinjingroad
- necaran <55765083+necaran@users.noreply.github.com>
- Marvin Rösch
@@ -991,7 +990,7 @@ put them back in again.` >}}
- Ross Smith II
- Vikas Bhansali <64532198+vibhansa-msft@users.noreply.github.com>
- Sudipto Baral
-- Sam Pegg
+- Sam Pegg <70067376+S-Pegg1@users.noreply.github.com>
- liubingrun
- Albin Parou
- n4n5 <56606507+Its-Just-Nans@users.noreply.github.com>
@@ -1012,3 +1011,11 @@ put them back in again.` >}}
- dougal <147946567+roucc@users.noreply.github.com>
- anon-pradip
- Robin Rolf
+- Jean-Christophe Cura
+- russcoss
+- Matt LaPaglia
+- Youfu Zhang <1315097+zhangyoufu@users.noreply.github.com>
+- juejinyuxitu
+- iTrooz
+- Microscotch
+- Andrew Ruthven
diff --git a/docs/content/box.md b/docs/content/box.md
index 85bc27a97..aaa19d579 100644
--- a/docs/content/box.md
+++ b/docs/content/box.md
@@ -84,7 +84,7 @@ y/e/d> y
```
See the [remote setup docs](/remote_setup/) for how to set it up on a
-machine with no Internet browser available.
+machine without an internet-connected web browser available.
Note that rclone runs a webserver on your local machine to collect the
token as returned from Box. This only runs from the moment it opens
diff --git a/docs/content/changelog.md b/docs/content/changelog.md
index 57a7cf6f8..5ebe40fb5 100644
--- a/docs/content/changelog.md
+++ b/docs/content/changelog.md
@@ -6,9 +6,34 @@ description: "Rclone Changelog"
# Changelog
-## v1.73.0 - 2025-08-22
+## v1.71.1 - 2025-09-24
-[See commits](https://github.com/rclone/rclone/compare/v1.70.0...v1.73.0)
+[See commits](https://github.com/rclone/rclone/compare/v1.71.0...v1.71.1)
+
+- Bug Fixes
+ - bisync: Fix error handling for renamed conflicts (nielash)
+ - march: Fix deadlock when using --fast-list on syncs (Nick Craig-Wood)
+ - operations: Fix partial name collisions for non --inplace copies (Nick Craig-Wood)
+ - pacer: Fix deadlock with --max-connections (Nick Craig-Wood)
+ - doc fixes (albertony, anon-pradip, Claudius Ellsel, dougal, Jean-Christophe Cura, Nick Craig-Wood, nielash)
+- Mount
+ - Do not log successful unmount as an error (Tilman Vogel)
+- VFS
+ - Fix SIGHUP killing serve instead of flushing directory caches (dougal)
+- Local
+ - Fix rmdir "Access is denied" on windows (nielash)
+- Box
+ - Fix about after change in API return (Nick Craig-Wood)
+- Combine
+ - Propagate SlowHash feature (skbeh)
+- Drive
+ - Update making your own client ID instructions (Ed Craig-Wood)
+- Internet Archive
+ - Fix server side copy files with spaces (Nick Craig-Wood)
+
+## v1.71.0 - 2025-08-22
+
+[See commits](https://github.com/rclone/rclone/compare/v1.70.0...v1.71.0)
- New S3 providers
- [Exaba](/s3/#exaba) (Nick Craig-Wood)
@@ -147,15 +172,15 @@ description: "Rclone Changelog"
## v1.70.1 - 2025-06-19
-[See commits](https://github.com/rclone/rclone/compare/v1.73.0...v1.70.1)
+[See commits](https://github.com/rclone/rclone/compare/v1.70.0...v1.70.1)
- Bug Fixes
- convmv: Fix spurious "error running command echo" on Windows (Nick Craig-Wood)
- doc fixes (albertony, Ed Craig-Wood, jinjingroad)
-## v1.73.0 - 2025-06-17
+## v1.70.0 - 2025-06-17
-[See commits](https://github.com/rclone/rclone/compare/v1.69.0...v1.73.0)
+[See commits](https://github.com/rclone/rclone/compare/v1.69.0...v1.70.0)
- New backends
- [DOI](/doi/) (Flora Thiebaut)
diff --git a/docs/content/commands/rclone.md b/docs/content/commands/rclone.md
index 3738aa446..aa531f0d0 100644
--- a/docs/content/commands/rclone.md
+++ b/docs/content/commands/rclone.md
@@ -1015,7 +1015,7 @@ rclone [flags]
--use-json-log Use json log format
--use-mmap Use mmap allocator (see docs)
--use-server-modtime Use server modified time instead of object metadata
- --user-agent string Set the user-agent to a specified string (default "rclone/v1.73.1")
+ --user-agent string Set the user-agent to a specified string (default "rclone/v1.73.2")
-v, --verbose count Print lots more stuff (repeat for more)
-V, --version Print the version number
--webdav-auth-redirect Preserve authentication on redirect
diff --git a/docs/content/docs.md b/docs/content/docs.md
index 77f386277..dd27f9d44 100644
--- a/docs/content/docs.md
+++ b/docs/content/docs.md
@@ -85,7 +85,6 @@ See the following for detailed instructions for
- [SMB](/smb/)
- [Storj](/storj/)
- [SugarSync](/sugarsync/)
-- [Terabox](/terabox/)
- [Union](/union/)
- [Uloz.to](/ulozto/)
- [Uptobox](/uptobox/)
@@ -385,6 +384,9 @@ does not work on Windows.)
rclone copy ':http,url="https://example.com":path/to/dir' /tmp/dir
```
+You can use [rclone config string](/commands/rclone_config_string/) to
+convert a remote into a connection string.
+
#### Connection strings, config and logging
If you supply extra configuration to a backend by command line flag,
diff --git a/docs/content/drive.md b/docs/content/drive.md
index de1c3196c..5b3579635 100644
--- a/docs/content/drive.md
+++ b/docs/content/drive.md
@@ -96,7 +96,7 @@ y/e/d> y
```
See the [remote setup docs](/remote_setup/) for how to set it up on a
-machine with no Internet browser available.
+machine without an internet-connected web browser available.
Note that rclone runs a webserver on your local machine to collect the
token as returned from Google if using web browser to automatically
diff --git a/docs/content/dropbox.md b/docs/content/dropbox.md
index 71eccb7b4..327c68b8e 100644
--- a/docs/content/dropbox.md
+++ b/docs/content/dropbox.md
@@ -60,7 +60,7 @@ y/e/d> y
```
See the [remote setup docs](/remote_setup/) for how to set it up on a
-machine with no Internet browser available.
+machine without an internet-connected web browser available.
Note that rclone runs a webserver on your local machine to collect the
token as returned from Dropbox. This only
diff --git a/docs/content/flags.md b/docs/content/flags.md
index c92a1c921..8f2977872 100644
--- a/docs/content/flags.md
+++ b/docs/content/flags.md
@@ -121,7 +121,7 @@ Flags for general networking and HTTP stuff.
--tpslimit float Limit HTTP transactions per second to this
--tpslimit-burst int Max burst of transactions for --tpslimit (default 1)
--use-cookies Enable session cookiejar
- --user-agent string Set the user-agent to a specified string (default "rclone/v1.73.1")
+ --user-agent string Set the user-agent to a specified string (default "rclone/v1.73.2")
```
diff --git a/docs/content/googlecloudstorage.md b/docs/content/googlecloudstorage.md
index c403a1d2e..a7596b8f5 100644
--- a/docs/content/googlecloudstorage.md
+++ b/docs/content/googlecloudstorage.md
@@ -147,7 +147,7 @@ y/e/d> y
```
See the [remote setup docs](/remote_setup/) for how to set it up on a
-machine with no Internet browser available.
+machine without an internet-connected web browser available.
Note that rclone runs a webserver on your local machine to collect the
token as returned from Google if using web browser to automatically
diff --git a/docs/content/googlephotos.md b/docs/content/googlephotos.md
index 102f2d039..2b87b235a 100644
--- a/docs/content/googlephotos.md
+++ b/docs/content/googlephotos.md
@@ -97,7 +97,7 @@ y/e/d> y
```
See the [remote setup docs](/remote_setup/) for how to set it up on a
-machine with no Internet browser available.
+machine without an internet-connected web browser available.
Note that rclone runs a webserver on your local machine to collect the
token as returned from Google if using web browser to automatically
diff --git a/docs/content/hidrive.md b/docs/content/hidrive.md
index 8e990e93e..f94eb80c4 100644
--- a/docs/content/hidrive.md
+++ b/docs/content/hidrive.md
@@ -72,7 +72,7 @@ and hence should not be shared with other persons.**
See the [below section](#keeping-your-tokens-safe) for more information.
See the [remote setup docs](/remote_setup/) for how to set it up on a
-machine with no Internet browser available.
+machine without an internet-connected web browser available.
Note that rclone runs a webserver on your local machine to collect the
token as returned from HiDrive. This only runs from the moment it opens
diff --git a/docs/content/jottacloud.md b/docs/content/jottacloud.md
index e4dab510b..e0765c7e5 100644
--- a/docs/content/jottacloud.md
+++ b/docs/content/jottacloud.md
@@ -7,106 +7,171 @@ versionIntroduced: "v1.43"
# {{< icon "fa fa-cloud" >}} Jottacloud
Jottacloud is a cloud storage service provider from a Norwegian company, using
-its own datacenters in Norway. In addition to the official service at
-[jottacloud.com](https://www.jottacloud.com/), it also provides white-label
-solutions to different companies, such as:
+its own datacenters in Norway.
+In addition to the official service at [jottacloud.com](https://www.jottacloud.com/),
+it also provides white-label solutions to different companies. The following
+are currently supported by this backend, using a different authentication setup
+as described [below](#whitelabel-authentication):
+
+- Elkjøp (with subsidiaries):
+ - Elkjøp Cloud (cloud.elkjop.no)
+ - Elgiganten Cloud (cloud.elgiganten.dk)
+ - Elgiganten Cloud (cloud.elgiganten.se)
+ - ELKO Cloud (cloud.elko.is)
+ - Gigantti Cloud (cloud.gigantti.fi)
- Telia
- Telia Cloud (cloud.telia.se)
- Telia Sky (sky.telia.no)
- Tele2
- Tele2 Cloud (mittcloud.tele2.se)
- Onlime
- - Onlime Cloud Storage (onlime.dk)
-- Elkjøp (with subsidiaries):
- - Elkjøp Cloud (cloud.elkjop.no)
- - Elgiganten Sweden (cloud.elgiganten.se)
- - Elgiganten Denmark (cloud.elgiganten.dk)
- - Giganti Cloud (cloud.gigantti.fi)
- - ELKO Cloud (cloud.elko.is)
-
-Most of the white-label versions are supported by this backend, although may
-require different authentication setup - described below.
+ - Onlime (onlime.dk)
+- MediaMarkt
+ - MediaMarkt Cloud (mediamarkt.jottacloud.com)
+ - Let's Go Cloud (letsgo.jotta.cloud)
Paths are specified as `remote:path`
Paths may be as deep as required, e.g. `remote:directory/subdirectory`.
-## Authentication types
-
-Some of the whitelabel versions uses a different authentication method than the
-official service, and you have to choose the correct one when setting up the remote.
-
-### Standard authentication
-
-The standard authentication method used by the official service (jottacloud.com),
-as well as some of the whitelabel services, requires you to generate a single-use
-personal login token from the account security settings in the service's web
-interface. Log in to your account, go to "Settings" and then "Security", or use
-the direct link presented to you by rclone when configuring the remote:
-. Scroll down to the section "Personal login
-token", and click the "Generate" button. Note that if you are using a whitelabel
-service you probably can't use the direct link, you need to find the same page in
-their dedicated web interface, and also it may be in a different location than
-described above.
-
-To access your account from multiple instances of rclone, you need to configure
-each of them with a separate personal login token. E.g. you create a Jottacloud
-remote with rclone in one location, and copy the configuration file to a second
-location where you also want to run rclone and access the same remote. Then you
-need to replace the token for one of them, using the [config reconnect](https://rclone.org/commands/rclone_config_reconnect/)
-command, which requires you to generate a new personal login token and supply
-as input. If you do not do this, the token may easily end up being invalidated,
-resulting in both instances failing with an error message something along the
+## Authentication
+
+Authentication in Jottacloud is in general based on OAuth and OpenID Connect
+(OIDC). There are different variants to choose from, depending on which service
+you are using, e.g. a white-label service may only support one of them. Note
+that there is no documentation to rely on, so the descriptions provided here
+are based on observations and may not be accurate.
+
+Jottacloud uses two optional OAuth security mechanisms, referred to as "Refresh
+Token Rotation" and "Automatic Reuse Detection", which has some implications.
+Access tokens normally have one hour expiry, after which they need to be
+refreshed (rotated), an operation that requires the refresh token to be
+supplied. Rclone does this automatically. This is standard OAuth. But in
+Jottacloud, such a refresh operation not only creates a new access token, but
+also refresh token, and invalidates the existing refresh token, the one that
+was supplied. It keeps track of the history of refresh tokens, sometimes
+referred to as a token family, descending from the original refresh token that
+was issued after the initial authentication. This is used to detect any
+attempts at reusing old refresh tokens, and trigger an immedate invalidation of
+the current refresh token, and effectively the entire refresh token family.
+
+When the current refresh token has been invalidated, next time rclone tries to
+perform a token refresh, it will fail with an error message something along the
lines of:
```text
- oauth2: cannot fetch token: 400 Bad Request
- Response: {"error":"invalid_grant","error_description":"Stale token"}
+CRITICAL: Failed to create file system for "remote:": (...): couldn't fetch token: invalid_grant: maybe token expired? - try refreshing with "rclone config reconnect remote:"
```
-When this happens, you need to replace the token as described above to be able
-to use your remote again.
-
-All personal login tokens you have taken into use will be listed in the web
-interface under "My logged in devices", and from the right side of that list
-you can click the "X" button to revoke individual tokens.
-
-### Legacy authentication
-
-If you are using one of the whitelabel versions (e.g. from Elkjøp) you may not
-have the option to generate a CLI token. In this case you'll have to use the
-legacy authentication. To do this select yes when the setup asks for legacy
-authentication and enter your username and password. The rest of the setup is
-identical to the default setup.
-
-### Telia Cloud authentication
-
-Similar to other whitelabel versions Telia Cloud doesn't offer the option of
-creating a CLI token, and additionally uses a separate authentication flow
-where the username is generated internally. To setup rclone to use Telia Cloud,
-choose Telia Cloud authentication in the setup. The rest of the setup is
-identical to the default setup.
-
-### Tele2 Cloud authentication
-
-As Tele2-Com Hem merger was completed this authentication can be used for former
-Com Hem Cloud and Tele2 Cloud customers as no support for creating a CLI token
-exists, and additionally uses a separate authentication flow where the username
-is generated internally. To setup rclone to use Tele2 Cloud, choose Tele2 Cloud
-authentication in the setup. The rest of the setup is identical to the default setup.
-
-### Onlime Cloud Storage authentication
-
-Onlime has sold access to Jottacloud proper, while providing localized support
-to Danish Customers, but have recently set up their own hosting, transferring
-their customers from Jottacloud servers to their own ones.
+If you run rclone with verbosity level 2 (`-vv`), you will see a debug message
+with an additional error description from the OAuth response:
-This, of course, necessitates using their servers for authentication, but
-otherwise functionality and architecture seems equivalent to Jottacloud.
+```text
+DEBUG : remote: got fatal oauth error: oauth2: "invalid_grant" "Session doesn't have required client"
+```
-To setup rclone to use Onlime Cloud Storage, choose Onlime Cloud authentication
-in the setup. The rest of the setup is identical to the default setup.
+(The error description used to be "Stale token" instead of "Session doesn't
+have required client", so you may see references to that in older descriptions
+of this situation.)
+
+When this happens, you need to re-authenticate to be able to use your remote
+again, e.g. using the [config reconnect](/commands/rclone_config_reconnect/)
+command as suggested in the error message. This will create an entirely new
+refresh token (family).
+
+A typical example of how you may end up in this situation, is if you create
+a Jottacloud remote with rclone in one location, and then copy the
+configuration file to a second location where you start using rclone to access
+the same remote. Eventually there will now be a token refresh attempt with an
+invalidated token, i.e. refresh token reuse, resulting in both instances
+starting to fail with the "invalid_grant" error. It is possible to copy remote
+configurations, but you must then replace the token for one of them using the
+[config reconnect](https://rclone.org/commands/rclone_config_reconnect/)
+command.
+
+You can get some overview of your active tokens in your service's web user
+interface, if you navigate to "Settings" and then "Security" (in which case
+you end up at or similar). Down on
+that page you have a section "My logged in devices". This contains a list
+of entries which seemingly represents currently valid refresh tokens, or
+refresh token families. From the right side of that list you can click a
+button ("X") to revoke (invalidate) it, which means you will still have access
+using an existing access token until that expires, but you will not be able to
+perform a token refresh. Note that this entire "My logged in devices" feature
+seem to behave a bit differently with different authentication variants and
+with use of the different (white-label) services.
+
+### Standard
+
+This is an OAuth variant designed for command-line applications. It is
+primarily supported by the official service (jottacloud.com), but may also be
+supported by some of the white-label services. The information necessary to be
+able to perform authentication, like domain name and endpoint to connect to,
+are found automatically (it is encoded into the supplied login token, described
+next), so you do not need to specify which service to configure.
+
+When configuring a remote, you are asked to enter a single-use personal login
+token, which you must manually generate from the account security settings in
+the service's web interface. You do not need a web browser on the same machine
+like with traditional OAuth, but need to use a web browser somewhere, and be
+able to be copy the generated string into your rclone configuration session.
+Log in to your service's web user interface, navigate to "Settings" and then
+"Security", or, for the official service, use the direct link presented to you
+by rclone when configuring the remote: .
+Scroll down to the section "Personal login token", and click the "Generate"
+button. Copy the presented string and paste it where rclone asks for it. Rclone
+will then use this to perform an initial token request, and receive a regular
+OAuth token which it stores in your remote configuration. There will then also
+be a new entry in the "My logged in devices" list in the web interface, with
+device name and application name "Jottacloud CLI".
+
+Each time a new token is created this way, i.e. a new personal login token is
+generated and traded in for an OAuth token, you get an entirely new refresh
+token family, with a new entry in the "My logged in devices". You can create as
+many remotes as you want, and use multiple instances of rclone on same or
+different machine, as long as you configure them separately like this, and not
+get your self into the refresh token reuse issue described above.
+
+### Traditional
+
+Jottacloud also supports a more traditional OAuth variant. Most of the
+white-label services support this, and for many of them this is the only
+alternative because they do not support personal login tokens. This method
+relies on pre-defined service-specific domain names and endpoints, and rclone
+need you to specify which service to configure. This also means that any
+changes to existing or additions of new white-label services needs an update
+in the rclone backend implementation.
+
+When configuring a remote, you must interactively login to an OAuth
+authorization web site, and a one-time authorization code is sent back to
+rclone behind the scene, which it uses to request an OAuth token. This means
+that you need to be on a machine with an internet-connected web browser. If you
+need it on a machine where this is not the case, then you will have to create
+the configuration on a different machine and copy it from there. The Jottacloud
+backend does not support the `rclone authorize` command. See the
+[remote setup docs](/remote_setup) for details.
+
+Jottacloud exerts some form of strict session management when authenticating
+using this method. This leads to some unexpected cases of the "invalid_grant"
+error described above, and effectively limits you to only use of a single
+active authentication on the same machine. I.e. you can only create a single
+rclone remote, and you can't even log in with the service's official desktop
+client while having a rclone remote configured, or else you will eventually get
+all sessions invalidated and are forced to re-authenticate.
+
+When you have successfully authenticated, there will be an entry in the
+"My logged in devices" list in the web interface representing your session. It
+will typically be listed with application name "Jottacloud for Desktop" or
+similar (it depends on the white-label service configuration).
+
+### Legacy
+
+Originally Jottacloud used an OAuth variant which required your account's
+username and password to be specified. When Jottacloud migrated to the newer
+methods, some white-label versions (those from Elkjøp) still used this legacy
+method for a long time. Currently there are no known uses of this, it is still
+supported by rclone, but the support will be removed in a future version.
## Configuration
@@ -125,7 +190,10 @@ n) New remote
s) Set configuration password
q) Quit config
n/s/q> n
+
+Enter name for new remote.
name> remote
+
Option Storage.
Type of storage to configure.
Choose a number from below, or type in your own value.
@@ -134,60 +202,63 @@ XX / Jottacloud
\ (jottacloud)
[snip]
Storage> jottacloud
+
+Option client_id.
+OAuth Client Id.
+Leave blank normally.
+Enter a value. Press Enter to leave empty.
+client_id>
+
+Option client_secret.
+OAuth Client Secret.
+Leave blank normally.
+Enter a value. Press Enter to leave empty.
+client_secret>
+
Edit advanced config?
y) Yes
n) No (default)
y/n> n
+
Option config_type.
-Select authentication type.
-Choose a number from below, or type in an existing string value.
+Type of authentication.
+Choose a number from below, or type in an existing value of type string.
Press Enter for the default (standard).
/ Standard authentication.
- 1 | Use this if you're a normal Jottacloud user.
+ | This is primarily supported by the official service, but may also be
+ | supported by some white-label services. It is designed for command-line
+ 1 | applications, and you will be asked to enter a single-use personal login
+ | token which you must manually generate from the account security settings
+ | in the web interface of your service.
\ (standard)
+ / Traditional authentication.
+ | This is supported by the official service and all white-label services
+ | that rclone knows about. You will be asked which service to connect to.
+ 2 | It has a limitation of only a single active authentication at a time. You
+ | need to be on, or have access to, a machine with an internet-connected
+ | web browser.
+ \ (traditional)
/ Legacy authentication.
- 2 | This is only required for certain whitelabel versions of Jottacloud and not recommended for normal users.
+ 3 | This is no longer supported by any known services and not recommended
+ | used. You will be asked for your account's username and password.
\ (legacy)
- / Telia Cloud authentication.
- 3 | Use this if you are using Telia Cloud.
- \ (telia)
- / Tele2 Cloud authentication.
- 4 | Use this if you are using Tele2 Cloud.
- \ (tele2)
- / Onlime Cloud authentication.
- 5 | Use this if you are using Onlime Cloud.
- \ (onlime)
config_type> 1
+
+Option config_login_token.
Personal login token.
-Generate here: https://www.jottacloud.com/web/secure
-Login Token>
+Generate it from the account security settings in the web interface of your
+service, for the official service on https://www.jottacloud.com/web/secure.
+Enter a value.
+config_login_token>
+
Use a non-standard device/mountpoint?
Choosing no, the default, will let you access the storage used for the archive
section of the official Jottacloud client. If you instead want to access the
sync or the backup section, for example, you must choose yes.
y) Yes
n) No (default)
-y/n> y
-Option config_device.
-The device to use. In standard setup the built-in Jotta device is used,
-which contains predefined mountpoints for archive, sync etc. All other devices
-are treated as backup devices by the official Jottacloud client. You may create
-a new by entering a unique name.
-Choose a number from below, or type in your own string value.
-Press Enter for the default (DESKTOP-3H31129).
- 1 > DESKTOP-3H31129
- 2 > Jotta
-config_device> 2
-Option config_mountpoint.
-The mountpoint to use for the built-in device Jotta.
-The standard setup is to use the Archive mountpoint. Most other mountpoints
-have very limited support in rclone and should generally be avoided.
-Choose a number from below, or type in an existing string value.
-Press Enter for the default (Archive).
- 1 > Archive
- 2 > Shared
- 3 > Sync
-config_mountpoint> 1
+y/n> n
+
Configuration complete.
Options:
- type: jottacloud
diff --git a/docs/content/onedrive.md b/docs/content/onedrive.md
index 6420037d1..2d85a60f8 100644
--- a/docs/content/onedrive.md
+++ b/docs/content/onedrive.md
@@ -100,7 +100,7 @@ y/e/d> y
```
See the [remote setup docs](/remote_setup/) for how to set it up on a
-machine with no Internet browser available.
+machine without an internet-connected web browser available.
Note that rclone runs a webserver on your local machine to collect the
token as returned from Microsoft. This only runs from the moment it
diff --git a/docs/content/pcloud.md b/docs/content/pcloud.md
index 1a99e3ef3..79a803081 100644
--- a/docs/content/pcloud.md
+++ b/docs/content/pcloud.md
@@ -71,7 +71,7 @@ y/e/d> y
```
See the [remote setup docs](/remote_setup/) for how to set it up on a
-machine with no Internet browser available.
+machine without an internet-connected web browser available.
Note if you are using remote config with rclone authorize while your pcloud
server is the EU region, you will need to set the hostname in 'Edit advanced
diff --git a/docs/content/premiumizeme.md b/docs/content/premiumizeme.md
index 045fbb02d..7b4ed67f2 100644
--- a/docs/content/premiumizeme.md
+++ b/docs/content/premiumizeme.md
@@ -65,7 +65,7 @@ y/e/d>
```
See the [remote setup docs](/remote_setup/) for how to set it up on a
-machine with no Internet browser available.
+machine without an internet-connected web browser available.
Note that rclone runs a webserver on your local machine to collect the
token as returned from premiumize.me. This only runs from the moment it opens
diff --git a/docs/content/putio.md b/docs/content/putio.md
index 4dba401a6..64d16a445 100644
--- a/docs/content/putio.md
+++ b/docs/content/putio.md
@@ -80,7 +80,7 @@ e/n/d/r/c/s/q> q
```
See the [remote setup docs](/remote_setup/) for how to set it up on a
-machine with no Internet browser available.
+machine without an internet-connected web browser available.
Note that rclone runs a webserver on your local machine to collect the
token as returned from put.io if using web browser to automatically
diff --git a/docs/content/remote_setup.md b/docs/content/remote_setup.md
index 5c6fd2d5c..e5c1cd052 100644
--- a/docs/content/remote_setup.md
+++ b/docs/content/remote_setup.md
@@ -6,22 +6,23 @@ description: "Configuring rclone up on a remote / headless machine"
# Configuring rclone on a remote / headless machine
Some of the configurations (those involving oauth2) require an
-Internet connected web browser.
+internet-connected web browser.
-If you are trying to set rclone up on a remote or headless box with no
-browser available on it (e.g. a NAS or a server in a datacenter) then
-you will need to use an alternative means of configuration. There are
-two ways of doing it, described below.
+If you are trying to set rclone up on a remote or headless machine with no
+browser available on it (e.g. a NAS or a server in a datacenter), then
+you will need to use an alternative means of configuration. There are
+three ways of doing it, described below.
## Configuring using rclone authorize
-On the headless box run `rclone` config but answer `N` to the `Use auto config?`
-question.
+On the headless machine run [rclone config](/commands/rclone_config), but
+answer `N` to the question `Use web browser to automatically authenticate rclone with remote?`.
```text
-Use auto config?
- * Say Y if not sure
- * Say N if you are working on a remote or headless machine
+Use web browser to automatically authenticate rclone with remote?
+ * Say Y if the machine running rclone has a web browser you can use
+ * Say N if running rclone on a (remote) machine without web browser access
+If not sure try Y. If Y failed, try N.
y) Yes (default)
n) No
@@ -33,33 +34,35 @@ a web browser available.
For more help and alternate methods see: https://rclone.org/remote_setup/
Execute the following on the machine with the web browser (same rclone
version recommended):
-rclone authorize "onedrive"
+ rclone authorize "onedrive"
Then paste the result.
Enter a value.
config_token>
```
-Then on your main desktop machine
+Then on your main desktop machine, run [rclone authorize](/commands/rclone_authorize/).
```text
rclone authorize "onedrive"
-If your browser doesn't open automatically go to the following link: http://127.0.0.1:53682/auth
-Log in and authorize rclone for access
-Waiting for code...
+NOTICE: Make sure your Redirect URL is set to "http://localhost:53682/" in your custom config.
+NOTICE: If your browser doesn't open automatically go to the following link: http://127.0.0.1:53682/auth?state=xxxxxxxxxxxxxxxxxxxxxx
+NOTICE: Log in and authorize rclone for access
+NOTICE: Waiting for code...
+
Got code
Paste the following into your remote machine --->
SECRET_TOKEN
<---End paste
```
-Then back to the headless box, paste in the code
+Then back to the headless machine, paste in the code.
```text
config_token> SECRET_TOKEN
--------------------
[acd12]
-client_id =
-client_secret =
+client_id =
+client_secret =
token = SECRET_TOKEN
--------------------
y) Yes this is OK
@@ -70,18 +73,19 @@ y/e/d>
## Configuring by copying the config file
-Rclone stores all of its config in a single configuration file. This
-can easily be copied to configure a remote rclone.
+Rclone stores all of its configuration in a single file. This can easily be
+copied to configure a remote rclone (although some backends does not support
+reusing the same configuration, consult your backend documentation to be
+sure).
-So first configure rclone on your desktop machine with
+Start by running [rclone config](/commands/rclone_config) to create the
+configuration file on your desktop machine.
```sh
rclone config
```
-to set up the config file.
-
-Find the config file by running `rclone config file`, for example
+Then locate the file by running [rclone config file](/commands/rclone_config_file).
```sh
$ rclone config file
@@ -89,31 +93,37 @@ Configuration file is stored at:
/home/user/.rclone.conf
```
-Now transfer it to the remote box (scp, cut paste, ftp, sftp, etc.) and
-place it in the correct place (use `rclone config file` on the remote
-box to find out where).
+Finally, transfer the file to the remote machine (scp, cut paste, ftp, sftp, etc.)
+and place it in the correct location (use [rclone config file](/commands/rclone_config_file)
+on the remote machine to find out where).
## Configuring using SSH Tunnel
-Linux and MacOS users can utilize SSH Tunnel to redirect the headless box
-port 53682 to local machine by using the following command:
+If you have an SSH client installed on your local machine, you can set up an
+SSH tunnel to redirect the port 53682 into the headless machine by using the
+following command:
```sh
ssh -L localhost:53682:localhost:53682 username@remote_server
```
-Then on the headless box run `rclone config` and answer `Y` to the
-`Use auto config?` question.
+Then on the headless machine run [rclone config](/commands/rclone_config) and
+answer `Y` to the question `Use web browser to automatically authenticate rclone with remote?`.
```text
-Use auto config?
- * Say Y if not sure
- * Say N if you are working on a remote or headless machine
+Use web browser to automatically authenticate rclone with remote?
+ * Say Y if the machine running rclone has a web browser you can use
+ * Say N if running rclone on a (remote) machine without web browser access
+If not sure try Y. If Y failed, try N.
y) Yes (default)
n) No
y/n> y
+NOTICE: Make sure your Redirect URL is set to "http://localhost:53682/" in your custom config.
+NOTICE: If your browser doesn't open automatically go to the following link: http://127.0.0.1:53682/auth?state=xxxxxxxxxxxxxxxxxxxxxx
+NOTICE: Log in and authorize rclone for access
+NOTICE: Waiting for code...
```
-Then copy and paste the auth url `http://127.0.0.1:53682/auth?state=xxxxxxxxxxxx`
-to the browser on your local machine, complete the auth and it is done.
+Finally, copy and paste the presented URL `http://127.0.0.1:53682/auth?state=xxxxxxxxxxxxxxxxxxxxxx`
+to the browser on your local machine, complete the auth and you are done.
diff --git a/docs/content/s3.md b/docs/content/s3.md
index 5b902854d..a930234eb 100644
--- a/docs/content/s3.md
+++ b/docs/content/s3.md
@@ -21,7 +21,9 @@ The S3 backend can be used with a number of different providers:
{{< provider name="DigitalOcean Spaces" home="https://www.digitalocean.com/products/object-storage/" config="/s3/#digitalocean-spaces" >}}
{{< provider name="Dreamhost" home="https://www.dreamhost.com/cloud/storage/" config="/s3/#dreamhost" >}}
{{< provider name="Exaba" home="https://exaba.com/" config="/s3/#exaba" >}}
+{{< provider name="FileLu S5 (S3-Compatible Object Storage)" home="https://s5lu.com/" config="/s3/#filelu-s5" >}}
{{< provider name="GCS" home="https://cloud.google.com/storage/docs" config="/s3/#google-cloud-storage" >}}
+{{< provider name="Hetzner" home="https://www.hetzner.com/storage/object-storage/" config="/s3/#hetzner" >}}
{{< provider name="Huawei OBS" home="https://www.huaweicloud.com/intl/en-us/product/obs.html" config="/s3/#huawei-obs" >}}
{{< provider name="IBM COS S3" home="http://www.ibm.com/cloud/object-storage" config="/s3/#ibm-cos-s3" >}}
{{< provider name="IDrive e2" home="https://www.idrive.com/e2/?refer=rclone" config="/s3/#idrive-e2" >}}
@@ -38,6 +40,7 @@ The S3 backend can be used with a number of different providers:
{{< provider name="Petabox" home="https://petabox.io/" config="/s3/#petabox" >}}
{{< provider name="Pure Storage FlashBlade" home="https://www.purestorage.com/products/unstructured-data-storage.html" config="/s3/#pure-storage-flashblade" >}}
{{< provider name="Qiniu Cloud Object Storage (Kodo)" home="https://www.qiniu.com/en/products/kodo" config="/s3/#qiniu" >}}
+{{< provider name="Rabata Cloud Storage" home="https://rabata.io" config="/s3/#Rabata" >}}
{{< provider name="RackCorp Object Storage" home="https://www.rackcorp.com/" config="/s3/#RackCorp" >}}
{{< provider name="Rclone Serve S3" home="/commands/rclone_serve_s3/" config="/s3/#rclone" >}}
{{< provider name="Scaleway" home="https://www.scaleway.com/en/object-storage/" config="/s3/#scaleway" >}}
@@ -3400,6 +3403,150 @@ endpoint = https://storage.googleapis.com
This is Google bug [#312292516](https://issuetracker.google.com/u/0/issues/312292516).
+### Hetzner Object Storage {#hetzner}
+
+Here is an example of making a [Hetzner Object Storage](https://www.hetzner.com/storage/object-storage/)
+configuration. First run:
+
+ rclone config
+
+This will guide you through an interactive setup process.
+
+```
+No remotes found, make a new one?
+n) New remote
+s) Set configuration password
+q) Quit config
+n/s/q> n
+
+Enter name for new remote.
+name> my-hetzner
+Option Storage.
+Type of storage to configure.
+Choose a number from below, or type in your own value.
+[snip]
+ XX / Amazon S3 Compliant Storage Providers including AWS, Alibaba, ArvanCloud, Ceph, ChinaMobile, Cloudflare, DigitalOcean, Dreamhost, GCS, Hetzner, HuaweiOBS, IBMCOS, IDrive, IONOS, LyveCloud, Leviia, Liara, Linode, Magalu, Minio, Netease, Outscale, Petabox, RackCorp, Rclone, Scaleway, SeaweedFS, Selectel, StackPath, Storj, Synology, TencentCOS, Wasabi, Qiniu and others
+ \ (s3)
+[snip]
+Storage> s3
+Option provider.
+Choose your S3 provider.
+Choose a number from below, or type in your own value.
+Press Enter to leave empty.
+[snip]
+XX / Hetzner Object Storage
+ \ (Hetzner)
+[snip]
+provider> Hetzner
+Option env_auth.
+Get AWS credentials from runtime (environment variables or EC2/ECS meta data if no env vars).
+Only applies if access_key_id and secret_access_key is blank.
+Choose a number from below, or type in your own boolean value (true or false).
+Press Enter for the default (false).
+ 1 / Enter AWS credentials in the next step.
+ \ (false)
+ 2 / Get AWS credentials from the environment (env vars or IAM).
+ \ (true)
+env_auth>
+Option access_key_id.
+AWS Access Key ID.
+Leave blank for anonymous access or runtime credentials.
+Enter a value. Press Enter to leave empty.
+access_key_id> ACCESS_KEY
+Option secret_access_key.
+AWS Secret Access Key (password).
+Leave blank for anonymous access or runtime credentials.
+Enter a value. Press Enter to leave empty.
+secret_access_key> SECRET_KEY
+Option region.
+Region to connect to.
+Choose a number from below, or type in your own value.
+Press Enter to leave empty.
+ 1 / Helsinki
+ \ (hel1)
+ 2 / Falkenstein
+ \ (fsn1)
+ 3 / Nuremberg
+ \ (nbg1)
+region>
+Option endpoint.
+Endpoint for Hetzner Object Storage
+Choose a number from below, or type in your own value.
+Press Enter to leave empty.
+ 1 / Helsinki
+ \ (hel1.your-objectstorage.com)
+ 2 / Falkenstein
+ \ (fsn1.your-objectstorage.com)
+ 3 / Nuremberg
+ \ (nbg1.your-objectstorage.com)
+endpoint>
+Option location_constraint.
+Location constraint - must be set to match the Region.
+Leave blank if not sure. Used when creating buckets only.
+Enter a value. Press Enter to leave empty.
+location_constraint>
+Option acl.
+Canned ACL used when creating buckets and storing or copying objects.
+This ACL is used for creating objects and if bucket_acl isn't set, for creating buckets too.
+For more info visit https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl
+Note that this ACL is applied when server-side copying objects as S3
+doesn't copy the ACL from the source but rather writes a fresh one.
+If the acl is an empty string then no X-Amz-Acl: header is added and
+the default (private) will be used.
+Choose a number from below, or type in your own value.
+Press Enter to leave empty.
+ / Owner gets FULL_CONTROL.
+ 1 | No one else has access rights (default).
+ \ (private)
+ / Owner gets FULL_CONTROL.
+ 2 | The AllUsers group gets READ access.
+ \ (public-read)
+acl>
+Edit advanced config?
+y) Yes
+n) No (default)
+y/n>
+Configuration complete.
+Options:
+- type: s3
+- provider: Hetzner
+- access_key_id: ACCESS_KEY
+- secret_access_key: SECRET_KEY
+Keep this "my-hetzner" remote?
+y) Yes this is OK (default)
+e) Edit this remote
+d) Delete this remote
+y/e/d>
+Current remotes:
+
+Name Type
+==== ====
+my-hetzner s3
+
+e) Edit existing remote
+n) New remote
+d) Delete remote
+r) Rename remote
+c) Copy remote
+s) Set configuration password
+q) Quit config
+e/n/d/r/c/s/q>
+```
+
+This will leave the config file looking like this.
+
+```
+[my-hetzner]
+type = s3
+provider = Hetzner
+access_key_id = ACCESS_KEY
+secret_access_key = SECRET_KEY
+region = hel1
+endpoint = hel1.your-objectstorage.com
+acl = private
+```
+
+
### Huawei OBS {#huawei-obs}
Object Storage Service (OBS) provides stable, secure, efficient, and easy-to-use cloud storage that lets you store virtually any volume of unstructured data in any format and access it from anywhere.
@@ -5635,6 +5782,244 @@ Name Type
qiniu s3
```
+### FileLu S5 {#filelu-s5}
+
+[FileLu S5 Object Storage](https://s5lu.com) is an S3-compatible object storage system.
+It provides multiple region options (Global, US-East, EU-Central, AP-Southeast, and ME-Central) while using a single endpoint (`s5lu.com`).
+FileLu S5 is designed for scalability, security, and simplicity, with predictable pricing and no hidden charges for data transfers or API requests.
+
+Here is an example of making a configuration. First run:
+
+```sh
+rclone config
+```
+
+This will guide you through an interactive setup process.
+
+```text
+No remotes found, make a new one\?
+n) New remote
+s) Set configuration password
+q) Quit config
+n/s/q> n
+
+Enter name for new remote.
+name> s5lu
+
+Option Storage.
+Type of storage to configure.
+Choose a number from below, or type in your own value.
+[snip]
+XX / Amazon S3 Compliant Storage Providers including AWS,... FileLu, ...
+ \ (s3)
+[snip]
+Storage> s3
+
+Option provider.
+Choose your S3 provider.
+Choose a number from below, or type in your own value.
+Press Enter to leave empty.
+[snip]
+XX / FileLu S5 Object Storage
+ \ (FileLu)
+[snip]
+provider> FileLu
+
+Option env_auth.
+Get AWS credentials from runtime (environment variables or EC2/ECS meta data if no env vars).
+Only applies if access_key_id and secret_access_key is blank.
+Choose a number from below, or type in your own boolean value (true or false).
+Press Enter for the default (false).
+ 1 / Enter AWS credentials in the next step.
+ \ (false)
+ 2 / Get AWS credentials from the environment (env vars or IAM).
+ \ (true)
+env_auth>
+
+Option access_key_id.
+AWS Access Key ID.
+Leave blank for anonymous access or runtime credentials.
+Enter a value. Press Enter to leave empty.
+access_key_id> XXX
+
+Option secret_access_key.
+AWS Secret Access Key (password).
+Leave blank for anonymous access or runtime credentials.
+Enter a value. Press Enter to leave empty.
+secret_access_key> XXX
+
+Option endpoint.
+Endpoint for S3 API.
+Required when using an S3 clone.
+Choose a number from below, or type in your own value.
+Press Enter to leave empty.
+ 1 / Global
+ \ (global)
+ 2 / North America (US-East)
+ \ (us-east)
+ 3 / Europe (EU-Central)
+ \ (eu-central)
+ 4 / Asia Pacific (AP-Southeast)
+ \ (ap-southeast)
+ 5 / Middle East (ME-Central)
+ \ (me-central)
+region> 1
+
+Edit advanced config?
+y) Yes
+n) No (default)
+y/n> n
+
+Configuration complete.
+Options:
+- type: s3
+- provider: FileLu
+- access_key_id: XXX
+- secret_access_key: XXX
+- endpoint: s5lu.com
+Keep this "s5lu" remote?
+y) Yes this is OK (default)
+e) Edit this remote
+d) Delete this remote
+y/e/d> y
+```
+
+This will leave the config file looking like this.
+
+```
+[s5lu]
+type = s3
+provider = FileLu
+access_key_id = XXX
+secret_access_key = XXX
+endpoint = s5lu.com
+```
+
+### Rabata {#Rabata}
+
+[Rabata](https://rabata.io) is an S3-compatible secure cloud storage service that offers flat, transparent pricing (no API request fees)
+while supporting standard S3 APIs. It is suitable for backup, application storage,media workflows, and archive use cases.
+
+Server side copy is not implemented with Rabata, also meaning modification time of objects cannot be updated.
+
+Rclone config:
+
+```
+rclone config
+No remotes found, make a new one?
+n) New remote
+s) Set configuration password
+q) Quit config
+n/s/q> n
+
+Enter name for new remote.
+name> Rabata
+
+Option Storage.
+Type of storage to configure.
+Choose a number from below, or type in your own value.
+[snip]
+XX / Amazon S3 Compliant Storage Providers including AWS, ...
+ \ (s3)
+[snip]
+Storage> s3
+
+Option provider.
+Choose your S3 provider.
+Choose a number from below, or type in your own value.
+Press Enter to leave empty.
+[snip]
+XX / Rabata Cloud Storage
+ \ (Rabata)
+[snip]
+provider> Rabata
+
+Option env_auth.
+Get AWS credentials from runtime (environment variables or EC2/ECS meta data if no env vars).
+Only applies if access_key_id and secret_access_key is blank.
+Choose a number from below, or type in your own boolean value (true or false).
+Press Enter for the default (false).
+ 1 / Enter AWS credentials in the next step.
+ \ (false)
+ 2 / Get AWS credentials from the environment (env vars or IAM).
+ \ (true)
+env_auth>
+
+Option access_key_id.
+AWS Access Key ID.
+Leave blank for anonymous access or runtime credentials.
+Enter a value. Press Enter to leave empty.
+access_key_id> ACCESS_KEY_ID
+
+Option secret_access_key.
+AWS Secret Access Key (password).
+Leave blank for anonymous access or runtime credentials.
+Enter a value. Press Enter to leave empty.
+secret_access_key> SECRET_ACCESS_KEY
+
+Option region.
+Region where your bucket will be created and your data stored.
+Choose a number from below, or type in your own value.
+Press Enter to leave empty.
+ 1 / US East (N. Virginia)
+ \ (us-east-1)
+ 2 / EU (Ireland)
+ \ (eu-west-1)
+ 3 / EU (London)
+ \ (eu-west-2)
+region> 3
+
+Option endpoint.
+Endpoint for Rabata Object Storage.
+Choose a number from below, or type in your own value.
+Press Enter to leave empty.
+ 1 / US East (N. Virginia)
+ \ (s3.us-east-1.rabata.io)
+ 2 / EU West (Ireland)
+ \ (s3.eu-west-1.rabata.io)
+ 3 / EU West (London)
+ \ (s3.eu-west-2.rabata.io)
+endpoint> 3
+
+Option location_constraint.
+location where your bucket will be created and your data stored.
+Choose a number from below, or type in your own value.
+Press Enter to leave empty.
+ 1 / US East (N. Virginia)
+ \ (us-east-1)
+ 2 / EU (Ireland)
+ \ (eu-west-1)
+ 3 / EU (London)
+ \ (eu-west-2)
+location_constraint> 3
+
+Edit advanced config?
+y) Yes
+n) No (default)
+y/n> n
+
+Configuration complete.
+Options:
+- type: s3
+- provider: Rabata
+- access_key_id: ACCESS_KEY_ID
+- secret_access_key: SECRET_ACCESS_KEY
+- region: eu-west-2
+- endpoint: s3.eu-west-2.rabata.io
+- location_constraint: eu-west-2
+Keep this "rabata" remote?
+y) Yes this is OK (default)
+e) Edit this remote
+d) Delete this remote
+y/e/d> y
+
+Current remotes:
+
+Name Type
+==== ====
+rabata s3
+```
+
### RackCorp {#RackCorp}
[RackCorp Object Storage](https://www.rackcorp.com/storage/s3storage) is an S3 compatible object storage platform from your friendly cloud provider RackCorp.
diff --git a/docs/content/sharefile.md b/docs/content/sharefile.md
index d8e8501c8..65280c876 100644
--- a/docs/content/sharefile.md
+++ b/docs/content/sharefile.md
@@ -84,7 +84,7 @@ y/e/d> y
```
See the [remote setup docs](/remote_setup/) for how to set it up on a
-machine with no Internet browser available.
+machine without an internet-connected web browser available.
Note that rclone runs a webserver on your local machine to collect the
token as returned from Citrix ShareFile. This only runs from the moment it opens
diff --git a/docs/content/smb.md b/docs/content/smb.md
index 0985ebcac..7fcd91266 100644
--- a/docs/content/smb.md
+++ b/docs/content/smb.md
@@ -21,7 +21,7 @@ you started to share on Windows. On smbd, it's the section title in `smb.conf`
(usually in `/etc/samba/`) file.
You can find shares by querying the root if you're unsure (e.g. `rclone lsd remote:`).
-You can't access to the shared printers from rclone, obviously.
+You can't access the shared printers from rclone, obviously.
You can't use Anonymous access for logging in. You have to use the `guest` user
with an empty password instead. The rclone client tries to avoid 8.3 names when
diff --git a/docs/content/yandex.md b/docs/content/yandex.md
index ae995df54..ae59e836c 100644
--- a/docs/content/yandex.md
+++ b/docs/content/yandex.md
@@ -61,7 +61,7 @@ y/e/d> y
```
See the [remote setup docs](/remote_setup/) for how to set it up on a
-machine with no Internet browser available.
+machine without an internet-connected web browser available.
Note that rclone runs a webserver on your local machine to collect the
token as returned from Yandex Disk. This only runs from the moment it
diff --git a/docs/content/zoho.md b/docs/content/zoho.md
index 9b889c26c..e538d7996 100644
--- a/docs/content/zoho.md
+++ b/docs/content/zoho.md
@@ -80,7 +80,7 @@ y/e/d>
```
See the [remote setup docs](/remote_setup/) for how to set it up on a
-machine with no Internet browser available.
+machine without an internet-connected web browser available.
Rclone runs a webserver on your local computer to collect the
authorization token from Zoho Workdrive. This is only from the moment
diff --git a/docs/layouts/chrome/navbar.html b/docs/layouts/chrome/navbar.html
index dd67fa9fa..3180139dd 100644
--- a/docs/layouts/chrome/navbar.html
+++ b/docs/layouts/chrome/navbar.html
@@ -19,6 +19,7 @@
Filtering
GUI
Remote Control
+ Remote Setup
Changelog
Bugs
FAQ
@@ -107,7 +108,6 @@
SMB / CIFS
Storj
SugarSync
- Terabox
Uloz.to
Uptobox
Union (merge backends)
diff --git a/docs/layouts/partials/version.html b/docs/layouts/partials/version.html
index 45e411a0a..3786da578 100644
--- a/docs/layouts/partials/version.html
+++ b/docs/layouts/partials/version.html
@@ -1 +1 @@
-v1.73.1
\ No newline at end of file
+v1.73.2
\ No newline at end of file
diff --git a/fs/accounting/accounting.go b/fs/accounting/accounting.go
index 009cb2c07..cb52e87eb 100644
--- a/fs/accounting/accounting.go
+++ b/fs/accounting/accounting.go
@@ -1,4 +1,4 @@
-// Package accounting providers an accounting and limiting reader
+// Package accounting providers an accounting and limiting reader
package accounting
import (
diff --git a/fs/accounting/accounting_other.go b/fs/accounting/accounting_other.go
index dc05b95dc..a16ca819e 100644
--- a/fs/accounting/accounting_other.go
+++ b/fs/accounting/accounting_other.go
@@ -1,4 +1,4 @@
-// Accounting and limiting reader
+// Accounting and limiting reader
// Non-unix specific functions.
//go:build !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris
diff --git a/fs/accounting/accounting_test.go b/fs/accounting/accounting_test.go
index 4b3a876df..d4e0bee20 100644
--- a/fs/accounting/accounting_test.go
+++ b/fs/accounting/accounting_test.go
@@ -1,4 +1,4 @@
-package accounting
+package accounting
import (
"bytes"
diff --git a/fs/accounting/accounting_unix.go b/fs/accounting/accounting_unix.go
index bacb0498a..3d4cdf612 100644
--- a/fs/accounting/accounting_unix.go
+++ b/fs/accounting/accounting_unix.go
@@ -1,4 +1,4 @@
-// Accounting and limiting reader
+// Accounting and limiting reader
// Unix specific functions.
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
diff --git a/fs/accounting/inprogress.go b/fs/accounting/inprogress.go
index 1eded676b..c3358c450 100644
--- a/fs/accounting/inprogress.go
+++ b/fs/accounting/inprogress.go
@@ -1,4 +1,4 @@
-package accounting
+package accounting
import (
"context"
diff --git a/fs/accounting/prometheus.go b/fs/accounting/prometheus.go
index 5ae07b749..3980cd52b 100644
--- a/fs/accounting/prometheus.go
+++ b/fs/accounting/prometheus.go
@@ -1,4 +1,4 @@
-package accounting
+package accounting
import (
"context"
diff --git a/fs/accounting/stats.go b/fs/accounting/stats.go
index 64f160953..361ab4518 100644
--- a/fs/accounting/stats.go
+++ b/fs/accounting/stats.go
@@ -1,4 +1,4 @@
-package accounting
+package accounting
import (
"bytes"
@@ -22,48 +22,52 @@ const (
averageStopAfter = time.Minute
)
-// MaxCompletedTransfers specifies maximum number of completed transfers in startedTransfers list
+// MaxCompletedTransfers specifies the default maximum number of
+// completed transfers in startedTransfers list. This can be adjusted
+// for a given StatsInfo by calling the SetMaxCompletedTransfers
+// method.
var MaxCompletedTransfers = 100
// StatsInfo accounts all transfers
// N.B.: if this struct is modified, please remember to also update sum() function in stats_groups
// to correctly count the updated fields
type StatsInfo struct {
- mu sync.RWMutex
- ctx context.Context
- ci *fs.ConfigInfo
- bytes int64
- errors int64
- lastError error
- fatalError bool
- retryError bool
- retryAfter time.Time
- checks int64
- checking *transferMap
- checkQueue int
- checkQueueSize int64
- transfers int64
- transferring *transferMap
- transferQueue int
- transferQueueSize int64
- listed int64
- renames int64
- renameQueue int
- renameQueueSize int64
- deletes int64
- deletesSize int64
- deletedDirs int64
- inProgress *inProgress
- startedTransfers []*Transfer // currently active transfers
- oldTimeRanges timeRanges // a merged list of time ranges for the transfers
- oldDuration time.Duration // duration of transfers we have culled
- group string
- startTime time.Time // the moment these stats were initialized or reset
- average averageValues
- serverSideCopies int64
- serverSideCopyBytes int64
- serverSideMoves int64
- serverSideMoveBytes int64
+ mu sync.RWMutex
+ ctx context.Context
+ ci *fs.ConfigInfo
+ bytes int64
+ errors int64
+ lastError error
+ fatalError bool
+ retryError bool
+ retryAfter time.Time
+ checks int64
+ checking *transferMap
+ checkQueue int
+ checkQueueSize int64
+ transfers int64
+ transferring *transferMap
+ transferQueue int
+ transferQueueSize int64
+ listed int64
+ renames int64
+ renameQueue int
+ renameQueueSize int64
+ deletes int64
+ deletesSize int64
+ deletedDirs int64
+ inProgress *inProgress
+ startedTransfers []*Transfer // currently active transfers
+ oldTimeRanges timeRanges // a merged list of time ranges for the transfers
+ oldDuration time.Duration // duration of transfers we have culled
+ group string
+ startTime time.Time // the moment these stats were initialized or reset
+ average averageValues
+ serverSideCopies int64
+ serverSideCopyBytes int64
+ serverSideMoves int64
+ serverSideMoveBytes int64
+ maxCompletedTransfers int
}
type averageValues struct {
@@ -81,17 +85,26 @@ type averageValues struct {
func NewStats(ctx context.Context) *StatsInfo {
ci := fs.GetConfig(ctx)
s := &StatsInfo{
- ctx: ctx,
- ci: ci,
- checking: newTransferMap(ci.Checkers, "checking"),
- transferring: newTransferMap(ci.Transfers, "transferring"),
- inProgress: newInProgress(ctx),
- startTime: time.Now(),
- average: averageValues{},
+ ctx: ctx,
+ ci: ci,
+ checking: newTransferMap(ci.Checkers, "checking"),
+ transferring: newTransferMap(ci.Transfers, "transferring"),
+ inProgress: newInProgress(ctx),
+ startTime: time.Now(),
+ average: averageValues{},
+ maxCompletedTransfers: MaxCompletedTransfers,
}
return s
}
+// SetMaxCompletedTransfers sets the maximum number of completed transfers to keep.
+func (s *StatsInfo) SetMaxCompletedTransfers(n int) *StatsInfo {
+ s.mu.Lock()
+ s.maxCompletedTransfers = n
+ s.mu.Unlock()
+ return s
+}
+
// RemoteStats returns stats for rc
//
// If short is true then the transfers and checkers won't be added.
@@ -912,22 +925,31 @@ func (s *StatsInfo) RemoveTransfer(transfer *Transfer) {
}
// PruneTransfers makes sure there aren't too many old transfers by removing
-// single finished transfer.
-func (s *StatsInfo) PruneTransfers() {
- if MaxCompletedTransfers < 0 {
- return
- }
+// a single finished transfer. Returns true if it removed a transfer.
+func (s *StatsInfo) PruneTransfers() bool {
s.mu.Lock()
+ defer s.mu.Unlock()
+ if s.maxCompletedTransfers < 0 {
+ return false
+ }
+ removed := false
// remove a transfer from the start if we are over quota
- if len(s.startedTransfers) > MaxCompletedTransfers+s.ci.Transfers {
+ if len(s.startedTransfers) > s.maxCompletedTransfers+s.ci.Transfers {
for i, tr := range s.startedTransfers {
if tr.IsDone() {
s._removeTransfer(tr, i)
+ removed = true
break
}
}
}
- s.mu.Unlock()
+ return removed
+}
+
+// RemoveDoneTransfers removes all Done transfers.
+func (s *StatsInfo) RemoveDoneTransfers() {
+ for s.PruneTransfers() {
+ }
}
// AddServerSideMove counts a server side move
diff --git a/fs/accounting/stats_groups.go b/fs/accounting/stats_groups.go
index a89866c7e..2f74d4a81 100644
--- a/fs/accounting/stats_groups.go
+++ b/fs/accounting/stats_groups.go
@@ -1,4 +1,4 @@
-package accounting
+package accounting
import (
"context"
diff --git a/fs/accounting/stats_groups_test.go b/fs/accounting/stats_groups_test.go
index e0d156672..fa5446254 100644
--- a/fs/accounting/stats_groups_test.go
+++ b/fs/accounting/stats_groups_test.go
@@ -1,4 +1,4 @@
-package accounting
+package accounting
import (
"context"
diff --git a/fs/accounting/stats_test.go b/fs/accounting/stats_test.go
index 6fcc09837..745207d95 100644
--- a/fs/accounting/stats_test.go
+++ b/fs/accounting/stats_test.go
@@ -1,4 +1,4 @@
-package accounting
+package accounting
import (
"context"
@@ -465,3 +465,27 @@ func TestPruneTransfers(t *testing.T) {
})
}
}
+
+func TestRemoveDoneTransfers(t *testing.T) {
+ ctx := context.Background()
+ s := NewStats(ctx)
+ const transfers = 10
+ for i := int64(1); i <= int64(transfers); i++ {
+ s.AddTransfer(&Transfer{
+ startedAt: time.Unix(i, 0),
+ completedAt: time.Unix(i+1, 0),
+ })
+ }
+
+ s.mu.Lock()
+ assert.Equal(t, time.Duration(transfers)*time.Second, s._totalDuration())
+ assert.Equal(t, transfers, len(s.startedTransfers))
+ s.mu.Unlock()
+
+ s.RemoveDoneTransfers()
+
+ s.mu.Lock()
+ assert.Equal(t, time.Duration(transfers)*time.Second, s._totalDuration())
+ assert.Equal(t, transfers, len(s.startedTransfers))
+ s.mu.Unlock()
+}
diff --git a/fs/accounting/token_bucket.go b/fs/accounting/token_bucket.go
index 00b36d344..d21fa4203 100644
--- a/fs/accounting/token_bucket.go
+++ b/fs/accounting/token_bucket.go
@@ -1,4 +1,4 @@
-package accounting
+package accounting
import (
"context"
diff --git a/fs/accounting/token_bucket_test.go b/fs/accounting/token_bucket_test.go
index 2c3e3bcce..dcba241df 100644
--- a/fs/accounting/token_bucket_test.go
+++ b/fs/accounting/token_bucket_test.go
@@ -1,4 +1,4 @@
-package accounting
+package accounting
import (
"context"
diff --git a/fs/accounting/tpslimit.go b/fs/accounting/tpslimit.go
index 9d6c73d5a..c6905ddc1 100644
--- a/fs/accounting/tpslimit.go
+++ b/fs/accounting/tpslimit.go
@@ -1,4 +1,4 @@
-package accounting
+package accounting
import (
"context"
diff --git a/fs/accounting/tpslimit_test.go b/fs/accounting/tpslimit_test.go
index a1bde981b..7def09765 100644
--- a/fs/accounting/tpslimit_test.go
+++ b/fs/accounting/tpslimit_test.go
@@ -1,4 +1,4 @@
-package accounting
+package accounting
import (
"context"
diff --git a/fs/accounting/transfer.go b/fs/accounting/transfer.go
index fc79c5ca1..e82a5427d 100644
--- a/fs/accounting/transfer.go
+++ b/fs/accounting/transfer.go
@@ -1,4 +1,4 @@
-package accounting
+package accounting
import (
"context"
diff --git a/fs/accounting/transfer_test.go b/fs/accounting/transfer_test.go
index 2aaa3d2c9..ebd9de94a 100644
--- a/fs/accounting/transfer_test.go
+++ b/fs/accounting/transfer_test.go
@@ -1,4 +1,4 @@
-package accounting
+package accounting
import (
"context"
diff --git a/fs/accounting/transfermap.go b/fs/accounting/transfermap.go
index 1f8f6c752..d565425c8 100644
--- a/fs/accounting/transfermap.go
+++ b/fs/accounting/transfermap.go
@@ -1,4 +1,4 @@
-package accounting
+package accounting
import (
"context"
diff --git a/fs/asyncreader/asyncreader.go b/fs/asyncreader/asyncreader.go
index 808419f9a..b97db2e21 100644
--- a/fs/asyncreader/asyncreader.go
+++ b/fs/asyncreader/asyncreader.go
@@ -1,4 +1,4 @@
-// Package asyncreader provides an asynchronous reader which reads
+// Package asyncreader provides an asynchronous reader which reads
// independently of write
package asyncreader
diff --git a/fs/asyncreader/asyncreader_test.go b/fs/asyncreader/asyncreader_test.go
index eae23b160..9fba4a40f 100644
--- a/fs/asyncreader/asyncreader_test.go
+++ b/fs/asyncreader/asyncreader_test.go
@@ -1,4 +1,4 @@
-package asyncreader
+package asyncreader
import (
"bufio"
diff --git a/fs/backend_config.go b/fs/backend_config.go
index c8d0e0ffe..21761b3e7 100644
--- a/fs/backend_config.go
+++ b/fs/backend_config.go
@@ -1,4 +1,4 @@
-// Structures and utilities for backend config
+// Structures and utilities for backend config
//
//
diff --git a/fs/backend_config_test.go b/fs/backend_config_test.go
index 9c373693f..ddefde261 100644
--- a/fs/backend_config_test.go
+++ b/fs/backend_config_test.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"fmt"
diff --git a/fs/bits.go b/fs/bits.go
index 79c1b2887..5c70e5529 100644
--- a/fs/bits.go
+++ b/fs/bits.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"encoding/json"
diff --git a/fs/bits_test.go b/fs/bits_test.go
index ad5f07400..4c8fcdafa 100644
--- a/fs/bits_test.go
+++ b/fs/bits_test.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"encoding/json"
diff --git a/fs/bwtimetable.go b/fs/bwtimetable.go
index 9a2173862..2dad6e907 100644
--- a/fs/bwtimetable.go
+++ b/fs/bwtimetable.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"encoding/json"
@@ -151,7 +151,7 @@ func (x *BwTimetable) Set(s string) error {
}
// Split the timetable string by both spaces and semicolons
- for _, tok := range strings.FieldsFunc(s, func(r rune) bool {
+ for tok := range strings.FieldsFuncSeq(s, func(r rune) bool {
return r == ' ' || r == ';'
}) {
tv := strings.Split(tok, ",")
diff --git a/fs/bwtimetable_test.go b/fs/bwtimetable_test.go
index b1d45f567..e9ef9b6b8 100644
--- a/fs/bwtimetable_test.go
+++ b/fs/bwtimetable_test.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"encoding/json"
diff --git a/fs/cache/cache.go b/fs/cache/cache.go
index 45ba69b99..5f0331bf7 100644
--- a/fs/cache/cache.go
+++ b/fs/cache/cache.go
@@ -1,4 +1,4 @@
-// Package cache implements the Fs cache
+// Package cache implements the Fs cache
package cache
import (
diff --git a/fs/cache/cache_test.go b/fs/cache/cache_test.go
index 0448f0244..1f82ba1fe 100644
--- a/fs/cache/cache_test.go
+++ b/fs/cache/cache_test.go
@@ -1,4 +1,4 @@
-package cache
+package cache
import (
"context"
diff --git a/fs/chunkedreader/chunkedreader.go b/fs/chunkedreader/chunkedreader.go
index 2d36dcee1..b84ce9053 100644
--- a/fs/chunkedreader/chunkedreader.go
+++ b/fs/chunkedreader/chunkedreader.go
@@ -1,4 +1,4 @@
-// Package chunkedreader provides functionality for reading a stream in chunks.
+// Package chunkedreader provides functionality for reading a stream in chunks.
package chunkedreader
import (
diff --git a/fs/chunkedreader/chunkedreader_test.go b/fs/chunkedreader/chunkedreader_test.go
index c3d28e3d8..182309dd3 100644
--- a/fs/chunkedreader/chunkedreader_test.go
+++ b/fs/chunkedreader/chunkedreader_test.go
@@ -1,4 +1,4 @@
-package chunkedreader
+package chunkedreader
import (
"context"
diff --git a/fs/chunkedreader/parallel.go b/fs/chunkedreader/parallel.go
index 15d0da4a1..340e63c26 100644
--- a/fs/chunkedreader/parallel.go
+++ b/fs/chunkedreader/parallel.go
@@ -1,4 +1,4 @@
-package chunkedreader
+package chunkedreader
import (
"context"
diff --git a/fs/chunkedreader/parallel_test.go b/fs/chunkedreader/parallel_test.go
index 5df6492e9..e4dd44bf5 100644
--- a/fs/chunkedreader/parallel_test.go
+++ b/fs/chunkedreader/parallel_test.go
@@ -1,4 +1,4 @@
-package chunkedreader
+package chunkedreader
import (
"context"
diff --git a/fs/chunkedreader/sequential.go b/fs/chunkedreader/sequential.go
index ee9400fb4..996ef2a3a 100644
--- a/fs/chunkedreader/sequential.go
+++ b/fs/chunkedreader/sequential.go
@@ -1,4 +1,4 @@
-package chunkedreader
+package chunkedreader
import (
"context"
diff --git a/fs/chunkedreader/sequential_test.go b/fs/chunkedreader/sequential_test.go
index b384c893d..a4beaa7be 100644
--- a/fs/chunkedreader/sequential_test.go
+++ b/fs/chunkedreader/sequential_test.go
@@ -1,4 +1,4 @@
-package chunkedreader
+package chunkedreader
import (
"testing"
diff --git a/fs/chunksize/chunksize.go b/fs/chunksize/chunksize.go
index d96c780c4..723e47c8b 100644
--- a/fs/chunksize/chunksize.go
+++ b/fs/chunksize/chunksize.go
@@ -1,4 +1,4 @@
-// Package chunksize calculates a suitable chunk size for large uploads
+// Package chunksize calculates a suitable chunk size for large uploads
package chunksize
import (
diff --git a/fs/chunksize/chunksize_test.go b/fs/chunksize/chunksize_test.go
index a1a778f94..66e3b46a0 100644
--- a/fs/chunksize/chunksize_test.go
+++ b/fs/chunksize/chunksize_test.go
@@ -1,4 +1,4 @@
-package chunksize
+package chunksize
import (
"testing"
diff --git a/fs/config.go b/fs/config.go
index ce0a1df9e..6ba18700a 100644
--- a/fs/config.go
+++ b/fs/config.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"context"
@@ -464,6 +464,11 @@ var ConfigOptionsInfo = Options{{
Default: SizeSuffix(64 * 1024 * 1024),
Help: "Chunk size for multi-thread downloads / uploads, if not set by filesystem",
Groups: "Copy",
+}, {
+ Name: "multi_thread_resume",
+ Default: false,
+ Help: "Enable resume support for multi-thread downloads on local filesystem (uses state files to track progress)",
+ Groups: "Copy",
}, {
Name: "use_json_log",
Default: false,
@@ -562,9 +567,9 @@ var ConfigOptionsInfo = Options{{
Help: "Transform paths during the copy process.",
Groups: "Copy",
}, {
- Name: "http_proxy",
+ Name: "proxy",
Default: "",
- Help: "HTTP proxy URL.",
+ Help: "Proxy URL, e.g. http://127.0.0.1:8080 or socks5://127.0.0.1:1080",
Groups: "Networking",
}}
@@ -656,6 +661,7 @@ type ConfigInfo struct {
MultiThreadSet bool `config:"multi_thread_set"` // whether MultiThreadStreams was set (set in fs/config/configflags)
MultiThreadChunkSize SizeSuffix `config:"multi_thread_chunk_size"` // Chunk size for multi-thread downloads / uploads, if not set by filesystem
MultiThreadWriteBufferSize SizeSuffix `config:"multi_thread_write_buffer_size"`
+ MultiThreadResume bool `config:"multi_thread_resume"`
OrderBy string `config:"order_by"` // instructions on how to order the transfer
UploadHeaders []*HTTPOption `config:"upload_headers"`
DownloadHeaders []*HTTPOption `config:"download_headers"`
@@ -679,7 +685,7 @@ type ConfigInfo struct {
MetadataMapper SpaceSepList `config:"metadata_mapper"`
MaxConnections int `config:"max_connections"`
NameTransform []string `config:"name_transform"`
- HTTPProxy string `config:"http_proxy"`
+ Proxy string `config:"proxy"`
}
func init() {
diff --git a/fs/config/authorize.go b/fs/config/authorize.go
index 8f3aa86ce..e7e0285a1 100644
--- a/fs/config/authorize.go
+++ b/fs/config/authorize.go
@@ -1,4 +1,4 @@
-package config
+package config
import (
"context"
@@ -12,9 +12,9 @@ import (
//
// It expects 1, 2 or 3 arguments
//
-// rclone authorize "fs name"
-// rclone authorize "fs name" "base64 encoded JSON blob"
-// rclone authorize "fs name" "client id" "client secret"
+// rclone authorize "backend name"
+// rclone authorize "backend name" "base64 encoded JSON blob"
+// rclone authorize "backend name" "client id" "client secret"
func Authorize(ctx context.Context, args []string, noAutoBrowser bool, templateFile string) error {
ctx = suppressConfirm(ctx)
ctx = fs.ConfigOAuthOnly(ctx)
diff --git a/fs/config/config.go b/fs/config/config.go
index 617780c9c..1390aae67 100644
--- a/fs/config/config.go
+++ b/fs/config/config.go
@@ -1,4 +1,4 @@
-// Package config reads, writes and edits the config file and deals with command line flags
+// Package config reads, writes and edits the config file and deals with command line flags
package config
import (
diff --git a/fs/config/config_read_password.go b/fs/config/config_read_password.go
index 9d302a14f..f67458271 100644
--- a/fs/config/config_read_password.go
+++ b/fs/config/config_read_password.go
@@ -1,4 +1,4 @@
-// ReadPassword for OSes which are supported by golang.org/x/term
+// ReadPassword for OSes which are supported by golang.org/x/term
// See https://github.com/golang/go/issues/14441 - plan9
//go:build !plan9
diff --git a/fs/config/config_read_password_unsupported.go b/fs/config/config_read_password_unsupported.go
index 8918ce425..f034b12c3 100644
--- a/fs/config/config_read_password_unsupported.go
+++ b/fs/config/config_read_password_unsupported.go
@@ -1,4 +1,4 @@
-// ReadPassword for OSes which are not supported by golang.org/x/term
+// ReadPassword for OSes which are not supported by golang.org/x/term
// See https://github.com/golang/go/issues/14441 - plan9
//go:build plan9
diff --git a/fs/config/config_test.go b/fs/config/config_test.go
index 7c4a503bf..d6997cf24 100644
--- a/fs/config/config_test.go
+++ b/fs/config/config_test.go
@@ -1,4 +1,4 @@
-// These are in an external package because we need to import configfile
+// These are in an external package because we need to import configfile
package config_test
diff --git a/fs/config/configfile/configfile.go b/fs/config/configfile/configfile.go
index c6a09fe50..7580f36e1 100644
--- a/fs/config/configfile/configfile.go
+++ b/fs/config/configfile/configfile.go
@@ -1,4 +1,4 @@
-// Package configfile implements a config file loader and saver
+// Package configfile implements a config file loader and saver
package configfile
import (
diff --git a/fs/config/configfile/configfile_other.go b/fs/config/configfile/configfile_other.go
index c70a51603..de06bfd7f 100644
--- a/fs/config/configfile/configfile_other.go
+++ b/fs/config/configfile/configfile_other.go
@@ -1,4 +1,4 @@
-// Read, write and edit the config file
+// Read, write and edit the config file
// Non-unix specific functions.
//go:build !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris
diff --git a/fs/config/configfile/configfile_test.go b/fs/config/configfile/configfile_test.go
index 8ec7cd518..de0108485 100644
--- a/fs/config/configfile/configfile_test.go
+++ b/fs/config/configfile/configfile_test.go
@@ -1,4 +1,4 @@
-package configfile
+package configfile
import (
"fmt"
diff --git a/fs/config/configfile/configfile_unix.go b/fs/config/configfile/configfile_unix.go
index d5e8ee3dd..9f1d0b3e3 100644
--- a/fs/config/configfile/configfile_unix.go
+++ b/fs/config/configfile/configfile_unix.go
@@ -1,4 +1,4 @@
-// Read, write and edit the config file
+// Read, write and edit the config file
// Unix specific functions.
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
diff --git a/fs/config/configflags/configflags.go b/fs/config/configflags/configflags.go
index ab8f10286..892dbd5ab 100644
--- a/fs/config/configflags/configflags.go
+++ b/fs/config/configflags/configflags.go
@@ -1,4 +1,4 @@
-// Package configflags defines the flags used by rclone. It is
+// Package configflags defines the flags used by rclone. It is
// decoupled into a separate package so it can be replaced.
package configflags
diff --git a/fs/config/configmap/configmap.go b/fs/config/configmap/configmap.go
index ee8d4918f..0cc0d7bb1 100644
--- a/fs/config/configmap/configmap.go
+++ b/fs/config/configmap/configmap.go
@@ -1,4 +1,4 @@
-// Package configmap provides an abstraction for reading and writing config
+// Package configmap provides an abstraction for reading and writing config
package configmap
import (
@@ -136,9 +136,11 @@ func (c Simple) Set(key, value string) {
c[key] = value
}
-// String the map value the same way the config parser does, but with
+// string the map value the same way the config parser does, but with
// sorted keys for reproducibility.
-func (c Simple) String() string {
+//
+// If human is set then use fewer quotes.
+func (c Simple) string(human bool) string {
var ks = make([]string, 0, len(c))
for k := range c {
ks = append(ks, k)
@@ -150,20 +152,41 @@ func (c Simple) String() string {
out.WriteRune(',')
}
out.WriteString(k)
+ v := c[k]
+ if human && v == "true" {
+ continue
+ }
out.WriteRune('=')
- out.WriteRune('\'')
- for _, ch := range c[k] {
- out.WriteRune(ch)
- // Escape ' as ''
- if ch == '\'' {
+ if !human || strings.ContainsAny(v, `'":=,`) {
+ out.WriteRune('\'')
+ for _, ch := range v {
out.WriteRune(ch)
+ // Escape ' as ''
+ if ch == '\'' {
+ out.WriteRune(ch)
+ }
}
+ out.WriteRune('\'')
+ } else {
+ out.WriteString(v)
}
- out.WriteRune('\'')
}
return out.String()
}
+// Human converts the map value the same way the config parser does,
+// but with sorted keys for reproducibility. This does it in human
+// readable form with fewer quotes.
+func (c Simple) Human() string {
+ return c.string(true)
+}
+
+// String the map value the same way the config parser does, but with
+// sorted keys for reproducibility.
+func (c Simple) String() string {
+ return c.string(false)
+}
+
// Encode from c into a string suitable for putting on the command line
func (c Simple) Encode() (string, error) {
if len(c) == 0 {
diff --git a/fs/config/configmap/configmap_external_test.go b/fs/config/configmap/configmap_external_test.go
new file mode 100644
index 000000000..d24b4f60b
--- /dev/null
+++ b/fs/config/configmap/configmap_external_test.go
@@ -0,0 +1,121 @@
+package configmap_test
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/rclone/rclone/fs/config/configmap"
+ "github.com/rclone/rclone/fs/fspath"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestSimpleString(t *testing.T) {
+ for _, tt := range []struct {
+ name string
+ want string
+ in configmap.Simple
+ }{
+ {name: "Nil", want: "", in: configmap.Simple(nil)},
+ {name: "Empty", want: "", in: configmap.Simple{}},
+ {name: "Basic", want: "config1='one'", in: configmap.Simple{
+ "config1": "one",
+ }},
+ {name: "Truthy", want: "config1='true',config2='true'", in: configmap.Simple{
+ "config1": "true",
+ "config2": "true",
+ }},
+ {name: "Quotable", want: `config1='"one"',config2=':two:',config3='''three''',config4='=four=',config5=',five,'`, in: configmap.Simple{
+ "config1": `"one"`,
+ "config2": `:two:`,
+ "config3": `'three'`,
+ "config4": `=four=`,
+ "config5": `,five,`,
+ }},
+ {name: "Order", want: "config1='one',config2='two',config3='three',config4='four',config5='five'", in: configmap.Simple{
+ "config5": "five",
+ "config4": "four",
+ "config3": "three",
+ "config2": "two",
+ "config1": "one",
+ }},
+ {name: "Escaping", want: "apple='',config1='o''n''e'", in: configmap.Simple{
+ "config1": "o'n'e",
+ "apple": "",
+ }},
+ } {
+ t.Run(tt.name, func(t *testing.T) {
+ // Check forwards
+ params := tt.in.String()
+ assert.Equal(t, tt.want, params)
+
+ // Check config round trips through config parser
+ remote := ":local," + params + ":"
+ if params == "" {
+ remote = ":local:"
+ }
+ what := fmt.Sprintf("remote = %q", remote)
+ parsed, err := fspath.Parse(remote)
+ require.NoError(t, err, what)
+ if len(parsed.Config) != 0 || len(tt.in) != 0 {
+ assert.Equal(t, tt.in, parsed.Config, what)
+ }
+ })
+ }
+
+}
+
+func TestSimpleHuman(t *testing.T) {
+ for _, tt := range []struct {
+ name string
+ want string
+ in configmap.Simple
+ }{
+ {name: "Nil", want: "", in: configmap.Simple(nil)},
+ {name: "Empty", want: "", in: configmap.Simple{}},
+ {name: "Basic", want: "config1=one", in: configmap.Simple{
+ "config1": "one",
+ }},
+ {name: "Truthy", want: "config1,config2", in: configmap.Simple{
+ "config1": "true",
+ "config2": "true",
+ }},
+ {name: "Quotable", want: `config1='"one"',config2=':two:',config3='''three''',config4='=four=',config5=',five,'`, in: configmap.Simple{
+ "config1": `"one"`,
+ "config2": `:two:`,
+ "config3": `'three'`,
+ "config4": `=four=`,
+ "config5": `,five,`,
+ }},
+ {name: "Order", want: "config1=one,config2=two,config3=three,config4=four,config5=five", in: configmap.Simple{
+ "config5": "five",
+ "config4": "four",
+ "config3": "three",
+ "config2": "two",
+ "config1": "one",
+ }},
+ {name: "Escaping", want: "apple=,config1='o''n''e'", in: configmap.Simple{
+ "config1": "o'n'e",
+ "apple": "",
+ }},
+ } {
+ t.Run(tt.name, func(t *testing.T) {
+ // Check forwards
+ params := tt.in.Human()
+ assert.Equal(t, tt.want, params)
+
+ // Check config round trips through config parser
+ remote := ":local," + params + ":"
+ if params == "" {
+ remote = ":local:"
+ }
+ what := fmt.Sprintf("remote = %q", remote)
+ parsed, err := fspath.Parse(remote)
+ require.NoError(t, err, what)
+ if len(parsed.Config) != 0 || len(tt.in) != 0 {
+ assert.Equal(t, tt.in, parsed.Config, what)
+ }
+ })
+ }
+
+}
diff --git a/fs/config/configmap/configmap_test.go b/fs/config/configmap/configmap_test.go
index 85d4686f1..8e1951976 100644
--- a/fs/config/configmap/configmap_test.go
+++ b/fs/config/configmap/configmap_test.go
@@ -1,4 +1,4 @@
-package configmap
+package configmap
import (
"encoding/base64"
@@ -246,30 +246,6 @@ func TestConfigMapClearSetters(t *testing.T) {
assert.Equal(t, []Setter(nil), m.setters)
}
-func TestSimpleString(t *testing.T) {
- // Basic
- assert.Equal(t, "", Simple(nil).String())
- assert.Equal(t, "", Simple{}.String())
- assert.Equal(t, "config1='one'", Simple{
- "config1": "one",
- }.String())
-
- // Check ordering
- assert.Equal(t, "config1='one',config2='two',config3='three',config4='four',config5='five'", Simple{
- "config5": "five",
- "config4": "four",
- "config3": "three",
- "config2": "two",
- "config1": "one",
- }.String())
-
- // Check escaping
- assert.Equal(t, "apple='',config1='o''n''e'", Simple{
- "config1": "o'n'e",
- "apple": "",
- }.String())
-}
-
func TestSimpleEncode(t *testing.T) {
for _, test := range []struct {
in Simple
diff --git a/fs/config/configstruct/configstruct.go b/fs/config/configstruct/configstruct.go
index f90806a2d..aaaee1d78 100644
--- a/fs/config/configstruct/configstruct.go
+++ b/fs/config/configstruct/configstruct.go
@@ -1,4 +1,4 @@
-// Package configstruct parses unstructured maps into structures
+// Package configstruct parses unstructured maps into structures
package configstruct
import (
@@ -261,7 +261,7 @@ func Set(config configmap.Getter, opt any) (err error) {
}
// setIfSameType set aPtr with b if they are the same type or returns false.
-func setIfSameType(aPtr interface{}, b interface{}) bool {
+func setIfSameType(aPtr any, b any) bool {
aVal := reflect.ValueOf(aPtr).Elem()
bVal := reflect.ValueOf(b)
diff --git a/fs/config/configstruct/configstruct_test.go b/fs/config/configstruct/configstruct_test.go
index fb0337220..a464ada3e 100644
--- a/fs/config/configstruct/configstruct_test.go
+++ b/fs/config/configstruct/configstruct_test.go
@@ -1,4 +1,4 @@
-package configstruct_test
+package configstruct_test
import (
"fmt"
diff --git a/fs/config/configstruct/internal_test.go b/fs/config/configstruct/internal_test.go
index 2f3727d1e..1fd1aa6a4 100644
--- a/fs/config/configstruct/internal_test.go
+++ b/fs/config/configstruct/internal_test.go
@@ -1,4 +1,4 @@
-package configstruct
+package configstruct
import (
"testing"
diff --git a/fs/config/crypt.go b/fs/config/crypt.go
index 4b356580c..0c55516ad 100644
--- a/fs/config/crypt.go
+++ b/fs/config/crypt.go
@@ -1,4 +1,4 @@
-package config
+package config
import (
"bufio"
diff --git a/fs/config/crypt_internal_test.go b/fs/config/crypt_internal_test.go
index 2d19643c5..82fc20d1d 100644
--- a/fs/config/crypt_internal_test.go
+++ b/fs/config/crypt_internal_test.go
@@ -1,4 +1,4 @@
-package config
+package config
import (
"context"
diff --git a/fs/config/crypt_test.go b/fs/config/crypt_test.go
index 73c3bf6f6..1b94fd692 100644
--- a/fs/config/crypt_test.go
+++ b/fs/config/crypt_test.go
@@ -1,4 +1,4 @@
-// These are in an external package because we need to import configfile
+// These are in an external package because we need to import configfile
//
// Internal tests are in crypt_internal_test.go
diff --git a/fs/config/default_storage.go b/fs/config/default_storage.go
index 4debac0be..aa457c073 100644
--- a/fs/config/default_storage.go
+++ b/fs/config/default_storage.go
@@ -1,4 +1,4 @@
-package config
+package config
import (
"encoding/json"
diff --git a/fs/config/default_storage_test.go b/fs/config/default_storage_test.go
index 4f1238c55..aa4683279 100644
--- a/fs/config/default_storage_test.go
+++ b/fs/config/default_storage_test.go
@@ -1,4 +1,4 @@
-package config
+package config
import (
"testing"
diff --git a/fs/config/flags/flags.go b/fs/config/flags/flags.go
index 953a3d0fd..ac8a2c6cf 100644
--- a/fs/config/flags/flags.go
+++ b/fs/config/flags/flags.go
@@ -1,4 +1,4 @@
-// Package flags contains enhanced versions of spf13/pflag flag
+// Package flags contains enhanced versions of spf13/pflag flag
// routines which will read from the environment also.
package flags
@@ -70,7 +70,7 @@ func (gs *Groups) Include(groupsString string) *Groups {
return gs
}
want := map[string]bool{}
- for _, groupName := range strings.Split(groupsString, ",") {
+ for groupName := range strings.SplitSeq(groupsString, ",") {
_, ok := All.ByName[groupName]
if !ok {
fs.Fatalf(nil, "Couldn't find group %q in command annotation", groupName)
@@ -173,7 +173,7 @@ func installFlag(flags *pflag.FlagSet, name string, groupsString string) {
// Add flag to Group if it is a global flag
if groupsString != "" && flags == pflag.CommandLine {
- for _, groupName := range strings.Split(groupsString, ",") {
+ for groupName := range strings.SplitSeq(groupsString, ",") {
if groupName == "rc-" {
groupName = "RC"
}
diff --git a/fs/config/obscure/obscure.go b/fs/config/obscure/obscure.go
index 713e4d248..6600eb894 100644
--- a/fs/config/obscure/obscure.go
+++ b/fs/config/obscure/obscure.go
@@ -1,4 +1,4 @@
-// Package obscure contains the Obscure and Reveal commands
+// Package obscure contains the Obscure and Reveal commands
package obscure
import (
diff --git a/fs/config/obscure/obscure_test.go b/fs/config/obscure/obscure_test.go
index d7ea43121..bd5b9beea 100644
--- a/fs/config/obscure/obscure_test.go
+++ b/fs/config/obscure/obscure_test.go
@@ -1,4 +1,4 @@
-package obscure
+package obscure
import (
"bytes"
diff --git a/fs/config/rc.go b/fs/config/rc.go
index c87dcb318..95f62bf82 100644
--- a/fs/config/rc.go
+++ b/fs/config/rc.go
@@ -1,4 +1,4 @@
-package config
+package config
import (
"context"
@@ -145,7 +145,6 @@ func rcProviders(ctx context.Context, in rc.Params) (out rc.Params, err error) {
func init() {
for _, name := range []string{"create", "update", "password"} {
- name := name
extraHelp := ""
if name == "create" {
extraHelp = "- type - type of the new remote\n"
diff --git a/fs/config/rc_test.go b/fs/config/rc_test.go
index 682a8fe71..d722d322d 100644
--- a/fs/config/rc_test.go
+++ b/fs/config/rc_test.go
@@ -1,4 +1,4 @@
-package config_test
+package config_test
import (
"context"
diff --git a/fs/config/ui.go b/fs/config/ui.go
index 6e31923fe..cc94313f1 100644
--- a/fs/config/ui.go
+++ b/fs/config/ui.go
@@ -1,4 +1,4 @@
-// Textual user interface parts of the config system
+// Textual user interface parts of the config system
package config
diff --git a/fs/config/ui_test.go b/fs/config/ui_test.go
index 7320aca47..e0f6234fb 100644
--- a/fs/config/ui_test.go
+++ b/fs/config/ui_test.go
@@ -1,4 +1,4 @@
-// These are in an external package because we need to import configfile
+// These are in an external package because we need to import configfile
//
// Internal tests are in ui_internal_test.go
diff --git a/fs/config_list.go b/fs/config_list.go
index 31ab0eba0..b842f2e25 100644
--- a/fs/config_list.go
+++ b/fs/config_list.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"bytes"
diff --git a/fs/config_list_test.go b/fs/config_list_test.go
index 7aa1e9eed..a7989d741 100644
--- a/fs/config_list_test.go
+++ b/fs/config_list_test.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"fmt"
diff --git a/fs/config_test.go b/fs/config_test.go
index 141f1185d..3caa14741 100644
--- a/fs/config_test.go
+++ b/fs/config_test.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"context"
diff --git a/fs/configmap.go b/fs/configmap.go
index 6f5ba5d52..a519492f5 100644
--- a/fs/configmap.go
+++ b/fs/configmap.go
@@ -1,4 +1,4 @@
-// Getters and Setters for ConfigMap
+// Getters and Setters for ConfigMap
package fs
diff --git a/fs/countsuffix.go b/fs/countsuffix.go
index b3e1daa9e..f8c6a0c96 100644
--- a/fs/countsuffix.go
+++ b/fs/countsuffix.go
@@ -1,4 +1,4 @@
-package fs
+package fs
// CountSuffix is parsed by flag with k/M/G decimal suffixes
import (
diff --git a/fs/countsuffix_test.go b/fs/countsuffix_test.go
index 58fca960a..767fb99a2 100644
--- a/fs/countsuffix_test.go
+++ b/fs/countsuffix_test.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"encoding/json"
diff --git a/fs/cutoffmode.go b/fs/cutoffmode.go
index 759068ddc..6a3d036ee 100644
--- a/fs/cutoffmode.go
+++ b/fs/cutoffmode.go
@@ -1,4 +1,4 @@
-package fs
+package fs
type cutoffModeChoices struct{}
diff --git a/fs/cutoffmode_test.go b/fs/cutoffmode_test.go
index 389350278..fb242c6a5 100644
--- a/fs/cutoffmode_test.go
+++ b/fs/cutoffmode_test.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"encoding/json"
diff --git a/fs/daemon_other.go b/fs/daemon_other.go
index 4e8043701..3ac76fd05 100644
--- a/fs/daemon_other.go
+++ b/fs/daemon_other.go
@@ -1,4 +1,4 @@
-// Daemonization stub for non-Unix platforms (common definitions)
+// Daemonization stub for non-Unix platforms (common definitions)
//go:build windows || plan9 || js
diff --git a/fs/daemon_unix.go b/fs/daemon_unix.go
index 04c128508..cf6117de4 100644
--- a/fs/daemon_unix.go
+++ b/fs/daemon_unix.go
@@ -1,4 +1,4 @@
-// Daemonization interface for Unix platforms (common definitions)
+// Daemonization interface for Unix platforms (common definitions)
//go:build !windows && !plan9 && !js
diff --git a/fs/deletemode.go b/fs/deletemode.go
index 9e16373d9..ee365c34c 100644
--- a/fs/deletemode.go
+++ b/fs/deletemode.go
@@ -1,4 +1,4 @@
-package fs
+package fs
// DeleteMode describes the possible delete modes in the config
type DeleteMode byte
diff --git a/fs/dir.go b/fs/dir.go
index 5830c6f38..d0eb54158 100644
--- a/fs/dir.go
+++ b/fs/dir.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"context"
diff --git a/fs/dir_wrapper.go b/fs/dir_wrapper.go
index 1560961e2..8724aba6d 100644
--- a/fs/dir_wrapper.go
+++ b/fs/dir_wrapper.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"context"
diff --git a/fs/direntries.go b/fs/direntries.go
index 72839a417..eb541fe7a 100644
--- a/fs/direntries.go
+++ b/fs/direntries.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import "fmt"
diff --git a/fs/direntries_test.go b/fs/direntries_test.go
index 16bf101a5..194ce0aa5 100644
--- a/fs/direntries_test.go
+++ b/fs/direntries_test.go
@@ -1,4 +1,4 @@
-package fs_test
+package fs_test
import (
"sort"
diff --git a/fs/dirtree/dirtree.go b/fs/dirtree/dirtree.go
index 70344ab34..f54ba4706 100644
--- a/fs/dirtree/dirtree.go
+++ b/fs/dirtree/dirtree.go
@@ -1,4 +1,4 @@
-// Package dirtree contains the DirTree type which is used for
+// Package dirtree contains the DirTree type which is used for
// building filesystem hierarchies in memory.
package dirtree
diff --git a/fs/dirtree/dirtree_test.go b/fs/dirtree/dirtree_test.go
index 45cd9a3aa..f9f27687d 100644
--- a/fs/dirtree/dirtree_test.go
+++ b/fs/dirtree/dirtree_test.go
@@ -1,4 +1,4 @@
-package dirtree
+package dirtree
import (
"fmt"
@@ -213,7 +213,7 @@ func BenchmarkCheckParents(b *testing.B) {
dt.Add(o)
}
b.StartTimer()
- for n := 0; n < b.N; n++ {
+ for b.Loop() {
dt.CheckParents("")
}
})
diff --git a/fs/driveletter/driveletter.go b/fs/driveletter/driveletter.go
index 68051473d..e98a51ca6 100644
--- a/fs/driveletter/driveletter.go
+++ b/fs/driveletter/driveletter.go
@@ -1,4 +1,4 @@
-//go:build !windows
+//go:build !windows
// Package driveletter returns whether a name is a valid drive letter
package driveletter
diff --git a/fs/driveletter/driveletter_windows.go b/fs/driveletter/driveletter_windows.go
index b66ec142d..97eb4cc18 100644
--- a/fs/driveletter/driveletter_windows.go
+++ b/fs/driveletter/driveletter_windows.go
@@ -1,4 +1,4 @@
-//go:build windows
+//go:build windows
// Package driveletter returns whether a name is a valid drive letter
package driveletter
diff --git a/fs/dump.go b/fs/dump.go
index a461d660a..6505e6547 100644
--- a/fs/dump.go
+++ b/fs/dump.go
@@ -1,4 +1,4 @@
-package fs
+package fs
// DumpFlags describes the Dump options in force
type DumpFlags = Bits[dumpChoices]
diff --git a/fs/dump_test.go b/fs/dump_test.go
index aa47264c4..31b106b83 100644
--- a/fs/dump_test.go
+++ b/fs/dump_test.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"encoding/json"
diff --git a/fs/enum.go b/fs/enum.go
index 3a0127f39..fa6661ee8 100644
--- a/fs/enum.go
+++ b/fs/enum.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"encoding/json"
diff --git a/fs/enum_test.go b/fs/enum_test.go
index c17f334ad..5469e916b 100644
--- a/fs/enum_test.go
+++ b/fs/enum_test.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"encoding/json"
diff --git a/fs/features.go b/fs/features.go
index 1563dc459..9ca5279c9 100644
--- a/fs/features.go
+++ b/fs/features.go
@@ -1,4 +1,4 @@
-// Filesystem features and optional interfaces
+// Filesystem features and optional interfaces
package fs
diff --git a/fs/filter/filter.go b/fs/filter/filter.go
index 0bbf3e69d..4223b0499 100644
--- a/fs/filter/filter.go
+++ b/fs/filter/filter.go
@@ -1,4 +1,4 @@
-// Package filter controls the filtering of files
+// Package filter controls the filtering of files
package filter
import (
diff --git a/fs/filter/filter_test.go b/fs/filter/filter_test.go
index a5e82d3ab..f7d868cad 100644
--- a/fs/filter/filter_test.go
+++ b/fs/filter/filter_test.go
@@ -1,4 +1,4 @@
-package filter
+package filter
import (
"context"
diff --git a/fs/filter/filterflags/filterflags.go b/fs/filter/filterflags/filterflags.go
index eaead5e79..4ab96eaab 100644
--- a/fs/filter/filterflags/filterflags.go
+++ b/fs/filter/filterflags/filterflags.go
@@ -1,4 +1,4 @@
-// Package filterflags implements command line flags to set up a filter
+// Package filterflags implements command line flags to set up a filter
package filterflags
import (
diff --git a/fs/filter/glob.go b/fs/filter/glob.go
index 40c0c56e2..664620137 100644
--- a/fs/filter/glob.go
+++ b/fs/filter/glob.go
@@ -1,4 +1,4 @@
-// rsync style glob parser
+// rsync style glob parser
package filter
diff --git a/fs/filter/glob_test.go b/fs/filter/glob_test.go
index a20a77775..e8b63e97d 100644
--- a/fs/filter/glob_test.go
+++ b/fs/filter/glob_test.go
@@ -1,4 +1,4 @@
-package filter
+package filter
import (
"testing"
diff --git a/fs/filter/rules.go b/fs/filter/rules.go
index 085a02aa5..cb43acb59 100644
--- a/fs/filter/rules.go
+++ b/fs/filter/rules.go
@@ -1,4 +1,4 @@
-package filter
+package filter
import (
"bufio"
diff --git a/fs/fingerprint.go b/fs/fingerprint.go
index 75d3256db..7d6e25cfd 100644
--- a/fs/fingerprint.go
+++ b/fs/fingerprint.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"context"
diff --git a/fs/fingerprint_test.go b/fs/fingerprint_test.go
index 713baf4cb..92d620af2 100644
--- a/fs/fingerprint_test.go
+++ b/fs/fingerprint_test.go
@@ -1,4 +1,4 @@
-package fs_test
+package fs_test
import (
"context"
diff --git a/fs/fs.go b/fs/fs.go
index 19a9b8e51..b69ce4aee 100644
--- a/fs/fs.go
+++ b/fs/fs.go
@@ -1,4 +1,4 @@
-// Package fs is a generic file system interface for rclone object storage systems
+// Package fs is a generic file system interface for rclone object storage systems
package fs
import (
diff --git a/fs/fs_test.go b/fs/fs_test.go
index 45b16229c..8a4b28c06 100644
--- a/fs/fs_test.go
+++ b/fs/fs_test.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"context"
diff --git a/fs/fserrors/enospc_error.go b/fs/fserrors/enospc_error.go
index bbb424e46..dec232cfc 100644
--- a/fs/fserrors/enospc_error.go
+++ b/fs/fserrors/enospc_error.go
@@ -1,4 +1,4 @@
-//go:build !plan9
+//go:build !plan9
package fserrors
diff --git a/fs/fserrors/enospc_error_notsupported.go b/fs/fserrors/enospc_error_notsupported.go
index f1f1e94de..bad236850 100644
--- a/fs/fserrors/enospc_error_notsupported.go
+++ b/fs/fserrors/enospc_error_notsupported.go
@@ -1,4 +1,4 @@
-//go:build plan9
+//go:build plan9
package fserrors
diff --git a/fs/fserrors/error.go b/fs/fserrors/error.go
index e65bd5be5..b576c6599 100644
--- a/fs/fserrors/error.go
+++ b/fs/fserrors/error.go
@@ -1,4 +1,4 @@
-// Package fserrors provides errors and error handling
+// Package fserrors provides errors and error handling
package fserrors
import (
diff --git a/fs/fserrors/error_syscall_test.go b/fs/fserrors/error_syscall_test.go
index f2523b6fd..2309900fe 100644
--- a/fs/fserrors/error_syscall_test.go
+++ b/fs/fserrors/error_syscall_test.go
@@ -1,4 +1,4 @@
-//go:build !plan9
+//go:build !plan9
// +build !plan9
package fserrors
diff --git a/fs/fserrors/error_test.go b/fs/fserrors/error_test.go
index 21bdaf5cb..634ff0795 100644
--- a/fs/fserrors/error_test.go
+++ b/fs/fserrors/error_test.go
@@ -1,4 +1,4 @@
-package fserrors
+package fserrors
import (
"context"
diff --git a/fs/fserrors/retriable_errors.go b/fs/fserrors/retriable_errors.go
index bd24f0a8e..a8e3b563a 100644
--- a/fs/fserrors/retriable_errors.go
+++ b/fs/fserrors/retriable_errors.go
@@ -1,4 +1,4 @@
-//go:build !plan9
+//go:build !plan9
package fserrors
diff --git a/fs/fserrors/retriable_errors_windows.go b/fs/fserrors/retriable_errors_windows.go
index 17a62af5c..ffdfddebe 100644
--- a/fs/fserrors/retriable_errors_windows.go
+++ b/fs/fserrors/retriable_errors_windows.go
@@ -1,4 +1,4 @@
-//go:build windows
+//go:build windows
package fserrors
diff --git a/fs/fshttp/dialer.go b/fs/fshttp/dialer.go
index c205ff5e2..01f73136f 100644
--- a/fs/fshttp/dialer.go
+++ b/fs/fshttp/dialer.go
@@ -1,4 +1,4 @@
-package fshttp
+package fshttp
import (
"context"
diff --git a/fs/fshttp/http.go b/fs/fshttp/http.go
index 65684f3db..0ee2432f1 100644
--- a/fs/fshttp/http.go
+++ b/fs/fshttp/http.go
@@ -1,4 +1,4 @@
-// Package fshttp contains the common http parts of the config, Transport and Client
+// Package fshttp contains the common http parts of the config, Transport and Client
package fshttp
import (
@@ -211,11 +211,11 @@ func NewTransportCustom(ctx context.Context, customize func(*http.Transport)) *T
// This also means we get new stuff when it gets added to go
t := new(http.Transport)
structs.SetDefaults(t, http.DefaultTransport.(*http.Transport))
- if ci.HTTPProxy != "" {
- proxyURL, err := url.Parse(ci.HTTPProxy)
+ if ci.Proxy != "" {
+ proxyURL, err := url.Parse(ci.Proxy)
if err != nil {
t.Proxy = func(*http.Request) (*url.URL, error) {
- return nil, fmt.Errorf("failed to set --http-proxy from %q: %w", ci.HTTPProxy, err)
+ return nil, fmt.Errorf("failed to set --proxy from %q: %w", ci.Proxy, err)
}
} else {
t.Proxy = http.ProxyURL(proxyURL)
diff --git a/fs/fshttp/http_test.go b/fs/fshttp/http_test.go
index 18f191e73..670061c47 100644
--- a/fs/fshttp/http_test.go
+++ b/fs/fshttp/http_test.go
@@ -1,4 +1,4 @@
-package fshttp
+package fshttp
import (
"context"
diff --git a/fs/fshttp/prometheus.go b/fs/fshttp/prometheus.go
index 2f83113b0..4f037fcec 100644
--- a/fs/fshttp/prometheus.go
+++ b/fs/fshttp/prometheus.go
@@ -1,4 +1,4 @@
-package fshttp
+package fshttp
import (
"fmt"
diff --git a/fs/fspath/fuzz.go b/fs/fspath/fuzz.go
index 226f26a4b..866959ea6 100644
--- a/fs/fspath/fuzz.go
+++ b/fs/fspath/fuzz.go
@@ -1,4 +1,4 @@
-//go:build gofuzz
+//go:build gofuzz
/*
Fuzz test the Parse function
diff --git a/fs/fspath/path.go b/fs/fspath/path.go
index 4cdd382c0..56c0da3c0 100644
--- a/fs/fspath/path.go
+++ b/fs/fspath/path.go
@@ -1,4 +1,4 @@
-// Package fspath contains routines for fspath manipulation
+// Package fspath contains routines for fspath manipulation
package fspath
import (
diff --git a/fs/fspath/path_test.go b/fs/fspath/path_test.go
index 07becd876..eaaf46aa9 100644
--- a/fs/fspath/path_test.go
+++ b/fs/fspath/path_test.go
@@ -1,4 +1,4 @@
-package fspath
+package fspath
import (
"flag"
diff --git a/fs/hash/hash.go b/fs/hash/hash.go
index 972fea186..56c0aff87 100644
--- a/fs/hash/hash.go
+++ b/fs/hash/hash.go
@@ -1,4 +1,4 @@
-// Package hash provides hash utilities for Fs.
+// Package hash provides hash utilities for Fs.
package hash
import (
diff --git a/fs/hash/hash_test.go b/fs/hash/hash_test.go
index a5e5f8cb7..0b38e2657 100644
--- a/fs/hash/hash_test.go
+++ b/fs/hash/hash_test.go
@@ -1,4 +1,4 @@
-package hash_test
+package hash_test
import (
"bytes"
diff --git a/fs/list/helpers.go b/fs/list/helpers.go
index 54303ef61..2f5a5fa49 100644
--- a/fs/list/helpers.go
+++ b/fs/list/helpers.go
@@ -1,4 +1,4 @@
-package list
+package list
import (
"context"
diff --git a/fs/list/helpers_test.go b/fs/list/helpers_test.go
index 8973b9ddd..a17d0a5f2 100644
--- a/fs/list/helpers_test.go
+++ b/fs/list/helpers_test.go
@@ -1,4 +1,4 @@
-package list
+package list
import (
"context"
@@ -62,7 +62,7 @@ func TestListRHelperSend(t *testing.T) {
helper := NewHelper(callback)
// Add 100 entries to force the callback to be invoked
- for i := 0; i < 100; i++ {
+ for range 100 {
require.NoError(t, helper.Add(entry))
}
@@ -120,7 +120,7 @@ var _ fs.ListPer = (*mockListPfs)(nil)
func TestListWithListP(t *testing.T) {
ctx := context.Background()
var entries fs.DirEntries
- for i := 0; i < 26; i++ {
+ for i := range 26 {
entries = append(entries, mockobject.New(fmt.Sprintf("%c", 'A'+i)))
}
t.Run("NoError", func(t *testing.T) {
diff --git a/fs/list/list.go b/fs/list/list.go
index c534b3363..9407c8289 100644
--- a/fs/list/list.go
+++ b/fs/list/list.go
@@ -1,4 +1,4 @@
-// Package list contains list functions
+// Package list contains list functions
package list
import (
diff --git a/fs/list/list_test.go b/fs/list/list_test.go
index 94fa7a6cc..2ec40f52f 100644
--- a/fs/list/list_test.go
+++ b/fs/list/list_test.go
@@ -1,4 +1,4 @@
-package list
+package list
import (
"context"
diff --git a/fs/list/sorter.go b/fs/list/sorter.go
index cc1d55a5b..71d31fe96 100644
--- a/fs/list/sorter.go
+++ b/fs/list/sorter.go
@@ -1,4 +1,4 @@
-package list
+package list
import (
"cmp"
@@ -222,7 +222,6 @@ func (lh *listHelper) send(max int) (err error) {
g, gCtx := errgroup.WithContext(lh.ls.ctx)
g.SetLimit(lh.ls.ci.Checkers)
for i, key := range lh.keys {
- i, key := i, key // can remove when go1.22 is minimum version
g.Go(func() error {
lh.entries[i], lh.errs[i] = lh.ls.keyToEntry(gCtx, key)
return nil
diff --git a/fs/list/sorter_test.go b/fs/list/sorter_test.go
index 7d34e0975..7446091c6 100644
--- a/fs/list/sorter_test.go
+++ b/fs/list/sorter_test.go
@@ -1,4 +1,4 @@
-package list
+package list
import (
"cmp"
@@ -144,7 +144,7 @@ func testSorterExt(t *testing.T, cutoff, N int, wantExtSort bool, keyFn KeyFn) {
// Make the directory entries
entriesMap := make(map[string]fs.DirEntry, N)
- for i := 0; i < N; i++ {
+ for i := range N {
remote := fmt.Sprintf("%010d", i)
prefix := "a"
if i%3 == 0 {
diff --git a/fs/log.go b/fs/log.go
index 2572fe612..1360dcd4f 100644
--- a/fs/log.go
+++ b/fs/log.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"context"
diff --git a/fs/log/event_log.go b/fs/log/event_log.go
index c20de7128..88a11bc7f 100644
--- a/fs/log/event_log.go
+++ b/fs/log/event_log.go
@@ -1,4 +1,4 @@
-// Windows event logging stubs for non windows machines
+// Windows event logging stubs for non windows machines
//go:build !windows
diff --git a/fs/log/event_log_windows.go b/fs/log/event_log_windows.go
index 9c1a0ad2a..054bd340c 100644
--- a/fs/log/event_log_windows.go
+++ b/fs/log/event_log_windows.go
@@ -1,4 +1,4 @@
-// Windows event logging
+// Windows event logging
//go:build windows
diff --git a/fs/log/log.go b/fs/log/log.go
index 0c0fad727..0788b2cea 100644
--- a/fs/log/log.go
+++ b/fs/log/log.go
@@ -1,4 +1,4 @@
-// Package log provides logging for rclone
+// Package log provides logging for rclone
package log
import (
diff --git a/fs/log/logflags/logflags.go b/fs/log/logflags/logflags.go
index 4c02e7ea5..66a4725c5 100644
--- a/fs/log/logflags/logflags.go
+++ b/fs/log/logflags/logflags.go
@@ -1,4 +1,4 @@
-// Package logflags implements command line flags to set up the log
+// Package logflags implements command line flags to set up the log
package logflags
import (
diff --git a/fs/log/redirect_stderr.go b/fs/log/redirect_stderr.go
index ce10ed36a..e821fe6b8 100644
--- a/fs/log/redirect_stderr.go
+++ b/fs/log/redirect_stderr.go
@@ -1,4 +1,4 @@
-// Log the panic to the log file - for oses which can't do this
+// Log the panic to the log file - for oses which can't do this
//go:build !windows && !darwin && !dragonfly && !freebsd && !linux && !nacl && !netbsd && !openbsd
diff --git a/fs/log/redirect_stderr_unix.go b/fs/log/redirect_stderr_unix.go
index dd7aa4d6f..988fc6020 100644
--- a/fs/log/redirect_stderr_unix.go
+++ b/fs/log/redirect_stderr_unix.go
@@ -1,4 +1,4 @@
-// Log the panic under unix to the log file
+// Log the panic under unix to the log file
//go:build !windows && !solaris && !plan9 && !js
diff --git a/fs/log/redirect_stderr_windows.go b/fs/log/redirect_stderr_windows.go
index 6d6c6abb8..15f8fa8c0 100644
--- a/fs/log/redirect_stderr_windows.go
+++ b/fs/log/redirect_stderr_windows.go
@@ -1,4 +1,4 @@
-// Log the panic under windows to the log file
+// Log the panic under windows to the log file
//
// Code from minix, via
//
diff --git a/fs/log/slog.go b/fs/log/slog.go
index 0efbcbee6..7fa5f04e1 100644
--- a/fs/log/slog.go
+++ b/fs/log/slog.go
@@ -1,4 +1,4 @@
-// Interfaces for the slog package
+// Interfaces for the slog package
package log
diff --git a/fs/log/slog_test.go b/fs/log/slog_test.go
index 5cfa52131..2cf1652fa 100644
--- a/fs/log/slog_test.go
+++ b/fs/log/slog_test.go
@@ -1,4 +1,4 @@
-package log
+package log
import (
"bytes"
diff --git a/fs/log/syslog.go b/fs/log/syslog.go
index f3ad7386e..6bd9f56c2 100644
--- a/fs/log/syslog.go
+++ b/fs/log/syslog.go
@@ -1,4 +1,4 @@
-// Syslog interface for non-Unix variants only
+// Syslog interface for non-Unix variants only
//go:build windows || nacl || plan9
diff --git a/fs/log/syslog_unix.go b/fs/log/syslog_unix.go
index 708083349..eb3d882a7 100644
--- a/fs/log/syslog_unix.go
+++ b/fs/log/syslog_unix.go
@@ -1,4 +1,4 @@
-// Syslog interface for Unix variants only
+// Syslog interface for Unix variants only
//go:build !windows && !nacl && !plan9
diff --git a/fs/log/systemd.go b/fs/log/systemd.go
index 8ebb2c0d9..dfb055bdd 100644
--- a/fs/log/systemd.go
+++ b/fs/log/systemd.go
@@ -1,4 +1,4 @@
-// Systemd interface for non-Unix variants only
+// Systemd interface for non-Unix variants only
//go:build !unix
diff --git a/fs/log/systemd_unix.go b/fs/log/systemd_unix.go
index fa29b203c..fb27da853 100644
--- a/fs/log/systemd_unix.go
+++ b/fs/log/systemd_unix.go
@@ -1,4 +1,4 @@
-// Systemd interface for Unix variants only
+// Systemd interface for Unix variants only
//go:build unix
diff --git a/fs/log_test.go b/fs/log_test.go
index 0bb04bd21..083b5bb99 100644
--- a/fs/log_test.go
+++ b/fs/log_test.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"encoding/json"
diff --git a/fs/logger/logger.go b/fs/logger/logger.go
index ac54231c7..2d9179a2d 100644
--- a/fs/logger/logger.go
+++ b/fs/logger/logger.go
@@ -1,4 +1,4 @@
-// Package logger implements testing for the sync (and bisync) logger
+// Package logger implements testing for the sync (and bisync) logger
package logger
import (
diff --git a/fs/logger/logger_test.go b/fs/logger/logger_test.go
index 62d3b85fa..f124a8616 100644
--- a/fs/logger/logger_test.go
+++ b/fs/logger/logger_test.go
@@ -1,4 +1,4 @@
-//go:build !plan9
+//go:build !plan9
// +build !plan9
package logger_test
diff --git a/fs/march/march.go b/fs/march/march.go
index 67a5b5bfa..e2ccf72de 100644
--- a/fs/march/march.go
+++ b/fs/march/march.go
@@ -1,4 +1,4 @@
-// Package march traverses two directories in lock step
+// Package march traverses two directories in lock step
package march
import (
@@ -137,20 +137,22 @@ func (m *March) makeListDir(ctx context.Context, f fs.Fs, includeAll bool, keyFn
)
return func(dir string, callback fs.ListRCallback) (err error) {
mu.Lock()
- defer mu.Unlock()
if !started {
dirCtx := filter.SetUseFilter(m.Ctx, f.Features().FilterAware && !includeAll) // make filter-aware backends constrain List
dirs, dirsErr = walk.NewDirTree(dirCtx, f, m.Dir, includeAll, ci.MaxDepth)
started = true
}
if dirsErr != nil {
+ mu.Unlock()
return dirsErr
}
entries, ok := dirs[dir]
if !ok {
+ mu.Unlock()
return fs.ErrorDirNotFound
}
delete(dirs, dir)
+ mu.Unlock()
// We use a stable sort here just in case there are
// duplicates. Assuming the remote delivers the entries in a
diff --git a/fs/march/march_test.go b/fs/march/march_test.go
index 76747287e..c798aa12b 100644
--- a/fs/march/march_test.go
+++ b/fs/march/march_test.go
@@ -1,4 +1,4 @@
-// Internal tests for march
+// Internal tests for march
package march
diff --git a/fs/metadata.go b/fs/metadata.go
index 919e7862a..ea8288e35 100644
--- a/fs/metadata.go
+++ b/fs/metadata.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"bytes"
diff --git a/fs/metadata_mapper_code.go b/fs/metadata_mapper_code.go
index d551984db..9a38f8376 100644
--- a/fs/metadata_mapper_code.go
+++ b/fs/metadata_mapper_code.go
@@ -1,4 +1,4 @@
-//go:build ignore
+//go:build ignore
// A simple metadata mapper for testing purposes
package main
diff --git a/fs/metadata_test.go b/fs/metadata_test.go
index 7e5389fb6..523710c12 100644
--- a/fs/metadata_test.go
+++ b/fs/metadata_test.go
@@ -1,4 +1,4 @@
-package fs_test
+package fs_test
import (
"context"
diff --git a/fs/mimetype.go b/fs/mimetype.go
index 93f82172d..4e08bc3b3 100644
--- a/fs/mimetype.go
+++ b/fs/mimetype.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"context"
@@ -32,7 +32,7 @@ func init() {
{"video/x-matroska", ".mpv,.mkv"},
{"application/x-subrip", ".srt"},
} {
- for _, ext := range strings.Split(t.extensions, ",") {
+ for ext := range strings.SplitSeq(t.extensions, ",") {
if mime.TypeByExtension(ext) == "" {
err := mime.AddExtensionType(ext, t.mimeType)
if err != nil {
diff --git a/fs/mount_helper.go b/fs/mount_helper.go
index ef81777fc..2b15eecaa 100644
--- a/fs/mount_helper.go
+++ b/fs/mount_helper.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"errors"
diff --git a/fs/mount_helper_test.go b/fs/mount_helper_test.go
index 3235b1195..7cec6972a 100644
--- a/fs/mount_helper_test.go
+++ b/fs/mount_helper_test.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"os"
diff --git a/fs/newfs.go b/fs/newfs.go
index 290e4ac40..997e69891 100644
--- a/fs/newfs.go
+++ b/fs/newfs.go
@@ -1,4 +1,4 @@
-// NewFs and its helpers
+// NewFs and its helpers
package fs
diff --git a/fs/newfs_internal_test.go b/fs/newfs_internal_test.go
index 6428a5bc8..11d18ed94 100644
--- a/fs/newfs_internal_test.go
+++ b/fs/newfs_internal_test.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"context"
diff --git a/fs/newfs_test.go b/fs/newfs_test.go
index 19667401e..e7eae3c12 100644
--- a/fs/newfs_test.go
+++ b/fs/newfs_test.go
@@ -1,4 +1,4 @@
-package fs_test
+package fs_test
import (
"context"
diff --git a/fs/object/object.go b/fs/object/object.go
index 9b4e25afe..dbf731779 100644
--- a/fs/object/object.go
+++ b/fs/object/object.go
@@ -1,15 +1,25 @@
-// Package object defines some useful Objects
+// Package object defines some useful Objects
package object
import (
"bytes"
"context"
"errors"
+ "fmt"
"io"
+ "mime"
+ "net/http"
+ "net/textproto"
+ "path"
+ "strings"
"time"
"github.com/rclone/rclone/fs"
+ "github.com/rclone/rclone/fs/fserrors"
+ "github.com/rclone/rclone/fs/fshttp"
"github.com/rclone/rclone/fs/hash"
+ "github.com/rclone/rclone/lib/pacer"
+ "github.com/rclone/rclone/lib/rest"
)
// StaticObjectInfo is an ObjectInfo which can be constructed from scratch
@@ -334,3 +344,168 @@ var (
_ fs.Object = (*MemoryObject)(nil)
_ fs.Metadataer = (*MemoryObject)(nil)
)
+
+var retryErrorCodes = []int{429, 500, 502, 503, 504}
+
+func shouldRetry(ctx context.Context, resp *http.Response, err error) (bool, error) {
+ if fserrors.ContextError(ctx, &err) {
+ return false, err
+ }
+ return fserrors.ShouldRetry(err) || fserrors.ShouldRetryHTTP(resp, retryErrorCodes), err
+}
+
+func (o *HTTPObject) fetch(ctx context.Context) error {
+
+ headOpts := rest.Opts{
+ Method: "HEAD",
+ RootURL: o.url,
+ }
+
+ resp, err := o.client.Call(ctx, &headOpts)
+ if err == nil && resp.StatusCode >= 200 && resp.StatusCode < 300 {
+ defer resp.Body.Close()
+ return o.parseMetadataResponse(resp)
+ }
+ if resp != nil {
+ resp.Body.Close()
+ }
+
+ getOpts := rest.Opts{
+ Method: "GET",
+ RootURL: o.url,
+ ExtraHeaders: map[string]string{
+ "Range": "bytes=0-0",
+ },
+ }
+
+ resp, err = o.client.Call(ctx, &getOpts)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode < 200 || resp.StatusCode >= 300 {
+ return fmt.Errorf("metadata fetch failed: %s", resp.Status)
+ }
+
+ return o.parseMetadataResponse(resp)
+}
+
+func (o *HTTPObject) parseMetadataResponse(resp *http.Response) error {
+ var filename string
+
+ if o.dstFileNameFromHeader {
+ if cd := resp.Header.Get("Content-Disposition"); cd != "" {
+ if _, params, err := mime.ParseMediaType(cd); err == nil {
+ if val, ok := params["filename"]; ok {
+ filename = textproto.TrimString(path.Base(strings.ReplaceAll(val, "\\", "/")))
+ }
+ }
+ }
+ } else {
+ filename = path.Base(resp.Request.URL.Path)
+ }
+ o.size = rest.ParseSizeFromHeaders(resp.Header)
+
+ if lm := resp.Header.Get("Last-Modified"); lm != "" {
+ if t, err := http.ParseTime(lm); err == nil {
+ o.modTime = t
+ }
+ }
+ o.remote = filename
+ return nil
+
+}
+
+var HTTPFs httpFs
+
+type httpFs struct{}
+
+func (h httpFs) Features() *fs.Features { return &fs.Features{} }
+func (h httpFs) Hashes() hash.Set { return hash.Set(hash.None) }
+func (h httpFs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) {
+ return nil, nil
+}
+func (h httpFs) Mkdir(ctx context.Context, dir string) error { return nil }
+func (h httpFs) Name() string { return "http" }
+func (h httpFs) NewObject(ctx context.Context, remote string) (fs.Object, error) {
+ return nil, fs.ErrorObjectNotFound
+}
+func (h httpFs) Precision() time.Duration { return time.Nanosecond }
+func (h httpFs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
+ return nil, nil
+}
+func (h httpFs) Rmdir(ctx context.Context, dir string) error { return nil }
+func (h httpFs) Root() string { return "" }
+func (h httpFs) String() string { return "http" }
+
+var _ fs.Fs = HTTPFs
+
+type HTTPObject struct {
+ p *fs.Pacer
+ client *rest.Client
+ url string
+ remote string
+ size int64
+ modTime time.Time
+ dstFileNameFromHeader bool
+}
+
+func NewHTTPObject(ctx context.Context, url string, dstFileNameFromHeader bool) (*HTTPObject, error) {
+ ci := fs.GetConfig(ctx)
+ o := &HTTPObject{url: url, client: rest.NewClient(fshttp.NewClient(ctx)), dstFileNameFromHeader: dstFileNameFromHeader}
+ o.p = fs.NewPacer(ctx, pacer.NewDefault())
+ o.p.SetRetries(ci.LowLevelRetries)
+ err := o.fetch(ctx)
+ if err != nil {
+ return nil, err
+ }
+ return o, nil
+}
+
+func (o *HTTPObject) String() string {
+ if o == nil {
+ return ""
+ }
+ return o.remote
+}
+
+func (o *HTTPObject) Remote() string { return o.remote }
+func (o *HTTPObject) ModTime(ctx context.Context) time.Time { return o.modTime }
+func (o *HTTPObject) Size() int64 { return o.size }
+func (o *HTTPObject) Fs() fs.Info { return HTTPFs }
+func (o *HTTPObject) Storable() bool { return true }
+func (o *HTTPObject) Open(ctx context.Context, options ...fs.OpenOption) (io.ReadCloser, error) {
+ var (
+ err error
+ res *http.Response
+ )
+
+ fs.FixRangeOption(options, o.size)
+ err = o.p.Call(func() (bool, error) {
+ opts := rest.Opts{
+ Method: "GET",
+ RootURL: o.url,
+ Options: options,
+ }
+ res, err = o.client.Call(ctx, &opts)
+ return shouldRetry(ctx, res, err)
+ })
+
+ if err != nil {
+ return nil, fmt.Errorf("Open failed: %w", err)
+ }
+ return res.Body, nil
+}
+func (o *HTTPObject) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) error {
+ return nil
+}
+func (o *HTTPObject) Remove(ctx context.Context) error { return nil }
+func (o *HTTPObject) SetModTime(ctx context.Context, modTime time.Time) error { return nil }
+func (o *HTTPObject) Hash(ctx context.Context, r hash.Type) (string, error) {
+ return "", hash.ErrUnsupported
+}
+
+var (
+ _ fs.Object = (*HTTPObject)(nil)
+)
diff --git a/fs/object/object_test.go b/fs/object/object_test.go
index be90c771e..fc1124443 100644
--- a/fs/object/object_test.go
+++ b/fs/object/object_test.go
@@ -1,4 +1,4 @@
-package object_test
+package object_test
import (
"bytes"
diff --git a/fs/open_options.go b/fs/open_options.go
index cb48a930e..b74800dd2 100644
--- a/fs/open_options.go
+++ b/fs/open_options.go
@@ -1,4 +1,4 @@
-// Options for Open
+// Options for Open
package fs
diff --git a/fs/open_options_test.go b/fs/open_options_test.go
index 37463adef..cd3a71120 100644
--- a/fs/open_options_test.go
+++ b/fs/open_options_test.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"fmt"
diff --git a/fs/operations/check.go b/fs/operations/check.go
index 56be4d0fb..15f9d95b1 100644
--- a/fs/operations/check.go
+++ b/fs/operations/check.go
@@ -1,4 +1,4 @@
-package operations
+package operations
import (
"bufio"
diff --git a/fs/operations/check_test.go b/fs/operations/check_test.go
index eeffb0514..dc8d91cd8 100644
--- a/fs/operations/check_test.go
+++ b/fs/operations/check_test.go
@@ -1,4 +1,4 @@
-package operations_test
+package operations_test
import (
"bytes"
diff --git a/fs/operations/copy.go b/fs/operations/copy.go
index f7871d292..71b34e882 100644
--- a/fs/operations/copy.go
+++ b/fs/operations/copy.go
@@ -1,4 +1,4 @@
-// This file implements operations.Copy
+// This file implements operations.Copy
//
// This is probably the most important operation in rclone.
diff --git a/fs/operations/copy_test.go b/fs/operations/copy_test.go
index e717f3bef..853ae9f05 100644
--- a/fs/operations/copy_test.go
+++ b/fs/operations/copy_test.go
@@ -1,4 +1,4 @@
-package operations_test
+package operations_test
import (
"context"
diff --git a/fs/operations/dedupe.go b/fs/operations/dedupe.go
index 60c66c4a3..00c96d45b 100644
--- a/fs/operations/dedupe.go
+++ b/fs/operations/dedupe.go
@@ -1,4 +1,4 @@
-// dedupe - gets rid of identical files remotes which can have duplicate file names (drive, mega)
+// dedupe - gets rid of identical files remotes which can have duplicate file names (drive, mega)
package operations
diff --git a/fs/operations/dedupe_test.go b/fs/operations/dedupe_test.go
index 15a4e2db9..e701a531d 100644
--- a/fs/operations/dedupe_test.go
+++ b/fs/operations/dedupe_test.go
@@ -1,4 +1,4 @@
-package operations_test
+package operations_test
import (
"context"
diff --git a/fs/operations/listdirsorted_test.go b/fs/operations/listdirsorted_test.go
index 4719d6c55..85c3cec8f 100644
--- a/fs/operations/listdirsorted_test.go
+++ b/fs/operations/listdirsorted_test.go
@@ -1,4 +1,4 @@
-package operations_test
+package operations_test
import (
"context"
diff --git a/fs/operations/logger.go b/fs/operations/logger.go
index 0d482206f..4f2b5026e 100644
--- a/fs/operations/logger.go
+++ b/fs/operations/logger.go
@@ -1,4 +1,4 @@
-package operations
+package operations
import (
"bytes"
diff --git a/fs/operations/lsjson.go b/fs/operations/lsjson.go
index 8e714efbf..a5f13aa22 100644
--- a/fs/operations/lsjson.go
+++ b/fs/operations/lsjson.go
@@ -1,4 +1,4 @@
-package operations
+package operations
import (
"context"
diff --git a/fs/operations/lsjson_test.go b/fs/operations/lsjson_test.go
index 75d4728ba..8227ccd57 100644
--- a/fs/operations/lsjson_test.go
+++ b/fs/operations/lsjson_test.go
@@ -1,4 +1,4 @@
-package operations_test
+package operations_test
import (
"context"
diff --git a/fs/operations/multithread.go b/fs/operations/multithread.go
index 9abd5f743..68ba5920f 100644
--- a/fs/operations/multithread.go
+++ b/fs/operations/multithread.go
@@ -1,15 +1,20 @@
-package operations
+package operations
import (
"bufio"
"context"
+ "encoding/binary"
"errors"
"fmt"
"io"
+ "os"
+ "path/filepath"
+ "sync"
"time"
"github.com/rclone/rclone/fs"
"github.com/rclone/rclone/fs/accounting"
+ "github.com/rclone/rclone/fs/hash"
"github.com/rclone/rclone/lib/atexit"
"github.com/rclone/rclone/lib/multipart"
"github.com/rclone/rclone/lib/pool"
@@ -18,8 +23,286 @@ import (
const (
multithreadChunkSize = 64 << 10
+ resumeStateSuffix = ".rclone"
+ resumeFileVersion = 1
)
+// resumeState holds the resume state using a bitmap
+type resumeState struct {
+ Source string
+ Destination string
+ Size int64
+ ChunkSize int64
+ NumChunks int
+ ETag string // ETag from source (if available)
+ ModTime time.Time // Modification time from source
+ // Bitmap: 1 bit per chunk, 1=complete, 0=missing
+ Bitmap []byte
+}
+
+// loadResumeState loads resume state from binary file
+func loadResumeState(filePath string) (*resumeState, error) {
+ statePath := filePath + resumeStateSuffix
+
+ f, err := os.Open(statePath)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return nil, nil
+ }
+ return nil, err
+ }
+ defer f.Close()
+
+ state := &resumeState{}
+
+ // Read version (1 byte)
+ var version uint8
+ if err := binary.Read(f, binary.BigEndian, &version); err != nil {
+ return nil, fmt.Errorf("failed to read version: %w", err)
+ }
+ if version != resumeFileVersion {
+ return nil, fmt.Errorf("unsupported resume file version: %d", version)
+ }
+
+ // Read source length and source string
+ var sourceLen uint32
+ if err := binary.Read(f, binary.BigEndian, &sourceLen); err != nil {
+ return nil, fmt.Errorf("failed to read source length: %w", err)
+ }
+ sourceBytes := make([]byte, sourceLen)
+ if _, err := io.ReadFull(f, sourceBytes); err != nil {
+ return nil, fmt.Errorf("failed to read source: %w", err)
+ }
+ state.Source = string(sourceBytes)
+
+ // Read destination length and destination string
+ var destLen uint32
+ if err := binary.Read(f, binary.BigEndian, &destLen); err != nil {
+ return nil, fmt.Errorf("failed to read destination length: %w", err)
+ }
+ destBytes := make([]byte, destLen)
+ if _, err := io.ReadFull(f, destBytes); err != nil {
+ return nil, fmt.Errorf("failed to read destination: %w", err)
+ }
+ state.Destination = string(destBytes)
+
+ // Read size
+ if err := binary.Read(f, binary.BigEndian, &state.Size); err != nil {
+ return nil, fmt.Errorf("failed to read size: %w", err)
+ }
+
+ // Read chunk size
+ if err := binary.Read(f, binary.BigEndian, &state.ChunkSize); err != nil {
+ return nil, fmt.Errorf("failed to read chunk size: %w", err)
+ }
+
+ // Read num chunks (read as int32 then convert to int)
+ var numChunks int32
+ if err := binary.Read(f, binary.BigEndian, &numChunks); err != nil {
+ return nil, fmt.Errorf("failed to read num chunks: %w", err)
+ }
+ state.NumChunks = int(numChunks)
+
+ // Read ETag length and ETag string
+ var etagLen uint32
+ if err := binary.Read(f, binary.BigEndian, &etagLen); err != nil {
+ return nil, fmt.Errorf("failed to read etag length: %w", err)
+ }
+ if etagLen > 0 {
+ etagBytes := make([]byte, etagLen)
+ if _, err := io.ReadFull(f, etagBytes); err != nil {
+ return nil, fmt.Errorf("failed to read etag: %w", err)
+ }
+ state.ETag = string(etagBytes)
+ }
+
+ // Read ModTime (Unix timestamp in seconds)
+ var modTimeSec int64
+ if err := binary.Read(f, binary.BigEndian, &modTimeSec); err != nil {
+ return nil, fmt.Errorf("failed to read modtime: %w", err)
+ }
+ state.ModTime = time.Unix(modTimeSec, 0)
+
+ // Read bitmap
+ bitmapLen := (state.NumChunks + 7) / 8
+ state.Bitmap = make([]byte, bitmapLen)
+ if _, err := io.ReadFull(f, state.Bitmap); err != nil {
+ return nil, fmt.Errorf("failed to read bitmap: %w", err)
+ }
+
+ return state, nil
+}
+
+// save saves the resume state to binary file
+func (rs *resumeState) save(filePath string) error {
+ statePath := filePath + resumeStateSuffix
+ tempPath := statePath + ".tmp"
+
+ f, err := os.Create(tempPath)
+ if err != nil {
+ return fmt.Errorf("failed to create temp file: %w", err)
+ }
+
+ // Write version
+ if err := binary.Write(f, binary.BigEndian, uint8(resumeFileVersion)); err != nil {
+ f.Close()
+ os.Remove(tempPath)
+ return fmt.Errorf("failed to write version: %w", err)
+ }
+
+ // Write source
+ if err := binary.Write(f, binary.BigEndian, uint32(len(rs.Source))); err != nil {
+ f.Close()
+ os.Remove(tempPath)
+ return fmt.Errorf("failed to write source length: %w", err)
+ }
+ if _, err := f.WriteString(rs.Source); err != nil {
+ f.Close()
+ os.Remove(tempPath)
+ return fmt.Errorf("failed to write source: %w", err)
+ }
+
+ // Write destination
+ if err := binary.Write(f, binary.BigEndian, uint32(len(rs.Destination))); err != nil {
+ f.Close()
+ os.Remove(tempPath)
+ return fmt.Errorf("failed to write destination length: %w", err)
+ }
+ if _, err := f.WriteString(rs.Destination); err != nil {
+ f.Close()
+ os.Remove(tempPath)
+ return fmt.Errorf("failed to write destination: %w", err)
+ }
+
+ // Write size
+ if err := binary.Write(f, binary.BigEndian, rs.Size); err != nil {
+ f.Close()
+ os.Remove(tempPath)
+ return fmt.Errorf("failed to write size: %w", err)
+ }
+
+ // Write chunk size
+ if err := binary.Write(f, binary.BigEndian, rs.ChunkSize); err != nil {
+ f.Close()
+ os.Remove(tempPath)
+ return fmt.Errorf("failed to write chunk size: %w", err)
+ }
+
+ // Write num chunks (cast to int32 for fixed size)
+ if err := binary.Write(f, binary.BigEndian, int32(rs.NumChunks)); err != nil {
+ f.Close()
+ os.Remove(tempPath)
+ return fmt.Errorf("failed to write num chunks: %w", err)
+ }
+
+ // Write ETag
+ if err := binary.Write(f, binary.BigEndian, uint32(len(rs.ETag))); err != nil {
+ f.Close()
+ os.Remove(tempPath)
+ return fmt.Errorf("failed to write etag length: %w", err)
+ }
+ if _, err := f.WriteString(rs.ETag); err != nil {
+ f.Close()
+ os.Remove(tempPath)
+ return fmt.Errorf("failed to write etag: %w", err)
+ }
+
+ // Write ModTime (Unix timestamp)
+ if err := binary.Write(f, binary.BigEndian, rs.ModTime.Unix()); err != nil {
+ f.Close()
+ os.Remove(tempPath)
+ return fmt.Errorf("failed to write modtime: %w", err)
+ }
+
+ // Write bitmap
+ if _, err := f.Write(rs.Bitmap); err != nil {
+ f.Close()
+ os.Remove(tempPath)
+ return fmt.Errorf("failed to write bitmap: %w", err)
+ }
+
+ if err := f.Close(); err != nil {
+ os.Remove(tempPath)
+ return fmt.Errorf("failed to close file: %w", err)
+ }
+
+ // Atomic rename
+ if err := os.Rename(tempPath, statePath); err != nil {
+ os.Remove(tempPath)
+ return fmt.Errorf("failed to rename file: %w", err)
+ }
+
+ return nil
+}
+
+// deleteResumeState deletes the resume state file
+func deleteResumeState(filePath string) error {
+ statePath := filePath + resumeStateSuffix
+ if err := os.Remove(statePath); err != nil && !os.IsNotExist(err) {
+ return fmt.Errorf("failed to delete resume state: %w", err)
+ }
+ return nil
+}
+
+// isChunkComplete checks if a chunk is marked as complete
+func (rs *resumeState) isChunkComplete(chunkIdx int) bool {
+ if chunkIdx < 0 || chunkIdx >= rs.NumChunks {
+ return false
+ }
+ byteIdx := chunkIdx / 8
+ bitIdx := uint(chunkIdx % 8)
+ return (rs.Bitmap[byteIdx] & (1 << (7 - bitIdx))) != 0
+}
+
+// markChunkComplete marks a chunk as complete
+func (rs *resumeState) markChunkComplete(chunkIdx int) {
+ if chunkIdx < 0 || chunkIdx >= rs.NumChunks {
+ return
+ }
+ byteIdx := chunkIdx / 8
+ bitIdx := uint(chunkIdx % 8)
+ rs.Bitmap[byteIdx] |= 1 << (7 - bitIdx)
+}
+
+// countComplete returns the number of complete chunks
+func (rs *resumeState) countComplete() int {
+ count := 0
+ for i := 0; i < rs.NumChunks; i++ {
+ if rs.isChunkComplete(i) {
+ count++
+ }
+ }
+ return count
+}
+
+// validate checks if the resume state is valid for the given parameters
+func (rs *resumeState) validate(source string, size int64, chunkSize int64) error {
+ if rs.Source != source {
+ return fmt.Errorf("source mismatch: expected %s, got %s", rs.Source, source)
+ }
+ if rs.Size != size {
+ return fmt.Errorf("size mismatch: expected %d, got %d", rs.Size, size)
+ }
+ if rs.ChunkSize != chunkSize {
+ return fmt.Errorf("chunk size mismatch: expected %d, got %d", rs.ChunkSize, chunkSize)
+ }
+ return nil
+}
+
+// newResumeState creates a new resume state with empty bitmap
+func newResumeState(source, destination string, size, chunkSize int64) *resumeState {
+ numChunks := calculateNumChunks(size, chunkSize)
+ bitmapLen := (numChunks + 7) / 8
+ return &resumeState{
+ Source: source,
+ Destination: destination,
+ Size: size,
+ ChunkSize: chunkSize,
+ NumChunks: numChunks,
+ Bitmap: make([]byte, bitmapLen),
+ }
+}
+
// Return a boolean as to whether we should use multi thread copy for
// this transfer
func doMultiThreadCopy(ctx context.Context, f fs.Fs, src fs.Object) bool {
@@ -93,6 +376,7 @@ func (mc *multiThreadCopyState) copyChunk(ctx context.Context, chunk int, writer
defer fs.CheckClose(rc, &err)
var rs io.ReadSeeker
+
if mc.noBuffering {
// Read directly if we are sure we aren't going to seek
// and account with accounting
@@ -134,6 +418,13 @@ func calculateNumChunks(size int64, chunkSize int64) int {
func multiThreadCopy(ctx context.Context, f fs.Fs, remote string, src fs.Object, concurrency int, tr *accounting.Transfer, options ...fs.OpenOption) (newDst fs.Object, err error) {
openChunkWriter := f.Features().OpenChunkWriter
ci := fs.GetConfig(ctx)
+
+ // Check if resume is enabled and destination supports it
+ resumeEnabled := ci.MultiThreadResume && f.Features().IsLocal
+
+ var state *resumeState
+ var localPath string
+
noBuffering := false
usingOpenWriterAt := false
if openChunkWriter == nil {
@@ -174,7 +465,7 @@ func multiThreadCopy(ctx context.Context, f fs.Fs, remote string, src fs.Object,
uploadedOK := false
defer atexit.OnError(&err, func() {
cancel()
- if info.LeavePartsOnError || uploadedOK {
+ if info.LeavePartsOnError || uploadedOK || resumeEnabled {
return
}
fs.Debugf(src, "multi-thread copy: cancelling transfer on exit")
@@ -205,6 +496,52 @@ func multiThreadCopy(ctx context.Context, f fs.Fs, remote string, src fs.Object,
concurrency = 1
}
+ // Initialize or validate resume state
+ if resumeEnabled {
+ localPath = filepath.Join(f.Root(), remote)
+
+ // Try to load existing resume state
+ state, err = loadResumeState(localPath)
+ if err != nil {
+ fs.Debugf(remote, "Failed to load resume state, starting fresh: %v", err)
+ state = nil
+ } else if state != nil {
+ // Validate state against current file
+ if err := state.validate(src.Remote(), src.Size(), info.ChunkSize); err != nil {
+ fs.Debugf(remote, "Resume state invalid (%v), starting fresh", err)
+ state = nil
+ } else {
+ // Validate ETag if available
+ currentETag, err := src.Hash(ctx, hash.MD5)
+ if err == nil && currentETag != "" && state.ETag != "" && currentETag != state.ETag {
+ fs.Logf(remote, "ETag mismatch (source changed), starting fresh download")
+ state = nil
+ } else if !state.ModTime.IsZero() {
+ currentModTime := src.ModTime(ctx)
+ if !currentModTime.Equal(state.ModTime) {
+ fs.Logf(remote, "Modification time mismatch (source changed), starting fresh download")
+ state = nil
+ }
+ }
+ }
+ }
+
+ if state == nil {
+ state = newResumeState(src.Remote(), localPath, src.Size(), info.ChunkSize)
+ // Store ETag and ModTime
+ etag, err := src.Hash(ctx, hash.MD5)
+ if err == nil {
+ state.ETag = etag
+ }
+ state.ModTime = src.ModTime(ctx)
+ }
+
+ completed := state.countComplete()
+ if completed > 0 {
+ fs.Logf(remote, "Resuming download: %d/%d chunks already complete", completed, state.NumChunks)
+ }
+ }
+
g, gCtx := errgroup.WithContext(uploadCtx)
g.SetLimit(concurrency)
@@ -220,15 +557,55 @@ func multiThreadCopy(ctx context.Context, f fs.Fs, remote string, src fs.Object,
// Make accounting
mc.acc = tr.Account(gCtx, nil)
+ // Account for already-downloaded bytes to show correct progress position
+ if resumeEnabled && state != nil {
+ var completedBytes int64
+ for chunkIdx := range numChunks {
+ if state.isChunkComplete(chunkIdx) {
+ chunkStart := int64(chunkIdx) * info.ChunkSize
+ chunkEnd := min(chunkStart+info.ChunkSize, src.Size())
+ completedBytes += chunkEnd - chunkStart
+ }
+ }
+ if completedBytes > 0 {
+ mc.acc.ServerSideTransferEnd(completedBytes)
+ }
+ }
+
fs.Debugf(src, "Starting multi-thread copy with %d chunks of size %v with %v parallel streams", mc.numChunks, fs.SizeSuffix(mc.partSize), concurrency)
+
+ // Track chunks for state saving
+ var stateMu sync.Mutex
+
for chunk := range mc.numChunks {
+ // Skip completed chunks if resuming
+ if resumeEnabled && state != nil && state.isChunkComplete(chunk) {
+ continue
+ }
+
// Fail fast, in case an errgroup managed function returns an error
if gCtx.Err() != nil {
break
}
chunk := chunk
g.Go(func() error {
- return mc.copyChunk(gCtx, chunk, chunkWriter)
+ err := mc.copyChunk(gCtx, chunk, chunkWriter)
+ if err != nil {
+ return err
+ }
+
+ // Mark as complete and save state (only after successful write)
+ if resumeEnabled && state != nil {
+ stateMu.Lock()
+ state.markChunkComplete(chunk)
+ if localPath != "" {
+ if saveErr := state.save(localPath); saveErr != nil {
+ fs.Debugf(remote, "Failed to save resume state: %v", saveErr)
+ }
+ }
+ stateMu.Unlock()
+ }
+ return nil
})
}
@@ -242,6 +619,13 @@ func multiThreadCopy(ctx context.Context, f fs.Fs, remote string, src fs.Object,
}
uploadedOK = true // file is definitely uploaded OK so no need to abort
+ // Delete state file on successful completion
+ if resumeEnabled && localPath != "" {
+ if err := deleteResumeState(localPath); err != nil {
+ fs.Debugf(remote, "Failed to delete resume state: %v", err)
+ }
+ }
+
obj, err := f.NewObject(ctx, remote)
if err != nil {
return nil, fmt.Errorf("multi-thread copy: failed to find object after copy: %w", err)
diff --git a/fs/operations/multithread_test.go b/fs/operations/multithread_test.go
index 3dc2a9ba8..53e3535a5 100644
--- a/fs/operations/multithread_test.go
+++ b/fs/operations/multithread_test.go
@@ -1,4 +1,4 @@
-package operations
+package operations
import (
"context"
diff --git a/fs/operations/operations.go b/fs/operations/operations.go
index a10ad41b2..d91f33474 100644
--- a/fs/operations/operations.go
+++ b/fs/operations/operations.go
@@ -1,4 +1,4 @@
-// Package operations does generic operations on filesystems and objects
+// Package operations does generic operations on filesystems and objects
package operations
import (
@@ -1873,8 +1873,50 @@ func copyURLFn(ctx context.Context, dstFileName string, url string, autoFilename
return fn(ctx, dstFileName, resp.Body, resp.ContentLength, modTime)
}
+func CopyURLMulti(ctx context.Context, fdst fs.Fs, dstFileName string, srcObj fs.Object, overwrite bool) (dst fs.Object, err error) {
+ ci := fs.GetConfig(ctx)
+
+ destObj, err := fdst.NewObject(ctx, dstFileName)
+
+ if err != nil && !errors.Is(err, fs.ErrorObjectNotFound) {
+ return nil, err
+ }
+
+ needsCopy := overwrite || errors.Is(err, fs.ErrorObjectNotFound) || (destObj != nil && NeedTransfer(ctx, destObj, srcObj))
+
+ if needsCopy {
+ tr := accounting.Stats(ctx).NewTransferRemoteSize(dstFileName, srcObj.Size(), nil, fdst)
+ defer func() {
+ tr.Done(ctx, err)
+ }()
+ return multiThreadCopy(ctx, fdst, dstFileName, srcObj, ci.MultiThreadStreams, tr)
+ }
+ return destObj, nil
+}
+
// CopyURL copies the data from the url to (fdst, dstFileName)
func CopyURL(ctx context.Context, fdst fs.Fs, dstFileName string, url string, autoFilename, dstFileNameFromHeader bool, noClobber bool) (dst fs.Object, err error) {
+ ci := fs.GetConfig(ctx)
+ if ci.MultiThreadStreams > 0 {
+ filename := dstFileName
+ srcObj, err := object.NewHTTPObject(ctx, url, dstFileNameFromHeader)
+ if err != nil {
+ return nil, err
+ }
+ if autoFilename {
+ filename = srcObj.Remote()
+ if filename == "." || filename == "/" {
+ return nil, fmt.Errorf("CopyURL failed: file name wasn't found in url")
+ }
+ }
+ if noClobber {
+ _, err = fdst.NewObject(ctx, filename)
+ if err == nil {
+ return nil, errors.New("CopyURL failed: file already exist")
+ }
+ }
+ return CopyURLMulti(ctx, fdst, filename, srcObj, false)
+ }
err = copyURLFn(ctx, dstFileName, url, autoFilename, dstFileNameFromHeader, func(ctx context.Context, dstFileName string, in io.ReadCloser, size int64, modTime time.Time) (err error) {
if noClobber {
_, err = fdst.NewObject(ctx, dstFileName)
diff --git a/fs/operations/operations_internal_test.go b/fs/operations/operations_internal_test.go
index fd85a9819..55b1efc5d 100644
--- a/fs/operations/operations_internal_test.go
+++ b/fs/operations/operations_internal_test.go
@@ -1,4 +1,4 @@
-// Internal tests for operations
+// Internal tests for operations
package operations
diff --git a/fs/operations/operations_test.go b/fs/operations/operations_test.go
index cb68b05ca..926980ccc 100644
--- a/fs/operations/operations_test.go
+++ b/fs/operations/operations_test.go
@@ -1,4 +1,4 @@
-// Integration tests - test rclone by doing real transactions to a
+// Integration tests - test rclone by doing real transactions to a
// storage provider to and from the local disk.
//
// By default it will use a local fs, however you can provide a
diff --git a/fs/operations/operationsflags/operationsflags.go b/fs/operations/operationsflags/operationsflags.go
index 054ed96bc..f3d53b5d5 100644
--- a/fs/operations/operationsflags/operationsflags.go
+++ b/fs/operations/operationsflags/operationsflags.go
@@ -1,4 +1,4 @@
-// Package operationsflags defines the flags used by rclone operations.
+// Package operationsflags defines the flags used by rclone operations.
// It is decoupled into a separate package so it can be replaced.
package operationsflags
diff --git a/fs/operations/rc.go b/fs/operations/rc.go
index cd9160f73..1ac2d4ebd 100644
--- a/fs/operations/rc.go
+++ b/fs/operations/rc.go
@@ -1,4 +1,4 @@
-package operations
+package operations
import (
"context"
@@ -160,7 +160,6 @@ func rcAbout(ctx context.Context, in rc.Params) (out rc.Params, err error) {
func init() {
for _, copy := range []bool{false, true} {
- copy := copy
name := "Move"
if copy {
name = "Copy"
@@ -217,7 +216,6 @@ func init() {
{name: "settier", title: "Changes storage tier or class on all files in the path", noRemote: true},
{name: "settierfile", title: "Changes storage tier or class on the single file pointed to", noCommand: true},
} {
- op := op
var remote, command string
if !op.noRemote {
remote = "- remote - a path within that remote e.g. \"dir\"\n"
diff --git a/fs/operations/rc_test.go b/fs/operations/rc_test.go
index 2d8d0f2af..3dbf3f920 100644
--- a/fs/operations/rc_test.go
+++ b/fs/operations/rc_test.go
@@ -1,4 +1,4 @@
-package operations_test
+package operations_test
import (
"context"
diff --git a/fs/operations/reopen.go b/fs/operations/reopen.go
index ed56941fa..5afbdda06 100644
--- a/fs/operations/reopen.go
+++ b/fs/operations/reopen.go
@@ -1,4 +1,4 @@
-package operations
+package operations
import (
"context"
diff --git a/fs/operations/reopen_test.go b/fs/operations/reopen_test.go
index 891888c50..b92f43e3f 100644
--- a/fs/operations/reopen_test.go
+++ b/fs/operations/reopen_test.go
@@ -1,4 +1,4 @@
-package operations
+package operations
import (
"context"
diff --git a/fs/override.go b/fs/override.go
index 9ba8c4aef..c1d597f2b 100644
--- a/fs/override.go
+++ b/fs/override.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import "context"
diff --git a/fs/override_dir.go b/fs/override_dir.go
index a20651155..5e8812b00 100644
--- a/fs/override_dir.go
+++ b/fs/override_dir.go
@@ -1,4 +1,4 @@
-package fs
+package fs
// OverrideDirectory is a wrapper to override the Remote for an
// Directory
diff --git a/fs/override_dir_test.go b/fs/override_dir_test.go
index 0e23e1b55..364843403 100644
--- a/fs/override_dir_test.go
+++ b/fs/override_dir_test.go
@@ -1,4 +1,4 @@
-package fs
+package fs
// Check interfaces satisfied
var _ Directory = (*OverrideDirectory)(nil)
diff --git a/fs/override_test.go b/fs/override_test.go
index f60dc7bdb..dfd959c5a 100644
--- a/fs/override_test.go
+++ b/fs/override_test.go
@@ -1,4 +1,4 @@
-package fs
+package fs
// Check all optional interfaces satisfied
var _ FullObjectInfo = (*OverrideRemote)(nil)
diff --git a/fs/pacer.go b/fs/pacer.go
index f08d52154..b717e74d2 100644
--- a/fs/pacer.go
+++ b/fs/pacer.go
@@ -1,4 +1,4 @@
-// Pacer with logging and calculator
+// Pacer with logging and calculator
package fs
diff --git a/fs/parseduration.go b/fs/parseduration.go
index 7f8ba99d3..b3a118f14 100644
--- a/fs/parseduration.go
+++ b/fs/parseduration.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"encoding/json"
diff --git a/fs/parseduration_test.go b/fs/parseduration_test.go
index fa2230f75..1c04d5096 100644
--- a/fs/parseduration_test.go
+++ b/fs/parseduration_test.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"encoding/json"
diff --git a/fs/parsetime.go b/fs/parsetime.go
index 504232b20..f20405863 100644
--- a/fs/parsetime.go
+++ b/fs/parsetime.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"encoding/json"
diff --git a/fs/parsetime_test.go b/fs/parsetime_test.go
index 42ab5ac71..d218db383 100644
--- a/fs/parsetime_test.go
+++ b/fs/parsetime_test.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"encoding/json"
diff --git a/fs/rc/cache.go b/fs/rc/cache.go
index 6ed5e848b..93ce8afcc 100644
--- a/fs/rc/cache.go
+++ b/fs/rc/cache.go
@@ -1,4 +1,4 @@
-// Utilities for accessing the Fs cache
+// Utilities for accessing the Fs cache
package rc
diff --git a/fs/rc/cache_test.go b/fs/rc/cache_test.go
index 15952fa04..86f3b862b 100644
--- a/fs/rc/cache_test.go
+++ b/fs/rc/cache_test.go
@@ -1,4 +1,4 @@
-package rc
+package rc
import (
"context"
diff --git a/fs/rc/config.go b/fs/rc/config.go
index 2fb0fc424..8da44ed16 100644
--- a/fs/rc/config.go
+++ b/fs/rc/config.go
@@ -1,4 +1,4 @@
-// Implement config options reading and writing
+// Implement config options reading and writing
//
// This is done here rather than in fs/fs.go so we don't cause a circular dependency
@@ -64,7 +64,7 @@ func filterBlocks(in Params, f func(oi fs.OptionsInfo)) (err error) {
return err
}
blocks := map[string]struct{}{}
- for _, name := range strings.Split(blocksStr, ",") {
+ for name := range strings.SplitSeq(blocksStr, ",") {
if name != "" {
blocks[name] = struct{}{}
}
diff --git a/fs/rc/config_test.go b/fs/rc/config_test.go
index 84b7c7685..a25b761d3 100644
--- a/fs/rc/config_test.go
+++ b/fs/rc/config_test.go
@@ -1,4 +1,4 @@
-package rc
+package rc
import (
"context"
diff --git a/fs/rc/internal.go b/fs/rc/internal.go
index 36387806f..cf7af0c29 100644
--- a/fs/rc/internal.go
+++ b/fs/rc/internal.go
@@ -1,4 +1,4 @@
-// Define the internal rc functions
+// Define the internal rc functions
package rc
diff --git a/fs/rc/internal_test.go b/fs/rc/internal_test.go
index 1b9b13f57..53f52837c 100644
--- a/fs/rc/internal_test.go
+++ b/fs/rc/internal_test.go
@@ -1,4 +1,4 @@
-package rc
+package rc
import (
"context"
diff --git a/fs/rc/jobs/job.go b/fs/rc/jobs/job.go
index 0df3cb7b1..3db0485db 100644
--- a/fs/rc/jobs/job.go
+++ b/fs/rc/jobs/job.go
@@ -1,4 +1,4 @@
-// Package jobs manages background jobs that the rc is running.
+// Package jobs manages background jobs that the rc is running.
package jobs
import (
@@ -191,6 +191,19 @@ func (jobs *Jobs) Get(ID int64) *Job {
return jobs.jobs[ID]
}
+func getJobID(in rc.Params) (int64, bool, error) {
+ jobID, err := in.GetInt64("_jobid")
+ if err != nil {
+ if rc.IsErrParamNotFound(err) {
+ return 0, false, nil
+ } else {
+ return 0, false, err
+ }
+ }
+ delete(in, "_jobid")
+ return jobID, true, nil
+}
+
// Check to see if the group is set
func getGroup(ctx context.Context, in rc.Params, id int64) (context.Context, string, error) {
group, err := in.GetString("_group")
@@ -261,9 +274,13 @@ var jobKey = jobKeyType{}
// NewJob creates a Job and executes it, possibly in the background if _async is set
func (jobs *Jobs) NewJob(ctx context.Context, fn rc.Func, in rc.Params) (job *Job, out rc.Params, err error) {
- id := jobID.Add(1)
in = in.Copy() // copy input so we can change it
+ customID, hasCustomID, err := getJobID(in)
+ if err != nil {
+ return nil, nil, err
+ }
+
ctx, isAsync, err := getAsync(ctx, in)
if err != nil {
return nil, nil, err
@@ -279,6 +296,16 @@ func (jobs *Jobs) NewJob(ctx context.Context, fn rc.Func, in rc.Params) (job *Jo
return nil, nil, err
}
+ var id int64
+ if hasCustomID {
+ if _, ok := jobs.jobs[customID]; ok {
+ return nil, nil, fmt.Errorf("job ID %d already in use", customID)
+ }
+ id = customID
+ } else {
+ id = jobID.Add(1)
+ }
+
ctx, group, err := getGroup(ctx, in, id)
if err != nil {
return nil, nil, err
diff --git a/fs/rc/jobs/job_test.go b/fs/rc/jobs/job_test.go
index dcce3879b..c279e7a59 100644
--- a/fs/rc/jobs/job_test.go
+++ b/fs/rc/jobs/job_test.go
@@ -1,4 +1,4 @@
-package jobs
+package jobs
import (
"context"
@@ -206,7 +206,7 @@ func TestJobRunPanic(t *testing.T) {
runtime.Gosched() // yield to make sure job is updated
// Wait a short time for the panic to propagate
- for i := uint(0); i < 10; i++ {
+ for i := range uint(10) {
job.mu.Lock()
e := job.Error
job.mu.Unlock()
@@ -539,8 +539,7 @@ func TestOnFinish(t *testing.T) {
func TestOnFinishAlreadyFinished(t *testing.T) {
jobID.Store(0)
done := make(chan struct{})
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
+ ctx := t.Context()
job, _, err := NewJob(ctx, shortFn, rc.Params{})
assert.NoError(t, err)
diff --git a/fs/rc/js/main.go b/fs/rc/js/main.go
index f13e88903..d0925437f 100644
--- a/fs/rc/js/main.go
+++ b/fs/rc/js/main.go
@@ -1,4 +1,4 @@
-// Rclone as a wasm library
+// Rclone as a wasm library
//
// This library exports the core rc functionality
diff --git a/fs/rc/js/serve.go b/fs/rc/js/serve.go
index 9f7822730..98cfbaba9 100644
--- a/fs/rc/js/serve.go
+++ b/fs/rc/js/serve.go
@@ -1,4 +1,4 @@
-//go:build none
+//go:build none
package main
diff --git a/fs/rc/params.go b/fs/rc/params.go
index ce4e71370..341a88697 100644
--- a/fs/rc/params.go
+++ b/fs/rc/params.go
@@ -1,4 +1,4 @@
-// Parameter parsing
+// Parameter parsing
package rc
diff --git a/fs/rc/params_test.go b/fs/rc/params_test.go
index d8b9b7e51..ad002559b 100644
--- a/fs/rc/params_test.go
+++ b/fs/rc/params_test.go
@@ -1,4 +1,4 @@
-package rc
+package rc
import (
"errors"
diff --git a/fs/rc/rc.go b/fs/rc/rc.go
index 0442bd50d..be3be117a 100644
--- a/fs/rc/rc.go
+++ b/fs/rc/rc.go
@@ -1,4 +1,4 @@
-// Package rc implements a remote control server and registry for rclone
+// Package rc implements a remote control server and registry for rclone
//
// To register your internal calls, call rc.Add(path, function). Your
// function should take and return a Param. It can also return an
diff --git a/fs/rc/rc_test.go b/fs/rc/rc_test.go
index c6c2ad30a..acbe701ad 100644
--- a/fs/rc/rc_test.go
+++ b/fs/rc/rc_test.go
@@ -1,4 +1,4 @@
-package rc
+package rc
import (
"bytes"
diff --git a/fs/rc/rcflags/rcflags.go b/fs/rc/rcflags/rcflags.go
index 99108c592..053d98b8c 100644
--- a/fs/rc/rcflags/rcflags.go
+++ b/fs/rc/rcflags/rcflags.go
@@ -1,4 +1,4 @@
-// Package rcflags implements command line flags to set up the remote control
+// Package rcflags implements command line flags to set up the remote control
package rcflags
import (
diff --git a/fs/rc/rcserver/metrics.go b/fs/rc/rcserver/metrics.go
index 367e6aeab..3ca2f40f2 100644
--- a/fs/rc/rcserver/metrics.go
+++ b/fs/rc/rcserver/metrics.go
@@ -1,4 +1,4 @@
-// Package rcserver implements the HTTP endpoint to serve the remote control
+// Package rcserver implements the HTTP endpoint to serve the remote control
package rcserver
import (
diff --git a/fs/rc/rcserver/metrics_test.go b/fs/rc/rcserver/metrics_test.go
index 0c51134b6..58b5821b4 100644
--- a/fs/rc/rcserver/metrics_test.go
+++ b/fs/rc/rcserver/metrics_test.go
@@ -1,4 +1,4 @@
-package rcserver
+package rcserver
import (
"context"
diff --git a/fs/rc/rcserver/rcserver.go b/fs/rc/rcserver/rcserver.go
index f94724d42..b811a6c4e 100644
--- a/fs/rc/rcserver/rcserver.go
+++ b/fs/rc/rcserver/rcserver.go
@@ -1,4 +1,4 @@
-// Package rcserver implements the HTTP endpoint to serve the remote control
+// Package rcserver implements the HTTP endpoint to serve the remote control
package rcserver
import (
diff --git a/fs/rc/rcserver/rcserver_test.go b/fs/rc/rcserver/rcserver_test.go
index 673aa4e69..05270a8cd 100644
--- a/fs/rc/rcserver/rcserver_test.go
+++ b/fs/rc/rcserver/rcserver_test.go
@@ -1,4 +1,4 @@
-package rcserver
+package rcserver
import (
"bytes"
diff --git a/fs/rc/registry.go b/fs/rc/registry.go
index 8cc11d526..1aa30e89f 100644
--- a/fs/rc/registry.go
+++ b/fs/rc/registry.go
@@ -1,4 +1,4 @@
-// Define the registry
+// Define the registry
package rc
diff --git a/fs/rc/webgui/plugins.go b/fs/rc/webgui/plugins.go
index 29bf8c1f7..a9afecf21 100644
--- a/fs/rc/webgui/plugins.go
+++ b/fs/rc/webgui/plugins.go
@@ -1,4 +1,4 @@
-// Package webgui provides plugin functionality to the Web GUI.
+// Package webgui provides plugin functionality to the Web GUI.
package webgui
import (
diff --git a/fs/rc/webgui/rc.go b/fs/rc/webgui/rc.go
index c3bada21f..a7d50c730 100644
--- a/fs/rc/webgui/rc.go
+++ b/fs/rc/webgui/rc.go
@@ -1,4 +1,4 @@
-package webgui
+package webgui
import (
"context"
diff --git a/fs/rc/webgui/rc_test.go b/fs/rc/webgui/rc_test.go
index 57c5c4a09..5b4088185 100644
--- a/fs/rc/webgui/rc_test.go
+++ b/fs/rc/webgui/rc_test.go
@@ -1,4 +1,4 @@
-package webgui
+package webgui
import (
"context"
diff --git a/fs/rc/webgui/webgui.go b/fs/rc/webgui/webgui.go
index bcc231d3a..b831802aa 100644
--- a/fs/rc/webgui/webgui.go
+++ b/fs/rc/webgui/webgui.go
@@ -1,4 +1,4 @@
-// Package webgui defines the Web GUI helpers.
+// Package webgui defines the Web GUI helpers.
package webgui
import (
diff --git a/fs/registry.go b/fs/registry.go
index 2df893394..ab0c70948 100644
--- a/fs/registry.go
+++ b/fs/registry.go
@@ -1,4 +1,4 @@
-// Filesystem registry and backend options
+// Filesystem registry and backend options
package fs
diff --git a/fs/sizesuffix.go b/fs/sizesuffix.go
index c8d454e81..9e8a7bf94 100644
--- a/fs/sizesuffix.go
+++ b/fs/sizesuffix.go
@@ -1,4 +1,4 @@
-package fs
+package fs
// SizeSuffix is parsed by flag with K/M/G binary suffixes
import (
diff --git a/fs/sizesuffix_test.go b/fs/sizesuffix_test.go
index 732ec4372..106e752e2 100644
--- a/fs/sizesuffix_test.go
+++ b/fs/sizesuffix_test.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"encoding/json"
diff --git a/fs/sync/pipe.go b/fs/sync/pipe.go
index 124a07780..300b08ab5 100644
--- a/fs/sync/pipe.go
+++ b/fs/sync/pipe.go
@@ -1,4 +1,4 @@
-package sync
+package sync
import (
"context"
diff --git a/fs/sync/pipe_test.go b/fs/sync/pipe_test.go
index 94916aa82..8283c5f5e 100644
--- a/fs/sync/pipe_test.go
+++ b/fs/sync/pipe_test.go
@@ -1,4 +1,4 @@
-package sync
+package sync
import (
"container/heap"
diff --git a/fs/sync/rc.go b/fs/sync/rc.go
index 42ca315f6..af80fd65d 100644
--- a/fs/sync/rc.go
+++ b/fs/sync/rc.go
@@ -1,4 +1,4 @@
-package sync
+package sync
import (
"context"
@@ -8,7 +8,6 @@ import (
func init() {
for _, name := range []string{"sync", "copy", "move"} {
- name := name
moveHelp := ""
if name == "move" {
moveHelp = "- deleteEmptySrcDirs - delete empty src directories if set\n"
diff --git a/fs/sync/rc_test.go b/fs/sync/rc_test.go
index ad5145633..15c7276a7 100644
--- a/fs/sync/rc_test.go
+++ b/fs/sync/rc_test.go
@@ -1,4 +1,4 @@
-package sync
+package sync
import (
"context"
diff --git a/fs/sync/sync.go b/fs/sync/sync.go
index e5feb5e3c..4e34e6f21 100644
--- a/fs/sync/sync.go
+++ b/fs/sync/sync.go
@@ -1,4 +1,4 @@
-// Package sync is the implementation of sync/copy/move
+// Package sync is the implementation of sync/copy/move
package sync
import (
@@ -743,7 +743,7 @@ func parseTrackRenamesStrategy(strategies string) (strategy trackRenamesStrategy
if len(strategies) == 0 {
return strategy, nil
}
- for _, s := range strings.Split(strategies, ",") {
+ for s := range strings.SplitSeq(strategies, ",") {
switch s {
case "hash":
strategy |= trackRenamesStrategyHash
diff --git a/fs/sync/sync_test.go b/fs/sync/sync_test.go
index 6fe57d824..f6f60539b 100644
--- a/fs/sync/sync_test.go
+++ b/fs/sync/sync_test.go
@@ -1,4 +1,4 @@
-// Test sync/copy/move
+// Test sync/copy/move
package sync
diff --git a/fs/sync/sync_transform_test.go b/fs/sync/sync_transform_test.go
index 8d0621095..30622f34b 100644
--- a/fs/sync/sync_transform_test.go
+++ b/fs/sync/sync_transform_test.go
@@ -1,4 +1,4 @@
-// Test transform
+// Test transform
package sync
@@ -136,7 +136,7 @@ func makeTestFiles(t *testing.T, r *fstest.Run, dir string) []fstest.Item {
items := []fstest.Item{}
for _, c := range alphabet {
var out strings.Builder
- for i := rune(0); i < 7; i++ {
+ for i := range rune(7) {
out.WriteRune(c + i)
}
fileName := path.Join(dir, fmt.Sprintf("%04d-%s.txt", n, out.String()))
diff --git a/fs/terminalcolormode.go b/fs/terminalcolormode.go
index 44ec4b94a..a84f7cd3e 100644
--- a/fs/terminalcolormode.go
+++ b/fs/terminalcolormode.go
@@ -1,4 +1,4 @@
-package fs
+package fs
// TerminalColorMode describes how ANSI codes should be handled
type TerminalColorMode = Enum[terminalColorModeChoices]
diff --git a/fs/terminalcolormode_test.go b/fs/terminalcolormode_test.go
index 4d6e0405d..9d5805b91 100644
--- a/fs/terminalcolormode_test.go
+++ b/fs/terminalcolormode_test.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"encoding/json"
diff --git a/fs/tristate.go b/fs/tristate.go
index dd70a6a62..84f4c5e1b 100644
--- a/fs/tristate.go
+++ b/fs/tristate.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"encoding/json"
diff --git a/fs/tristate_test.go b/fs/tristate_test.go
index b70f9a928..00fd7a1dd 100644
--- a/fs/tristate_test.go
+++ b/fs/tristate_test.go
@@ -1,4 +1,4 @@
-package fs
+package fs
import (
"encoding/json"
diff --git a/fs/types.go b/fs/types.go
index dc3cc0cef..7c126e3db 100644
--- a/fs/types.go
+++ b/fs/types.go
@@ -1,4 +1,4 @@
-// Filesystem related types and interfaces
+// Filesystem related types and interfaces
// Note that optional interfaces are found in features.go
package fs
diff --git a/fs/version.go b/fs/version.go
index bebf62fd1..28652902a 100644
--- a/fs/version.go
+++ b/fs/version.go
@@ -1,4 +1,4 @@
-package fs
+package fs
// Version of rclone containing the complete version string
var Version string
diff --git a/fs/versioncheck.go b/fs/versioncheck.go
index 0436eb82f..eafc5e339 100644
--- a/fs/versioncheck.go
+++ b/fs/versioncheck.go
@@ -1,4 +1,4 @@
-//go:build !go1.24
+//go:build !go1.24
package fs
diff --git a/fs/versionsuffix.go b/fs/versionsuffix.go
index cf7c7cfcd..7da5ee697 100644
--- a/fs/versionsuffix.go
+++ b/fs/versionsuffix.go
@@ -1,4 +1,4 @@
-package fs
+package fs
// VersionSuffix of rclone containing the pre-release label if any
var VersionSuffix = "DEV"
diff --git a/fs/versiontag.go b/fs/versiontag.go
index bf11cfc72..8b1ead9d8 100644
--- a/fs/versiontag.go
+++ b/fs/versiontag.go
@@ -1,4 +1,4 @@
-package fs
+package fs
// VersionTag of rclone
-var VersionTag = "v1.73.1"
+var VersionTag = "v1.73.2"
diff --git a/fs/walk/walk.go b/fs/walk/walk.go
index d9857a2f1..28b4ccf4f 100644
--- a/fs/walk/walk.go
+++ b/fs/walk/walk.go
@@ -1,4 +1,4 @@
-// Package walk walks directories
+// Package walk walks directories
package walk
import (
diff --git a/fs/walk/walk_test.go b/fs/walk/walk_test.go
index 7ba0e59da..dc8dc7a30 100644
--- a/fs/walk/walk_test.go
+++ b/fs/walk/walk_test.go
@@ -1,4 +1,4 @@
-package walk
+package walk
import (
"context"
diff --git a/fstest/fstest.go b/fstest/fstest.go
index a0205a1c9..89f7cb68f 100644
--- a/fstest/fstest.go
+++ b/fstest/fstest.go
@@ -1,4 +1,4 @@
-// Package fstest provides utilities for testing the Fs
+// Package fstest provides utilities for testing the Fs
package fstest
// FIXME put name of test FS in Fs structure
diff --git a/fstest/fstests/fstests.go b/fstest/fstests/fstests.go
index 5e1105cfc..c3075e161 100644
--- a/fstest/fstests/fstests.go
+++ b/fstest/fstests/fstests.go
@@ -1,4 +1,4 @@
-// Package fstests provides generic integration tests for the Fs and
+// Package fstests provides generic integration tests for the Fs and
// Object interfaces.
//
// These tests are concerned with the basic functionality of a
diff --git a/fstest/mockdir/dir.go b/fstest/mockdir/dir.go
index 103692910..38de12b10 100644
--- a/fstest/mockdir/dir.go
+++ b/fstest/mockdir/dir.go
@@ -1,4 +1,4 @@
-// Package mockdir makes a mock fs.Directory object
+// Package mockdir makes a mock fs.Directory object
package mockdir
import (
diff --git a/fstest/mockfs/mockfs.go b/fstest/mockfs/mockfs.go
index 68df1f6b4..94dd2aba4 100644
--- a/fstest/mockfs/mockfs.go
+++ b/fstest/mockfs/mockfs.go
@@ -1,4 +1,4 @@
-// Package mockfs provides mock Fs for testing.
+// Package mockfs provides mock Fs for testing.
package mockfs
import (
diff --git a/fstest/mockobject/mockobject.go b/fstest/mockobject/mockobject.go
index 89f0fab74..67545684b 100644
--- a/fstest/mockobject/mockobject.go
+++ b/fstest/mockobject/mockobject.go
@@ -1,4 +1,4 @@
-// Package mockobject provides a mock object which can be created from a string
+// Package mockobject provides a mock object which can be created from a string
package mockobject
import (
diff --git a/fstest/run.go b/fstest/run.go
index 4ffb33692..282e2275a 100644
--- a/fstest/run.go
+++ b/fstest/run.go
@@ -1,4 +1,4 @@
-/*
+/*
This provides Run for use in creating test suites
diff --git a/fstest/runs/config.go b/fstest/runs/config.go
index 82b19c8f3..b33c73412 100644
--- a/fstest/runs/config.go
+++ b/fstest/runs/config.go
@@ -1,4 +1,4 @@
-// Config handling
+// Config handling
// Package runs provides the types used by test_all
package runs
diff --git a/fstest/runs/report.go b/fstest/runs/report.go
index f3c7580aa..3ee9a6782 100644
--- a/fstest/runs/report.go
+++ b/fstest/runs/report.go
@@ -1,15 +1,16 @@
-package runs
+package runs
import (
+ "bytes"
"encoding/json"
"fmt"
"html/template"
"os"
"os/exec"
"path"
- "regexp"
"runtime"
"sort"
+ "strings"
"time"
"github.com/rclone/rclone/fs"
@@ -45,14 +46,6 @@ type ReportRun struct {
Runs Runs
}
-// Parse version numbers
-// v1.49.0
-// v1.49.0-031-g2298834e-beta
-// v1.49.0-032-g20793a5f-sharefile-beta
-// match 1 is commit number
-// match 2 is branch name
-var parseVersion = regexp.MustCompile(`^v(?:[0-9.]+)-(?:\d+)-g([0-9a-f]+)(?:-(.*))?-beta$`)
-
// FIXME take -issue or -pr parameter...
// NewReport initialises and returns a Report
@@ -82,17 +75,33 @@ func NewReport(Opt RunOpt) *Report {
// Online version
r.URL = Opt.URLBase + r.DateTime + "/index.html"
- // Get branch/commit out of version
- parts := parseVersion.FindStringSubmatch(r.Version)
- if len(parts) >= 3 {
- r.Commit = parts[1]
- r.Branch = parts[2]
+ // Get branch/commit
+ r.Branch, r.Commit = gitBranchAndCommit()
+
+ return r
+}
+
+// gitBranchAndCommit returns the current branch and commit hash.
+//
+// It returns "" on error.
+func gitBranchAndCommit() (branch, commit string) {
+ // branch (empty if detached)
+ var b bytes.Buffer
+ cmdB := exec.Command("git", "symbolic-ref", "--short", "-q", "HEAD")
+ cmdB.Stdout = &b
+ if e := cmdB.Run(); e == nil {
+ branch = strings.TrimSpace(b.String())
}
- if r.Branch == "" {
- r.Branch = "master"
+
+ // commit (full SHA)
+ var c bytes.Buffer
+ cmdC := exec.Command("git", "rev-parse", "HEAD")
+ cmdC.Stdout = &c
+ if e := cmdC.Run(); e == nil {
+ commit = strings.TrimSpace(c.String())
}
- return r
+ return branch, commit
}
// End should be called when the tests are complete
diff --git a/fstest/runs/run.go b/fstest/runs/run.go
index 2859e7392..cecce1222 100644
--- a/fstest/runs/run.go
+++ b/fstest/runs/run.go
@@ -1,4 +1,4 @@
-// Run a test
+// Run a test
package runs
@@ -157,7 +157,7 @@ func testsToRegexp(tests []string) string {
// Make a trie showing which parts are used at each level
for _, test := range tests {
parent := split
- for _, name := range strings.Split(test, "/") {
+ for name := range strings.SplitSeq(test, "/") {
current := parent[name]
if current == nil {
current = trie{}
diff --git a/fstest/runs/run_test.go b/fstest/runs/run_test.go
index 4353b1417..ad6316daf 100644
--- a/fstest/runs/run_test.go
+++ b/fstest/runs/run_test.go
@@ -1,4 +1,4 @@
-package runs
+package runs
import (
"fmt"
diff --git a/fstest/test_all/clean.go b/fstest/test_all/clean.go
index a6f52d199..5911bee61 100644
--- a/fstest/test_all/clean.go
+++ b/fstest/test_all/clean.go
@@ -1,4 +1,4 @@
-// Clean the left over test files
+// Clean the left over test files
package main
diff --git a/fstest/test_all/config.yaml b/fstest/test_all/config.yaml
index b5311bae7..5cec06838 100644
--- a/fstest/test_all/config.yaml
+++ b/fstest/test_all/config.yaml
@@ -139,6 +139,7 @@ backends:
- backend: "compress"
remote: "TestCompressDrive:"
fastlist: false
+ extratime: 2.0
- backend: "compress"
remote: "TestCompressS3:"
fastlist: false
@@ -610,6 +611,7 @@ backends:
- backend: "zoho"
remote: "TestZoho:"
fastlist: false
+ extratime: 2.0
tests:
- backend
- backend: "hdfs"
diff --git a/fstest/test_all/test_all.go b/fstest/test_all/test_all.go
index ae1c73ab6..cf64d8e95 100644
--- a/fstest/test_all/test_all.go
+++ b/fstest/test_all/test_all.go
@@ -1,4 +1,4 @@
-// Run tests for all the remotes. Run this with package names which
+// Run tests for all the remotes. Run this with package names which
// need integration testing.
//
// See the `test` target in the Makefile.
diff --git a/fstest/testserver/init.d/TestSwiftAIO b/fstest/testserver/init.d/TestSwiftAIO
index 7a04d63ff..7e20bd67c 100755
--- a/fstest/testserver/init.d/TestSwiftAIO
+++ b/fstest/testserver/init.d/TestSwiftAIO
@@ -8,10 +8,12 @@ PORT=28628
. $(dirname "$0")/docker.bash
start() {
+ # We need to replace the remakerings in the container to create Policy-1.
docker run --rm -d --name ${NAME} \
-p 127.0.0.1:${PORT}:8080 \
- bouncestorage/swift-aio
-
+ -v $(dirname "$0")/TestSwiftAIO.d/remakerings:/etc/swift/remakerings:ro \
+ openstackswift/saio
+
echo type=swift
echo env_auth=false
echo user=test:tester
diff --git a/fstest/testserver/init.d/TestSwiftAIO.d/remakerings b/fstest/testserver/init.d/TestSwiftAIO.d/remakerings
new file mode 100755
index 000000000..27c49b17f
--- /dev/null
+++ b/fstest/testserver/init.d/TestSwiftAIO.d/remakerings
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+if ! grep -q "^\[storage-policy:1\]" swift.conf; then
+ cat <> swift.conf
+
+[storage-policy:1]
+name = Policy-1
+EOF
+fi
+
+rm -f *.builder *.ring.gz backups/*.builder backups/*.ring.gz
+
+swift-ring-builder object.builder create 10 1 1
+swift-ring-builder object.builder add r1z1-127.0.0.1:6200/swift-d0 1
+swift-ring-builder object.builder add r1z1-127.0.0.1:6200/swift-d1 1
+swift-ring-builder object.builder add r1z1-127.0.0.1:6200/swift-d2 1
+swift-ring-builder object.builder add r1z1-127.0.0.1:6200/swift-d3 1
+swift-ring-builder object.builder add r1z1-127.0.0.1:6200/swift-d4 1
+swift-ring-builder object.builder add r1z1-127.0.0.1:6200/swift-d5 1
+swift-ring-builder object.builder rebalance
+swift-ring-builder container.builder create 10 1 1
+swift-ring-builder container.builder add r1z1-127.0.0.1:6201/swift-d0 1
+swift-ring-builder container.builder add r1z1-127.0.0.1:6201/swift-d1 1
+swift-ring-builder container.builder add r1z1-127.0.0.1:6201/swift-d2 1
+swift-ring-builder container.builder add r1z1-127.0.0.1:6201/swift-d3 1
+swift-ring-builder container.builder add r1z1-127.0.0.1:6201/swift-d4 1
+swift-ring-builder container.builder add r1z1-127.0.0.1:6201/swift-d5 1
+swift-ring-builder container.builder rebalance
+swift-ring-builder account.builder create 10 1 1
+swift-ring-builder account.builder add r1z1-127.0.0.1:6202/swift-d0 1
+swift-ring-builder account.builder add r1z1-127.0.0.1:6202/swift-d1 1
+swift-ring-builder account.builder add r1z1-127.0.0.1:6202/swift-d2 1
+swift-ring-builder account.builder add r1z1-127.0.0.1:6202/swift-d3 1
+swift-ring-builder account.builder add r1z1-127.0.0.1:6202/swift-d4 1
+swift-ring-builder account.builder add r1z1-127.0.0.1:6202/swift-d5 1
+swift-ring-builder account.builder rebalance
+
+# For Policy-1:
+swift-ring-builder object-1.builder create 10 1 1
+swift-ring-builder object-1.builder add r1z1-127.0.0.1:6200/swift-d0 1
+swift-ring-builder object-1.builder add r1z1-127.0.0.1:6200/swift-d1 1
+swift-ring-builder object-1.builder add r1z1-127.0.0.1:6200/swift-d2 1
+swift-ring-builder object-1.builder add r1z1-127.0.0.1:6200/swift-d3 1
+swift-ring-builder object-1.builder add r1z1-127.0.0.1:6200/swift-d4 1
+swift-ring-builder object-1.builder add r1z1-127.0.0.1:6200/swift-d5 1
+swift-ring-builder object-1.builder rebalance
diff --git a/fstest/testserver/init.d/TestSwiftAIOsegments b/fstest/testserver/init.d/TestSwiftAIOsegments
index 197487bcc..db02630ee 100755
--- a/fstest/testserver/init.d/TestSwiftAIOsegments
+++ b/fstest/testserver/init.d/TestSwiftAIOsegments
@@ -8,9 +8,11 @@ PORT=28632
. $(dirname "$0")/docker.bash
start() {
+ # We need to replace the remakerings in the container to create Policy-1.
docker run --rm -d --name ${NAME} \
-p 127.0.0.1:${PORT}:8080 \
- bouncestorage/swift-aio
+ -v $(dirname "$0")/TestSwiftAIO.d/remakerings:/etc/swift/remakerings:ro \
+ openstackswift/saio
echo type=swift
echo env_auth=false
diff --git a/fstest/testserver/testserver.go b/fstest/testserver/testserver.go
index 2d7fe44bd..f826eace0 100644
--- a/fstest/testserver/testserver.go
+++ b/fstest/testserver/testserver.go
@@ -1,4 +1,4 @@
-// Package testserver starts and stops test servers if required
+// Package testserver starts and stops test servers if required
package testserver
import (
@@ -82,7 +82,7 @@ func start(name string) error {
// parse the output and set environment vars from it
var connect string
var connectDelay time.Duration
- for _, line := range bytes.Split(out, []byte("\n")) {
+ for line := range bytes.SplitSeq(out, []byte("\n")) {
line = bytes.TrimSpace(line)
part := matchLine.FindSubmatch(line)
if part != nil {
@@ -110,7 +110,7 @@ func start(name string) error {
return nil
}
// If we got a _connect value then try to connect to it
- const maxTries = 30
+ const maxTries = 100
var rdBuf = make([]byte, 1)
for i := 1; i <= maxTries; i++ {
if i != 0 {
@@ -175,7 +175,16 @@ func Start(remoteName string) (fn func(), err error) {
if running[name] <= 0 {
// if server isn't running check to see if this server has
// been started already but not by us and stop it if so
- if os.Getenv(envKey(name, "type")) == "" && isRunning(name) {
+ const maxTries = 10
+ for i := 1; i <= maxTries; i++ {
+ if os.Getenv(envKey(name, "type")) == "" && !isRunning(name) {
+ fs.Logf(name, "Stopped server")
+ break
+ }
+ if i != 1 {
+ time.Sleep(time.Second)
+ fs.Logf(name, "Attempting to stop %s try %d/%d", name, i, maxTries)
+ }
stop(name)
}
if !isRunning(name) {
@@ -211,6 +220,6 @@ func stop(name string) {
fs.Errorf(name, "Failed to stop server: %v", err)
}
running[name] = 0
- fs.Logf(name, "Stopped server")
+ fs.Logf(name, "Stopping server")
}
}
diff --git a/fstest/testy/testy.go b/fstest/testy/testy.go
index 5b73c0139..b42091ff0 100644
--- a/fstest/testy/testy.go
+++ b/fstest/testy/testy.go
@@ -1,4 +1,4 @@
-// Package testy contains test utilities for rclone
+// Package testy contains test utilities for rclone
package testy
import (
diff --git a/go.mod b/go.mod
index 8cb3066b9..a7f6ea9b7 100644
--- a/go.mod
+++ b/go.mod
@@ -4,36 +4,36 @@ go 1.24.0
require (
bazil.org/fuse v0.0.0-20230120002735-62a210ff1fd5
- github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.2
- github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.11.0
+ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1
+ github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.12.0
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2
github.com/Azure/azure-sdk-for-go/sdk/storage/azfile v1.5.2
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
- github.com/Files-com/files-sdk-go/v3 v3.2.218
+ github.com/Files-com/files-sdk-go/v3 v3.2.242
github.com/Max-Sum/base32768 v0.0.0-20230304063302-18e6ce5945fd
github.com/a8m/tree v0.0.0-20240104212747-2c8764a5f17e
github.com/aalpar/deheap v0.0.0-20210914013432-0cc84d79dec3
github.com/abbot/go-http-auth v0.4.0
github.com/anacrolix/dms v1.7.2
- github.com/anacrolix/log v0.16.0
+ github.com/anacrolix/log v0.17.0
github.com/atotto/clipboard v0.1.4
- github.com/aws/aws-sdk-go-v2 v1.38.0
- github.com/aws/aws-sdk-go-v2/config v1.31.0
- github.com/aws/aws-sdk-go-v2/credentials v1.18.4
- github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.18.4
- github.com/aws/aws-sdk-go-v2/service/s3 v1.87.0
- github.com/aws/smithy-go v1.22.5
+ github.com/aws/aws-sdk-go-v2 v1.39.1
+ github.com/aws/aws-sdk-go-v2/config v1.31.10
+ github.com/aws/aws-sdk-go-v2/credentials v1.18.14
+ github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.19.8
+ github.com/aws/aws-sdk-go-v2/service/s3 v1.88.2
+ github.com/aws/smithy-go v1.23.0
github.com/buengese/sgzip v0.1.1
- github.com/cloudinary/cloudinary-go/v2 v2.12.0
+ github.com/cloudinary/cloudinary-go/v2 v2.13.0
github.com/cloudsoda/go-smb2 v0.0.0-20250228001242-d4c70e6251cc
github.com/colinmarc/hdfs/v2 v2.4.0
github.com/coreos/go-semver v0.3.1
- github.com/coreos/go-systemd/v22 v22.5.0
+ github.com/coreos/go-systemd/v22 v22.6.0
github.com/dop251/scsu v0.0.0-20220106150536-84ac88021d00
github.com/dropbox/dropbox-sdk-go-unofficial/v6 v6.0.5
- github.com/gabriel-vasile/mimetype v1.4.9
- github.com/gdamore/tcell/v2 v2.8.1
- github.com/go-chi/chi/v5 v5.2.2
+ github.com/gabriel-vasile/mimetype v1.4.10
+ github.com/gdamore/tcell/v2 v2.9.0
+ github.com/go-chi/chi/v5 v5.2.3
github.com/go-darwin/apfs v0.0.0-20211011131704-f84b94dbf348
github.com/go-git/go-billy/v5 v5.6.2
github.com/google/uuid v1.6.0
@@ -47,49 +47,49 @@ require (
github.com/klauspost/compress v1.18.0
github.com/koofr/go-httpclient v0.0.0-20240520111329-e20f8f203988
github.com/koofr/go-koofrclient v0.0.0-20221207135200-cbd7fc9ad6a6
- github.com/lanrat/extsort v1.4.0
+ github.com/lanrat/extsort v1.4.2
github.com/mattn/go-colorable v0.1.14
- github.com/mattn/go-runewidth v0.0.16
+ github.com/mattn/go-runewidth v0.0.17
github.com/minio/minio-go/v7 v7.0.95
github.com/mitchellh/go-homedir v1.1.0
github.com/moby/sys/mountinfo v0.7.2
github.com/ncw/swift/v2 v2.0.4
- github.com/oracle/oci-go-sdk/v65 v65.98.0
+ github.com/oracle/oci-go-sdk/v65 v65.101.0
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/peterh/liner v1.2.2
github.com/pkg/sftp v1.13.9
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2
- github.com/prometheus/client_golang v1.23.0
+ github.com/prometheus/client_golang v1.23.2
github.com/putdotio/go-putio/putio v0.0.0-20200123120452-16d982cac2b8
- github.com/quasilyte/go-ruleguard/dsl v0.3.22
+ github.com/quasilyte/go-ruleguard/dsl v0.3.23
github.com/rclone/gofakes3 v0.0.4
github.com/rfjakob/eme v1.1.2
github.com/rivo/uniseg v0.4.7
github.com/rogpeppe/go-internal v1.14.1
- github.com/shirou/gopsutil/v4 v4.25.7
+ github.com/shirou/gopsutil/v4 v4.25.8
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
- github.com/spf13/cobra v1.9.1
- github.com/spf13/pflag v1.0.7
- github.com/stretchr/testify v1.10.0
- github.com/t3rm1n4l/go-mega v0.0.0-20241213151442-a19cff0ec7b5
+ github.com/spf13/cobra v1.10.1
+ github.com/spf13/pflag v1.0.10
+ github.com/stretchr/testify v1.11.1
+ github.com/t3rm1n4l/go-mega v0.0.0-20250926104142-ccb8d3498e6c
github.com/unknwon/goconfig v1.0.0
github.com/willscott/go-nfs v0.0.3
- github.com/winfsp/cgofuse v1.6.0
+ github.com/winfsp/cgofuse v1.6.1-0.20250813110601-7d90b0992471
github.com/xanzy/ssh-agent v0.3.3
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78
github.com/yunify/qingstor-sdk-go/v3 v3.2.0
github.com/zeebo/blake3 v0.2.4
github.com/zeebo/xxh3 v1.0.2
- go.etcd.io/bbolt v1.4.2
- goftp.io/server/v2 v2.0.1
- golang.org/x/crypto v0.41.0
- golang.org/x/net v0.43.0
- golang.org/x/oauth2 v0.30.0
- golang.org/x/sync v0.16.0
- golang.org/x/sys v0.35.0
- golang.org/x/text v0.28.0
- golang.org/x/time v0.12.0
- google.golang.org/api v0.247.0
+ go.etcd.io/bbolt v1.4.3
+ goftp.io/server/v2 v2.0.2
+ golang.org/x/crypto v0.42.0
+ golang.org/x/net v0.44.0
+ golang.org/x/oauth2 v0.31.0
+ golang.org/x/sync v0.17.0
+ golang.org/x/sys v0.36.0
+ golang.org/x/text v0.29.0
+ golang.org/x/time v0.13.0
+ google.golang.org/api v0.250.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/validator.v2 v2.0.1
gopkg.in/yaml.v3 v3.0.1
@@ -97,11 +97,11 @@ require (
)
require (
- cloud.google.com/go/auth v0.16.4 // indirect
+ cloud.google.com/go/auth v0.16.5 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
- cloud.google.com/go/compute/metadata v0.8.0 // indirect
+ cloud.google.com/go/compute/metadata v0.9.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
- github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect
+ github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0 // indirect
github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf // indirect
github.com/ProtonMail/gluon v0.17.1-0.20230724134000-308be39be96e // indirect
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect
@@ -109,24 +109,25 @@ require (
github.com/ProtonMail/gopenpgp/v2 v2.9.0 // indirect
github.com/PuerkitoBio/goquery v1.10.3 // indirect
github.com/akavel/rsrc v0.10.2 // indirect
- github.com/anacrolix/generics v0.0.3 // indirect
+ github.com/anacrolix/generics v0.1.0 // indirect
github.com/andybalholm/cascadia v1.3.3 // indirect
github.com/appscode/go-querystring v0.0.0-20170504095604-0126cfb3f1dc // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
- github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.0 // indirect
- github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.3 // indirect
- github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.3 // indirect
- github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.3 // indirect
+ github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1 // indirect
+ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.8 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.8 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.8 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
- github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.3 // indirect
- github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0 // indirect
- github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.3 // indirect
- github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.3 // indirect
- github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.3 // indirect
- github.com/aws/aws-sdk-go-v2/service/sso v1.28.0 // indirect
- github.com/aws/aws-sdk-go-v2/service/ssooidc v1.33.0 // indirect
- github.com/aws/aws-sdk-go-v2/service/sts v1.37.0 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.8 // indirect
+ github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 // indirect
+ github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.8 // indirect
+ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.8 // indirect
+ github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.8 // indirect
+ github.com/aws/aws-sdk-go-v2/service/sso v1.29.4 // indirect
+ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.0 // indirect
+ github.com/aws/aws-sdk-go-v2/service/sts v1.38.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
+ github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/bradenaw/juniper v0.15.3 // indirect
github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 // indirect
github.com/calebcase/tmpfile v1.0.3 // indirect
@@ -139,7 +140,7 @@ require (
github.com/cronokirby/saferith v0.33.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
- github.com/ebitengine/purego v0.8.4 // indirect
+ github.com/ebitengine/purego v0.9.0 // indirect
github.com/emersion/go-message v0.18.2 // indirect
github.com/emersion/go-vcard v0.0.0-20241024213814-c9703dde27ff // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
@@ -163,7 +164,6 @@ require (
github.com/google/s2a-go v0.1.9 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
github.com/googleapis/gax-go/v2 v2.15.0 // indirect
- github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect
github.com/gorilla/schema v1.4.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
@@ -183,8 +183,8 @@ require (
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/lpar/date v1.0.0 // indirect
- github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
- github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect
+ github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
+ github.com/lufia/plan9stats v0.0.0-20250827001030-24949be3fa54 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/minio/crc64nvme v1.1.1 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
@@ -199,10 +199,10 @@ require (
github.com/pkg/errors v0.9.1 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
- github.com/prometheus/common v0.65.0 // indirect
+ github.com/prometheus/common v0.66.1 // indirect
github.com/prometheus/procfs v0.17.0 // indirect
github.com/rasky/go-xdr v0.0.0-20170124162913-1a41d1a06c93 // indirect
- github.com/relvacode/iso8601 v1.6.0 // indirect
+ github.com/relvacode/iso8601 v1.7.0 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 // indirect
@@ -210,30 +210,31 @@ require (
github.com/samber/lo v1.51.0 // indirect
github.com/shabbyrobe/gocovmerge v0.0.0-20230507112040-c3350d9342df // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
+ github.com/smartystreets/goconvey v1.8.1 // indirect
github.com/sony/gobreaker v1.0.0 // indirect
github.com/spacemonkeygo/monkit/v3 v3.0.24 // indirect
- github.com/tinylib/msgp v1.3.0 // indirect
+ github.com/tinylib/msgp v1.4.0 // indirect
github.com/tklauser/go-sysconf v0.3.15 // indirect
github.com/tklauser/numcpus v0.10.0 // indirect
github.com/willscott/go-nfs-client v0.0.0-20240104095149-b44639837b00 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
github.com/zeebo/errs v1.4.0 // indirect
go.mongodb.org/mongo-driver v1.17.4 // indirect
- go.opentelemetry.io/auto/sdk v1.1.0 // indirect
- go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 // indirect
- go.opentelemetry.io/otel v1.37.0 // indirect
- go.opentelemetry.io/otel/metric v1.37.0 // indirect
- go.opentelemetry.io/otel/trace v1.37.0 // indirect
- go.yaml.in/yaml/v2 v2.4.2 // indirect
- golang.org/x/exp v0.0.0-20250811191247-51f88131bc50 // indirect
- golang.org/x/tools v0.36.0 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20250811230008-5f3141c8851a // indirect
- google.golang.org/grpc v1.74.2 // indirect
- google.golang.org/protobuf v1.36.7 // indirect
+ go.opentelemetry.io/auto/sdk v1.2.1 // indirect
+ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect
+ go.opentelemetry.io/otel v1.38.0 // indirect
+ go.opentelemetry.io/otel/metric v1.38.0 // indirect
+ go.opentelemetry.io/otel/trace v1.38.0 // indirect
+ go.yaml.in/yaml/v2 v2.4.3 // indirect
+ golang.org/x/exp v0.0.0-20250911091902-df9299821621 // indirect
+ golang.org/x/tools v0.37.0 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20250922171735-9219d122eba9 // indirect
+ google.golang.org/grpc v1.75.1 // indirect
+ google.golang.org/protobuf v1.36.9 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
moul.io/http2curl/v2 v2.3.0 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
- storj.io/common v0.0.0-20250808122759-804533d519c1 // indirect
+ storj.io/common v0.0.0-20250918032746-784a656bec7e // indirect
storj.io/drpc v0.0.35-0.20250513201419-f7819ea69b55 // indirect
storj.io/eventkit v0.0.0-20250410172343-61f26d3de156 // indirect
storj.io/infectious v0.0.2 // indirect
@@ -246,6 +247,7 @@ require (
github.com/ProtonMail/go-crypto v1.3.0
github.com/golang-jwt/jwt/v4 v4.5.2
github.com/pkg/xattr v0.4.12
- golang.org/x/mobile v0.0.0-20250808145247-395d808d53cd
- golang.org/x/term v0.34.0
+ github.com/pquerna/otp v1.5.0
+ golang.org/x/mobile v0.0.0-20250911085028-6912353760cf
+ golang.org/x/term v0.35.0
)
diff --git a/go.sum b/go.sum
index 409c60c5a..f3a86c7dc 100644
--- a/go.sum
+++ b/go.sum
@@ -15,8 +15,8 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
-cloud.google.com/go/auth v0.16.4 h1:fXOAIQmkApVvcIn7Pc2+5J8QTMVbUGLscnSVNl11su8=
-cloud.google.com/go/auth v0.16.4/go.mod h1:j10ncYwjX/g3cdX7GpEzsdM+d+ZNsXAbb6qXA7p1Y5M=
+cloud.google.com/go/auth v0.16.5 h1:mFWNQ2FEVWAliEQWpAdH80omXFokmrnbDhUS9cBywsI=
+cloud.google.com/go/auth v0.16.5/go.mod h1:utzRfHMP+Vv0mpOkTRQoWD2q3BatTOoWbA7gCc2dUhQ=
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
@@ -25,8 +25,8 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
-cloud.google.com/go/compute/metadata v0.8.0 h1:HxMRIbao8w17ZX6wBnjhcDkW6lTFpgcaobyVfZWqRLA=
-cloud.google.com/go/compute/metadata v0.8.0/go.mod h1:sYOGTp851OV9bOFJ9CH7elVvyzopvWQFNNghtDQ/Biw=
+cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
+cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
@@ -39,10 +39,10 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.2 h1:Hr5FTipp7SL07o2FvoVOX9HRiRH3CR3Mj8pxqCcdD5A=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.2/go.mod h1:QyVsSSN64v5TGltphKLQ2sQxe4OBQg0J1eKRcVBnfgE=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.11.0 h1:MhRfI58HblXzCtWEZCO0feHs8LweePB3s90r7WaR1KU=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.11.0/go.mod h1:okZ+ZURbArNdlJ+ptXoyHNuOETzOl1Oww19rm8I2WLA=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 h1:5YTBM8QDVIBN3sxBil89WfdAAqDZbyJTgh688DSxX5w=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.12.0 h1:wL5IEG5zb7BVv1Kv0Xm92orq+5hB5Nipn3B5tn4Rqfk=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.12.0/go.mod h1:J7MUC/wtRpfGVbQ5sIItY5/FuVWmvzlY21WAOfQnq/I=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA=
@@ -57,12 +57,12 @@ github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
-github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
-github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0 h1:XkkQbfMyuH2jTSjQjSoihryI8GINRcs4xp8lNawg0FI=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
-github.com/Files-com/files-sdk-go/v3 v3.2.218 h1:tIvcbHXNY/bq+Sno6vajOJOxhe5XbU59Fa1ohOybK+s=
-github.com/Files-com/files-sdk-go/v3 v3.2.218/go.mod h1:E0BaGQbcMUcql+AfubCR/iasWKBxX5UZPivnQGC2z0M=
+github.com/Files-com/files-sdk-go/v3 v3.2.242 h1:mE2LHt6hpwacgntXIATo0JJ6MW2Hcthd3V4+GHrdlg4=
+github.com/Files-com/files-sdk-go/v3 v3.2.242/go.mod h1:9nNJzlafE8PnMYGb8zbEKzWsVxfgx/LV2faJgP9HIZ0=
github.com/IBM/go-sdk-core/v5 v5.21.0 h1:DUnYhvC4SoC8T84rx5omnhY3+xcQg/Whyoa3mDPIMkk=
github.com/IBM/go-sdk-core/v5 v5.21.0/go.mod h1:Q3BYO6iDA2zweQPDGbNTtqft5tDcEpm6RTuqMlPcvbw=
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
@@ -98,10 +98,10 @@ github.com/akavel/rsrc v0.10.2 h1:Zxm8V5eI1hW4gGaYsJQUhxpjkENuG91ki8B4zCrvEsw=
github.com/akavel/rsrc v0.10.2/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
github.com/anacrolix/dms v1.7.2 h1:JAAJJIlXp+jT2yEah1EbR1AFpGALHL238uSKFXec2qw=
github.com/anacrolix/dms v1.7.2/go.mod h1:excFJW5MKBhn5yt5ZMyeE9iFVqnO6tEGQl7YG/2tUoQ=
-github.com/anacrolix/generics v0.0.3 h1:wMkQgQzq0obSy1tMkxDu7Ife7PsegOBWHDRaSW31EnM=
-github.com/anacrolix/generics v0.0.3/go.mod h1:MN3ve08Z3zSV/rTuX/ouI4lNdlfTxgdafQJiLzyNRB8=
-github.com/anacrolix/log v0.16.0 h1:DSuyb5kAJwl3Y0X1TRcStVrTS9ST9b0BHW+7neE4Xho=
-github.com/anacrolix/log v0.16.0/go.mod h1:m0poRtlr41mriZlXBQ9SOVZ8yZBkLjOkDhd5Li5pITA=
+github.com/anacrolix/generics v0.1.0 h1:r6OgogjCdml3K5A8ixUG0X9DM4jrQiMfIkZiBOGvIfg=
+github.com/anacrolix/generics v0.1.0/go.mod h1:MN3ve08Z3zSV/rTuX/ouI4lNdlfTxgdafQJiLzyNRB8=
+github.com/anacrolix/log v0.17.0 h1:cZvEGRPCbIg+WK+qAxWj/ap2Gj8cx1haOCSVxNZQpK4=
+github.com/anacrolix/log v0.17.0/go.mod h1:m0poRtlr41mriZlXBQ9SOVZ8yZBkLjOkDhd5Li5pITA=
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
github.com/appscode/go-querystring v0.0.0-20170504095604-0126cfb3f1dc h1:LoL75er+LKDHDUfU5tRvFwxH0LjPpZN8OoG8Ll+liGU=
@@ -110,46 +110,48 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3d
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
-github.com/aws/aws-sdk-go-v2 v1.38.0 h1:UCRQ5mlqcFk9HJDIqENSLR3wiG1VTWlyUfLDEvY7RxU=
-github.com/aws/aws-sdk-go-v2 v1.38.0/go.mod h1:9Q0OoGQoboYIAJyslFyF1f5K1Ryddop8gqMhWx/n4Wg=
-github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.0 h1:6GMWV6CNpA/6fbFHnoAjrv4+LGfyTqZz2LtCHnspgDg=
-github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.0/go.mod h1:/mXlTIVG9jbxkqDnr5UQNQxW1HRYxeGklkM9vAFeabg=
-github.com/aws/aws-sdk-go-v2/config v1.31.0 h1:9yH0xiY5fUnVNLRWO0AtayqwU1ndriZdN78LlhruJR4=
-github.com/aws/aws-sdk-go-v2/config v1.31.0/go.mod h1:VeV3K72nXnhbe4EuxxhzsDc/ByrCSlZwUnWH52Nde/I=
-github.com/aws/aws-sdk-go-v2/credentials v1.18.4 h1:IPd0Algf1b+Qy9BcDp0sCUcIWdCQPSzDoMK3a8pcbUM=
-github.com/aws/aws-sdk-go-v2/credentials v1.18.4/go.mod h1:nwg78FjH2qvsRM1EVZlX9WuGUJOL5od+0qvm0adEzHk=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.3 h1:GicIdnekoJsjq9wqnvyi2elW6CGMSYKhdozE7/Svh78=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.3/go.mod h1:R7BIi6WNC5mc1kfRM7XM/VHC3uRWkjc396sfabq4iOo=
-github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.18.4 h1:0SzCLoPRSK3qSydsaFQWugP+lOBCTPwfcBOm6222+UA=
-github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.18.4/go.mod h1:JAet9FsBHjfdI+TnMBX4ModNNaQHAd3dc/Bk+cNsxeM=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.3 h1:o9RnO+YZ4X+kt5Z7Nvcishlz0nksIt2PIzDglLMP0vA=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.3/go.mod h1:+6aLJzOG1fvMOyzIySYjOFjcguGvVRL68R+uoRencN4=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.3 h1:joyyUFhiTQQmVK6ImzNU9TQSNRNeD9kOklqTzyk5v6s=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.3/go.mod h1:+vNIyZQP3b3B1tSLI0lxvrU9cfM7gpdRXMFfm67ZcPc=
+github.com/aws/aws-sdk-go-v2 v1.39.1 h1:fWZhGAwVRK/fAN2tmt7ilH4PPAE11rDj7HytrmbZ2FE=
+github.com/aws/aws-sdk-go-v2 v1.39.1/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY=
+github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1 h1:i8p8P4diljCr60PpJp6qZXNlgX4m2yQFpYk+9ZT+J4E=
+github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1/go.mod h1:ddqbooRZYNoJ2dsTwOty16rM+/Aqmk/GOXrK8cg7V00=
+github.com/aws/aws-sdk-go-v2/config v1.31.10 h1:7LllDZAegXU3yk41mwM6KcPu0wmjKGQB1bg99bNdQm4=
+github.com/aws/aws-sdk-go-v2/config v1.31.10/go.mod h1:Ge6gzXPjqu4v0oHvgAwvGzYcK921GU0hQM25WF/Kl+8=
+github.com/aws/aws-sdk-go-v2/credentials v1.18.14 h1:TxkI7QI+sFkTItN/6cJuMZEIVMFXeu2dI1ZffkXngKI=
+github.com/aws/aws-sdk-go-v2/credentials v1.18.14/go.mod h1:12x4Uw/vijC11XkctTjy92TNCQ+UnNJkT7fzX0Yd93E=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.8 h1:gLD09eaJUdiszm7vd1btiQUYE0Hj+0I2b8AS+75z9AY=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.8/go.mod h1:4RW3oMPt1POR74qVOC4SbubxAwdP4pCT0nSw3jycOU4=
+github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.19.8 h1:QcAh/TNGM3MWe95ilMWwnieXWXsyM33Mb/RuTGlWLm4=
+github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.19.8/go.mod h1:72m/ZCCgYpXJzsgI8uJFYMnXEjtZ4kkaolL9NRXLSnU=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.8 h1:6bgAZgRyT4RoFWhxS+aoGMFyE0cD1bSzFnEEi4bFPGI=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.8/go.mod h1:KcGkXFVU8U28qS4KvLEcPxytPZPBcRawaH2Pf/0jptE=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.8 h1:HhJYoES3zOz34yWEpGENqJvRVPqpmJyR3+AFg9ybhdY=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.8/go.mod h1:JnA+hPWeYAVbDssp83tv+ysAG8lTfLVXvSsyKg/7xNA=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
-github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.3 h1:ZV2XK2L3HBq9sCKQiQ/MdhZJppH/rH0vddEAamsHUIs=
-github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.3/go.mod h1:b9F9tk2HdHpbf3xbN7rUZcfmJI26N6NcJu/8OsBFI/0=
-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0 h1:6+lZi2JeGKtCraAj1rpoZfKqnQ9SptseRZioejfUOLM=
-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0/go.mod h1:eb3gfbVIxIoGgJsi9pGne19dhCBpK6opTYpQqAmdy44=
-github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.3 h1:3ZKmesYBaFX33czDl6mbrcHb6jeheg6LqjJhQdefhsY=
-github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.3/go.mod h1:7ryVb78GLCnjq7cw45N6oUb9REl7/vNUwjvIqC5UgdY=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.3 h1:ieRzyHXypu5ByllM7Sp4hC5f/1Fy5wqxqY0yB85hC7s=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.3/go.mod h1:O5ROz8jHiOAKAwx179v+7sHMhfobFVi6nZt8DEyiYoM=
-github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.3 h1:SE/e52dq9a05RuxzLcjT+S5ZpQobj3ie3UTaSf2NnZc=
-github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.3/go.mod h1:zkpvBTsR020VVr8TOrwK2TrUW9pOir28sH5ECHpnAfo=
-github.com/aws/aws-sdk-go-v2/service/s3 v1.87.0 h1:egoDf+Geuuntmw79Mz6mk9gGmELCPzg5PFEABOHB+6Y=
-github.com/aws/aws-sdk-go-v2/service/s3 v1.87.0/go.mod h1:t9MDi29H+HDbkolTSQtbI0HP9DemAWQzUjmWC7LGMnE=
-github.com/aws/aws-sdk-go-v2/service/sso v1.28.0 h1:Mc/MKBf2m4VynyJkABoVEN+QzkfLqGj0aiJuEe7cMeM=
-github.com/aws/aws-sdk-go-v2/service/sso v1.28.0/go.mod h1:iS5OmxEcN4QIPXARGhavH7S8kETNL11kym6jhoS7IUQ=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.33.0 h1:6csaS/aJmqZQbKhi1EyEMM7yBW653Wy/B9hnBofW+sw=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.33.0/go.mod h1:59qHWaY5B+Rs7HGTuVGaC32m0rdpQ68N8QCN3khYiqs=
-github.com/aws/aws-sdk-go-v2/service/sts v1.37.0 h1:MG9VFW43M4A8BYeAfaJJZWrroinxeTi2r3+SnmLQfSA=
-github.com/aws/aws-sdk-go-v2/service/sts v1.37.0/go.mod h1:JdeBDPgpJfuS6rU/hNglmOigKhyEZtBmbraLE4GK1J8=
-github.com/aws/smithy-go v1.22.5 h1:P9ATCXPMb2mPjYBgueqJNCA5S9UfktsW0tTxi+a7eqw=
-github.com/aws/smithy-go v1.22.5/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
+github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.8 h1:1/bT9kDdLQzfZ1e6J6hpW+SfNDd6xrV8F3M2CuGyUz8=
+github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.8/go.mod h1:RbdwTONAIi59ej/+1H+QzZORt5bcyAtbrS7FQb2pvz0=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 h1:oegbebPEMA/1Jny7kvwejowCaHz1FWZAQ94WXFNCyTM=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1/go.mod h1:kemo5Myr9ac0U9JfSjMo9yHLtw+pECEHsFtJ9tqCEI8=
+github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.8 h1:tIN8MFT1z5STK5kTdOT1TCfMN/bn5fSEnlKsTL8qBOU=
+github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.8/go.mod h1:VKS56txtNWjKI8FqD/hliL0BcshyF4ZaLBa1rm2Y+5s=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.8 h1:M6JI2aGFEzYxsF6CXIuRBnkge9Wf9a2xU39rNeXgu10=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.8/go.mod h1:Fw+MyTwlwjFsSTE31mH211Np+CUslml8mzc0AFEG09s=
+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.8 h1:AgYCo1Rb8XChJXA871BXHDNxNWOTAr6V5YdsRIBbgv0=
+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.8/go.mod h1:Au9dvIGm1Hbqnt29d3VakOCQuN9l0WrkDDTRq8biWS4=
+github.com/aws/aws-sdk-go-v2/service/s3 v1.88.2 h1:T7b3qniouutV5Wwa9B1q7gW+Y8s1B3g9RE9qa7zLBIM=
+github.com/aws/aws-sdk-go-v2/service/s3 v1.88.2/go.mod h1:tW9TsLb6t1eaTdBE6LITyJW1m/+DjQPU78Q/jT2FJu8=
+github.com/aws/aws-sdk-go-v2/service/sso v1.29.4 h1:FTdEN9dtWPB0EOURNtDPmwGp6GGvMqRJCAihkSl/1No=
+github.com/aws/aws-sdk-go-v2/service/sso v1.29.4/go.mod h1:mYubxV9Ff42fZH4kexj43gFPhgc/LyC7KqvUKt1watc=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.0 h1:I7ghctfGXrscr7r1Ga/mDqSJKm7Fkpl5Mwq79Z+rZqU=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.0/go.mod h1:Zo9id81XP6jbayIFWNuDpA6lMBWhsVy+3ou2jLa4JnA=
+github.com/aws/aws-sdk-go-v2/service/sts v1.38.5 h1:+LVB0xBqEgjQoqr9bGZbRzvg212B0f17JdflleJRNR4=
+github.com/aws/aws-sdk-go-v2/service/sts v1.38.5/go.mod h1:xoaxeqnnUaZjPjaICgIy5B+MHCSb/ZSOn4MvkFNOUA0=
+github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE=
+github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
+github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/bradenaw/juniper v0.15.3 h1:RHIAMEDTpvmzV1wg1jMAHGOoI2oJUSPx3lxRldXnFGo=
github.com/bradenaw/juniper v0.15.3/go.mod h1:UX4FX57kVSaDp4TPqvSjkAAewmRFAfXf27BOs5z9dq8=
github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 h1:GKTyiRCL6zVf5wWaqKnf+7Qs6GbEPfd4iMOitWzXJx8=
@@ -175,8 +177,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
-github.com/cloudinary/cloudinary-go/v2 v2.12.0 h1:uveBJeNpJztKDwFW/B+Wuklq584hQmQXlo+hGTSOGZ8=
-github.com/cloudinary/cloudinary-go/v2 v2.12.0/go.mod h1:ireC4gqVetsjVhYlwjUJwKTbZuWjEIynbR9zQTlqsvo=
+github.com/cloudinary/cloudinary-go/v2 v2.13.0 h1:ugiQwb7DwpWQnete2AZkTh94MonZKmxD7hDGy1qTzDs=
+github.com/cloudinary/cloudinary-go/v2 v2.13.0/go.mod h1:ireC4gqVetsjVhYlwjUJwKTbZuWjEIynbR9zQTlqsvo=
github.com/cloudsoda/go-smb2 v0.0.0-20250228001242-d4c70e6251cc h1:t8YjNUCt1DimB4HCIXBztwWMhgxr5yG5/YaRl9Afdfg=
github.com/cloudsoda/go-smb2 v0.0.0-20250228001242-d4c70e6251cc/go.mod h1:CgWpFCFWzzEA5hVkhAc6DZZzGd3czx+BblvOzjmg6KA=
github.com/cloudsoda/sddl v0.0.0-20250224235906-926454e91efc h1:0xCWmFKBmarCqqqLeM7jFBSw/Or81UEElFqO8MY+GDs=
@@ -188,8 +190,8 @@ github.com/colinmarc/hdfs/v2 v2.4.0 h1:v6R8oBx/Wu9fHpdPoJJjpGSUxo8NhHIwrwsfhFvU9
github.com/colinmarc/hdfs/v2 v2.4.0/go.mod h1:0NAO+/3knbMx6+5pCv+Hcbaz4xn/Zzbn9+WIib2rKVI=
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
-github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
-github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/coreos/go-systemd/v22 v22.6.0 h1:aGVa/v8B7hpb0TKl0MWoAavPDmHvobFe5R5zn0bCJWo=
+github.com/coreos/go-systemd/v22 v22.6.0/go.mod h1:iG+pp635Fo7ZmV/j14KUcmEyWF+0X7Lua8rrTWzYgWU=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
@@ -209,11 +211,10 @@ github.com/dropbox/dropbox-sdk-go-unofficial/v6 v6.0.5 h1:FT+t0UEDykcor4y3dMVKXI
github.com/dropbox/dropbox-sdk-go-unofficial/v6 v6.0.5/go.mod h1:rSS3kM9XMzSQ6pw91Qgd6yB5jdt70N4OdtrAf74As5M=
github.com/dsnet/try v0.0.3 h1:ptR59SsrcFUYbT/FhAbKTV6iLkeD6O18qfIWRml2fqI=
github.com/dsnet/try v0.0.3/go.mod h1:WBM8tRpUmnXXhY1U6/S8dt6UWdHTQ7y8A5YSkRCkq40=
-github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
-github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
-github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
+github.com/ebitengine/purego v0.9.0 h1:mh0zpKBIXDceC63hpvPuGLiJ8ZAa3DfrFTudmfi8A4k=
+github.com/ebitengine/purego v0.9.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/emersion/go-message v0.18.2 h1:rl55SQdjd9oJcIoQNhubD2Acs1E6IzlZISRTK7x/Lpg=
github.com/emersion/go-message v0.18.2/go.mod h1:XpJyL70LwRvq2a8rVbHXikPgKj8+aI0kGdHlg16ibYA=
github.com/emersion/go-vcard v0.0.0-20241024213814-c9703dde27ff h1:4N8wnS3f1hNHSmFD5zgFkWCyA4L1kCDkImPAtK7D6tg=
@@ -232,20 +233,20 @@ github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0X
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
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/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
-github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
+github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
+github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw=
github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo=
-github.com/gdamore/tcell/v2 v2.8.1 h1:KPNxyqclpWpWQlPLx6Xui1pMk8S+7+R37h3g07997NU=
-github.com/gdamore/tcell/v2 v2.8.1/go.mod h1:bj8ori1BG3OYMjmb3IklZVWfZUJ1UBQt9JXrOCOhGWw=
+github.com/gdamore/tcell/v2 v2.9.0 h1:N6t+eqK7/xwtRPwxzs1PXeRWnm0H9l02CrgJ7DLn1ys=
+github.com/gdamore/tcell/v2 v2.9.0/go.mod h1:8/ZoqM9rxzYphT9tH/9LnunhV9oPBqwS8WHGYm5nrmo=
github.com/geoffgarside/ber v1.2.0 h1:/loowoRcs/MWLYmGX9QtIAbA+V/FrnVLsMMPhwiRm64=
github.com/geoffgarside/ber v1.2.0/go.mod h1:jVPKeCbj6MvQZhwLYsGwaGI52oUorHoHKNecGT85ZCc=
github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
-github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618=
-github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
+github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE=
+github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-darwin/apfs v0.0.0-20211011131704-f84b94dbf348 h1:JnrjqG5iR07/8k7NqrLNilRsl3s1EPRQEGvbPyOce68=
github.com/go-darwin/apfs v0.0.0-20211011131704-f84b94dbf348/go.mod h1:Czxo/d1g948LtrALAZdL04TL/HnkopquAjxYUuI02bo=
github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
@@ -281,7 +282,6 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
-github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
@@ -351,9 +351,8 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo=
github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc=
-github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
-github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
-github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
+github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
github.com/gorilla/schema v1.4.1 h1:jUg5hUjCSDZpNGLuXQOgIWGdlgrIdYvgQ0wZtdK1M3E=
github.com/gorilla/schema v1.4.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
@@ -399,7 +398,6 @@ github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh6
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
-github.com/jlaffaye/ftp v0.0.0-20190624084859-c1312a7102bf/go.mod h1:lli8NYPQOFy3O++YmYbqVgOcQ1JPCwdOy+5zSjKJ9qY=
github.com/jlaffaye/ftp v0.2.1-0.20240918233326-1b970516f5d3 h1:ZxO6Qr2GOXPdcW80Mcn3nemvilMPvpWqxrNfK2ZnNNs=
github.com/jlaffaye/ftp v0.2.1-0.20240918233326-1b970516f5d3/go.mod h1:dvLUr/8Fs9a2OBrEnCC5duphbkz/k/mSy5OkXg3PAgI=
github.com/josephspurrier/goversioninfo v1.5.0 h1:9TJtORoyf4YMoWSOo/cXFN9A/lB3PniJ91OxIH6e7Zg=
@@ -423,7 +421,6 @@ github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYW
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
-github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/koofr/go-httpclient v0.0.0-20240520111329-e20f8f203988 h1:CjEMN21Xkr9+zwPmZPaJJw+apzVbjGL5uK/6g9Q2jGU=
github.com/koofr/go-httpclient v0.0.0-20240520111329-e20f8f203988/go.mod h1:/agobYum3uo/8V6yPVnq+R82pyVGCeuWW5arT4Txn8A=
github.com/koofr/go-koofrclient v0.0.0-20221207135200-cbd7fc9ad6a6 h1:FHVoZMOVRA+6/y4yRlbiR3WvsrOcKBd/f64H7YiWR2U=
@@ -440,31 +437,29 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
-github.com/lanrat/extsort v1.4.0 h1:jysS/Tjnp7mBwJ6NG8SY+XYFi8HF3LujGbqY9jOWjco=
-github.com/lanrat/extsort v1.4.0/go.mod h1:hceP6kxKPKebjN1RVrDBXMXXECbaI41Y94tt6MDazc4=
+github.com/lanrat/extsort v1.4.2 h1:akbLIdo4PhNZtvjpaWnbXtGMmLtnGzXplkzfgl+XTTY=
+github.com/lanrat/extsort v1.4.2/go.mod h1:hceP6kxKPKebjN1RVrDBXMXXECbaI41Y94tt6MDazc4=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lpar/date v1.0.0 h1:bq/zVqFTUmsxvd/CylidY4Udqpr9BOFrParoP6p0x/I=
github.com/lpar/date v1.0.0/go.mod h1:KjYe0dDyMQTgpqcUz4LEIeM5VZwhggjVx/V2dtc8NSo=
-github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
-github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
-github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 h1:PpXWgLPs+Fqr325bN2FD2ISlRRztXibcX6e8f5FR5Dc=
-github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
+github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
+github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
+github.com/lufia/plan9stats v0.0.0-20250827001030-24949be3fa54 h1:mFWunSatvkQQDhpdyuFAYwyAan3hzCuma+Pz8sqvOfg=
+github.com/lufia/plan9stats v0.0.0-20250827001030-24949be3fa54/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
-github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
-github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-runewidth v0.0.17 h1:78v8ZlW0bP43XfmAfPsdXcoNCelfMHsDmd/pkENfrjQ=
+github.com/mattn/go-runewidth v0.0.17/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI=
github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
-github.com/minio/minio-go/v6 v6.0.46/go.mod h1:qD0lajrGW49lKZLtXKtCB4X/qkMf0a5tBvN2PaZg7Gg=
github.com/minio/minio-go/v7 v7.0.95 h1:ywOUPg+PebTMTzn9VDsoFJy32ZuARN9zhB+K3IYEvYU=
github.com/minio/minio-go/v7 v7.0.95/go.mod h1:wOOX3uxS334vImCNRVyIDdXX9OsXDm89ToynKgqUKlo=
-github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
github.com/minio/xxml v0.0.3 h1:ZIpPQpfyG5uZQnqqC0LZuWtPk/WT8G/qkxvO6jb7zMU=
github.com/minio/xxml v0.0.3/go.mod h1:wcXErosl6IezQIMEWSK/LYC2VS7LJ1dAkgvuyIN3aH4=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
@@ -491,8 +486,8 @@ github.com/onsi/ginkgo/v2 v2.17.3 h1:oJcvKpIb7/8uLpDDtnQuf18xVnwKp8DTD7DQ6gTd/MU
github.com/onsi/ginkgo/v2 v2.17.3/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc=
github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
-github.com/oracle/oci-go-sdk/v65 v65.98.0 h1:ZKsy97KezSiYSN1Fml4hcwjpO+wq01rjBkPqIiUejVc=
-github.com/oracle/oci-go-sdk/v65 v65.98.0/go.mod h1:RGiXfpDDmRRlLtqlStTzeBjjdUNXyqm3KXKyLCm3A/Q=
+github.com/oracle/oci-go-sdk/v65 v65.101.0 h1:EErMOuw98JXi0P7DgPg5zjouCA5s61iWD5tFWNCVLHk=
+github.com/oracle/oci-go-sdk/v65 v65.101.0/go.mod h1:RGiXfpDDmRRlLtqlStTzeBjjdUNXyqm3KXKyLCm3A/Q=
github.com/panjf2000/ants/v2 v2.11.3 h1:AfI0ngBoXJmYOpDh9m516vjqoUu2sLrIVgppI9TZVpg=
github.com/panjf2000/ants/v2 v2.11.3/go.mod h1:8u92CYMUc6gyvTIw8Ru7Mt7+/ESnJahz5EVtqfrilek=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
@@ -519,31 +514,32 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
-github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=
-github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=
+github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs=
+github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
+github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
+github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
-github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
-github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
+github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
+github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
github.com/putdotio/go-putio/putio v0.0.0-20200123120452-16d982cac2b8 h1:Y258uzXU/potCYnQd1r6wlAnoMB68BiCkCcCnKx1SH8=
github.com/putdotio/go-putio/putio v0.0.0-20200123120452-16d982cac2b8/go.mod h1:bSJjRokAHHOhA+XFxplld8w2R/dXLH7Z3BZ532vhFwU=
-github.com/quasilyte/go-ruleguard/dsl v0.3.22 h1:wd8zkOhSNr+I+8Qeciml08ivDt1pSXe60+5DqOpCjPE=
-github.com/quasilyte/go-ruleguard/dsl v0.3.22/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
+github.com/quasilyte/go-ruleguard/dsl v0.3.23 h1:lxjt5B6ZCiBeeNO8/oQsegE6fLeCzuMRoVWSkXC4uvY=
+github.com/quasilyte/go-ruleguard/dsl v0.3.23/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
github.com/quic-go/quic-go v0.53.0 h1:QHX46sISpG2S03dPeZBgVIZp8dGagIaiu2FiVYvpCZI=
github.com/quic-go/quic-go v0.53.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
github.com/rasky/go-xdr v0.0.0-20170124162913-1a41d1a06c93 h1:UVArwN/wkKjMVhh2EQGC0tEc1+FqiLlvYXY5mQ2f8Wg=
github.com/rasky/go-xdr v0.0.0-20170124162913-1a41d1a06c93/go.mod h1:Nfe4efndBz4TibWycNE+lqyJZiMX4ycx+QKV8Ta0f/o=
github.com/rclone/gofakes3 v0.0.4 h1:LswpC49VY/UJ1zucoL5ktnOEX6lq3qK7e1aFIAfqCbk=
github.com/rclone/gofakes3 v0.0.4/go.mod h1:j/UoS+2/Mr7xAlfKhyVC58YyFQmh9uoQA5YZQXQUqmg=
-github.com/relvacode/iso8601 v1.6.0 h1:eFXUhMJN3Gz8Rcq82f9DTMW0svjtAVuIEULglM7QHTU=
-github.com/relvacode/iso8601 v1.6.0/go.mod h1:FlNp+jz+TXpyRqgmM7tnzHHzBnz776kmAH2h3sZCn0I=
+github.com/relvacode/iso8601 v1.7.0 h1:BXy+V60stMP6cpswc+a93Mq3e65PfXCgDFfhvNNGrdo=
+github.com/relvacode/iso8601 v1.7.0/go.mod h1:FlNp+jz+TXpyRqgmM7tnzHHzBnz776kmAH2h3sZCn0I=
github.com/rfjakob/eme v1.1.2 h1:SxziR8msSOElPayZNFfQw4Tjx/Sbaeeh3eRvrHVMUs4=
github.com/rfjakob/eme v1.1.2/go.mod h1:cVvpasglm/G3ngEfcfT/Wt0GwhkuO32pf/poW6Nyk1k=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
@@ -562,18 +558,17 @@ github.com/samber/lo v1.51.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRo
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shabbyrobe/gocovmerge v0.0.0-20230507112040-c3350d9342df h1:S77Pf5fIGMa7oSwp8SQPp7Hb4ZiI38K3RNBKD2LLeEM=
github.com/shabbyrobe/gocovmerge v0.0.0-20230507112040-c3350d9342df/go.mod h1:dcuzJZ83w/SqN9k4eQqwKYMgmKWzg/KzJAURBhRL1tc=
-github.com/shirou/gopsutil/v4 v4.25.7 h1:bNb2JuqKuAu3tRlPv5piSmBZyMfecwQ+t/ILq+1JqVM=
-github.com/shirou/gopsutil/v4 v4.25.7/go.mod h1:XV/egmwJtd3ZQjBpJVY5kndsiOO4IRqy9TQnmm6VP7U=
-github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/shirou/gopsutil/v4 v4.25.8 h1:NnAsw9lN7587WHxjJA9ryDnqhJpFH6A+wagYWTOH970=
+github.com/shirou/gopsutil/v4 v4.25.8/go.mod h1:q9QdMmfAOVIw7a+eF86P7ISEU6ka+NLgkUxlopV4RwI=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
-github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
-github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
-github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=
-github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
+github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
+github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
+github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
github.com/snabb/httpreaderat v1.0.1 h1:whlb+vuZmyjqVop8x1EKOg05l2NE4z9lsMMXjmSUCnY=
github.com/snabb/httpreaderat v1.0.1/go.mod h1:lpbGrKDWF37yvRbtRvQsbesS6Ty5c83t8ztannPoMsA=
github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
@@ -581,13 +576,12 @@ github.com/sony/gobreaker v1.0.0 h1:feX5fGGXSl3dYd4aHZItw+FpHLvvoaqkawKjVNiFMNQ=
github.com/sony/gobreaker v1.0.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/spacemonkeygo/monkit/v3 v3.0.24 h1:cKixJ+evHnfJhWNyIZjBy5hoW8LTWmrJXPo18tzLNrk=
github.com/spacemonkeygo/monkit/v3 v3.0.24/go.mod h1:XkZYGzknZwkD0AKUnZaSXhRiVTLCkq7CWVa3IsE72gA=
-github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
-github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
-github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
-github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
+github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
+github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
+github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
@@ -602,13 +596,15 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
-github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
+github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/t3rm1n4l/go-mega v0.0.0-20241213151442-a19cff0ec7b5 h1:Sa+sR8aaAMFwxhXWENEnE6ZpqhZ9d7u1RT2722Rw6hc=
github.com/t3rm1n4l/go-mega v0.0.0-20241213151442-a19cff0ec7b5/go.mod h1:UdZiFUFu6e2WjjtjxivwXWcwc1N/8zgbkBR9QNucUOY=
+github.com/t3rm1n4l/go-mega v0.0.0-20250926104142-ccb8d3498e6c h1:BLopNCyqewbE8+BtlIp/Juzu8AJGxz0gHdGADnsblVc=
+github.com/t3rm1n4l/go-mega v0.0.0-20250926104142-ccb8d3498e6c/go.mod h1:ykucQyiE9Q2qx1wLlEtZkkNn1IURib/2O+Mvd25i1Fo=
github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8=
-github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww=
-github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
+github.com/tinylib/msgp v1.4.0 h1:SYOeDRiydzOw9kSiwdYp9UcBgPFtLU2WDHaJXyHruf8=
+github.com/tinylib/msgp v1.4.0/go.mod h1:cvjFkb4RiC8qSBOPMGPSzSAx47nAsfhLVTCZZNuHv5o=
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
@@ -625,8 +621,8 @@ github.com/willscott/go-nfs v0.0.3 h1:Z5fHVxMsppgEucdkKBN26Vou19MtEM875NmRwj156R
github.com/willscott/go-nfs v0.0.3/go.mod h1:VhNccO67Oug787VNXcyx9JDI3ZoSpqoKMT/lWMhUIDg=
github.com/willscott/go-nfs-client v0.0.0-20240104095149-b44639837b00 h1:U0DnHRZFzoIV1oFEZczg5XyPut9yxk9jjtax/9Bxr/o=
github.com/willscott/go-nfs-client v0.0.0-20240104095149-b44639837b00/go.mod h1:Tq++Lr/FgiS3X48q5FETemXiSLGuYMQT2sPjYNPJSwA=
-github.com/winfsp/cgofuse v1.6.0 h1:re3W+HTd0hj4fISPBqfsrwyvPFpzqhDu8doJ9nOPDB0=
-github.com/winfsp/cgofuse v1.6.0/go.mod h1:uxjoF2jEYT3+x+vC2KJddEGdk/LU8pRowXmyVMHSV5I=
+github.com/winfsp/cgofuse v1.6.1-0.20250813110601-7d90b0992471 h1:aSOo0k+aLWdhUQiUxzv4cZ7cUp3OLP+Qx7cjs6OUxME=
+github.com/winfsp/cgofuse v1.6.1-0.20250813110601-7d90b0992471/go.mod h1:uxjoF2jEYT3+x+vC2KJddEGdk/LU8pRowXmyVMHSV5I=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
@@ -650,8 +646,8 @@ github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
-go.etcd.io/bbolt v1.4.2 h1:IrUHp260R8c+zYx/Tm8QZr04CX+qWS5PGfPdevhdm1I=
-go.etcd.io/bbolt v1.4.2/go.mod h1:Is8rSHO/b4f3XigBC0lL0+4FwAQv3HXEEIgFMuKHceM=
+go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
+go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw=
go.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@@ -659,35 +655,33 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
-go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0/go.mod h1:NfchwuyNoMcZ5MLHwPrODwUF1HWCXWrL31s8gSAdIKY=
-go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
-go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
-go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
-go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
-go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
-go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
-go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
-go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
-go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
-go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
+go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
+go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
+go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
+go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
+go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
+go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
+go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
+go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
+go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
+go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
+go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
+go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
-go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
-go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
-goftp.io/server/v2 v2.0.1 h1:H+9UbCX2N206ePDSVNCjBftOKOgil6kQ5RAQNx5hJwE=
-goftp.io/server/v2 v2.0.1/go.mod h1:7+H/EIq7tXdfo1Muu5p+l3oQ6rYkDZ8lY7IM5d5kVdQ=
+go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
+go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
+goftp.io/server/v2 v2.0.2 h1:tkZpqyXys+vC15W5yGMi8Kzmbv1QSgeKr8qJXBnJbm8=
+goftp.io/server/v2 v2.0.2/go.mod h1:Fl1WdcV7fx1pjOWx7jEHb7tsJ8VwE7+xHu6bVJ6r2qg=
golang.org/x/arch v0.14.0 h1:z9JUEZWr8x4rR0OU6c4/4t6E6jOZ8/QBS2bBYBm4tx4=
golang.org/x/arch v0.14.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
@@ -701,8 +695,8 @@ golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
-golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
-golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
+golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
+golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -713,8 +707,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
-golang.org/x/exp v0.0.0-20250811191247-51f88131bc50 h1:3yiSh9fhy5/RhCSntf4Sy0Tnx50DmMpQ4MQdKKk4yg4=
-golang.org/x/exp v0.0.0-20250811191247-51f88131bc50/go.mod h1:rT6SFzZ7oxADUDx58pcaKFTcZ+inxAa9fTrYx/uVYwg=
+golang.org/x/exp v0.0.0-20250911091902-df9299821621 h1:2id6c1/gto0kaHYyrixvknJ8tUK/Qs5IsmBtrc+FtgU=
+golang.org/x/exp v0.0.0-20250911091902-df9299821621/go.mod h1:TwQYMMnGpvZyc+JpB/UAuTNIsVJifOlSkrZkhcvpVUk=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -729,8 +723,8 @@ golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPI
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
-golang.org/x/mobile v0.0.0-20250808145247-395d808d53cd h1:Qd7qm8Xr8riwtdI4F+SWrlnKK/7tLDyTQ5YNv42tvtU=
-golang.org/x/mobile v0.0.0-20250808145247-395d808d53cd/go.mod h1:Rg5Br31eIKqfc+43CRdWRfPfFqV9DjN92usHvW9563E=
+golang.org/x/mobile v0.0.0-20250911085028-6912353760cf h1:2HVicFltkNthxuudLg8n5TzyNVUESF91+X7+/fxEjSM=
+golang.org/x/mobile v0.0.0-20250911085028-6912353760cf/go.mod h1:tfwPrSLpQwNZm2LZ6L4ol2VGzxz+xdyj0fN+n4A50OQ=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
@@ -743,8 +737,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
-golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
-golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
+golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
+golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -753,7 +747,6 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -784,16 +777,16 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
-golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
-golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
+golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
+golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
-golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
+golang.org/x/oauth2 v0.31.0 h1:8Fq0yVZLh4j4YA47vHKFTa9Ew5XIrCP8LC6UeNZnLxo=
+golang.org/x/oauth2 v0.31.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -810,13 +803,12 @@ golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
-golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
-golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
+golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -862,10 +854,9 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
-golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
+golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
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=
@@ -877,10 +868,9 @@ golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
-golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
-golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
-golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
+golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
+golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -896,20 +886,19 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
-golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
-golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
+golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
+golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
-golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
+golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI=
+golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
@@ -952,12 +941,14 @@ 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.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
-golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
-golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
+golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
+golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
+gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
@@ -974,8 +965,8 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
-google.golang.org/api v0.247.0 h1:tSd/e0QrUlLsrwMKmkbQhYVa109qIintOls2Wh6bngc=
-google.golang.org/api v0.247.0/go.mod h1:r1qZOPmxXffXg6xS5uhx16Fa/UFY8QU/K4bfKrnvovM=
+google.golang.org/api v0.250.0 h1:qvkwrf/raASj82UegU2RSDGWi/89WkLckn4LuO4lVXM=
+google.golang.org/api v0.250.0/go.mod h1:Y9Uup8bDLJJtMzJyQnu+rLRJLA0wn+wTtc6vTlOvfXo=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -1013,10 +1004,10 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4=
google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s=
-google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY=
-google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822/go.mod h1:h3c4v36UTKzUiuaOKQ6gr3S+0hovBtUrXzTG/i3+XEc=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250811230008-5f3141c8851a h1:tPE/Kp+x9dMSwUm/uM0JKK0IfdiJkwAbSMSeZBXXJXc=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250811230008-5f3141c8851a/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo=
+google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 h1:FiusG7LWj+4byqhbvmB+Q93B/mOxJLN2DTozDuZm4EU=
+google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250922171735-9219d122eba9 h1:V1jCN2HBa8sySkR5vLcCSqJSTMv093Rw9EJefhQGP7M=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250922171735-9219d122eba9/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -1029,8 +1020,8 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4=
-google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM=
+google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI=
+google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -1041,14 +1032,13 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
-google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A=
-google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
+google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
+google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
-gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
@@ -1075,8 +1065,8 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
-storj.io/common v0.0.0-20250808122759-804533d519c1 h1:z7ZjU+TlPZ2Lq2S12hT6+Fr7jFsBxPMrPBH4zZpZuUA=
-storj.io/common v0.0.0-20250808122759-804533d519c1/go.mod h1:YNr7/ty6CmtpG5C9lEPtPXK3hOymZpueCb9QCNuPMUY=
+storj.io/common v0.0.0-20250918032746-784a656bec7e h1:wBeNT7CA1Qwnm8jGP+mKp/IW12vhytCGjVSCKeEF6xM=
+storj.io/common v0.0.0-20250918032746-784a656bec7e/go.mod h1:YNr7/ty6CmtpG5C9lEPtPXK3hOymZpueCb9QCNuPMUY=
storj.io/drpc v0.0.35-0.20250513201419-f7819ea69b55 h1:8OE12DvUnB9lfZcHe7IDGsuhjrY9GBAr964PVHmhsro=
storj.io/drpc v0.0.35-0.20250513201419-f7819ea69b55/go.mod h1:Y9LZaa8esL1PW2IDMqJE7CFSNq7d5bQ3RI7mGPtmKMg=
storj.io/eventkit v0.0.0-20250410172343-61f26d3de156 h1:5MZ0CyMbG6Pi0rRzUWVG6dvpXjbBYEX2oyXuj+tT+sk=
diff --git a/lib/atexit/atexit.go b/lib/atexit/atexit.go
index 07af75ebc..f15abe5f5 100644
--- a/lib/atexit/atexit.go
+++ b/lib/atexit/atexit.go
@@ -1,4 +1,4 @@
-// Package atexit provides handling for functions you want called when
+// Package atexit provides handling for functions you want called when
// the program exits unexpectedly due to a signal.
//
// You should also make sure you call Run in the normal exit path.
diff --git a/lib/atexit/atexit_other.go b/lib/atexit/atexit_other.go
index 46b9088f2..f98ee9fa1 100644
--- a/lib/atexit/atexit_other.go
+++ b/lib/atexit/atexit_other.go
@@ -1,4 +1,4 @@
-//go:build windows || plan9
+//go:build windows || plan9
package atexit
diff --git a/lib/atexit/atexit_test.go b/lib/atexit/atexit_test.go
index 6e0315ffe..977cb8b21 100644
--- a/lib/atexit/atexit_test.go
+++ b/lib/atexit/atexit_test.go
@@ -1,4 +1,4 @@
-package atexit
+package atexit
import (
"os"
diff --git a/lib/atexit/atexit_unix.go b/lib/atexit/atexit_unix.go
index 9c50171b3..3b16cf55c 100644
--- a/lib/atexit/atexit_unix.go
+++ b/lib/atexit/atexit_unix.go
@@ -1,4 +1,4 @@
-//go:build !windows && !plan9
+//go:build !windows && !plan9
package atexit
diff --git a/lib/batcher/batcher.go b/lib/batcher/batcher.go
index 1ac7f589d..603bc881a 100644
--- a/lib/batcher/batcher.go
+++ b/lib/batcher/batcher.go
@@ -1,4 +1,4 @@
-// Package batcher implements a generic batcher.
+// Package batcher implements a generic batcher.
//
// It uses two types:
//
diff --git a/lib/batcher/batcher_test.go b/lib/batcher/batcher_test.go
index 3391bd81b..a8b535632 100644
--- a/lib/batcher/batcher_test.go
+++ b/lib/batcher/batcher_test.go
@@ -1,4 +1,4 @@
-package batcher
+package batcher
import (
"context"
diff --git a/lib/batcher/options.go b/lib/batcher/options.go
index b5c34b12f..757301819 100644
--- a/lib/batcher/options.go
+++ b/lib/batcher/options.go
@@ -1,4 +1,4 @@
-package batcher
+package batcher
import (
"fmt"
diff --git a/lib/bucket/bucket.go b/lib/bucket/bucket.go
index 4fffb4d79..8a92f153f 100644
--- a/lib/bucket/bucket.go
+++ b/lib/bucket/bucket.go
@@ -1,4 +1,4 @@
-// Package bucket is contains utilities for managing bucket-based backends
+// Package bucket is contains utilities for managing bucket-based backends
package bucket
import (
diff --git a/lib/bucket/bucket_test.go b/lib/bucket/bucket_test.go
index 3dab9e846..b7e83dacb 100644
--- a/lib/bucket/bucket_test.go
+++ b/lib/bucket/bucket_test.go
@@ -1,4 +1,4 @@
-package bucket
+package bucket
import (
"errors"
diff --git a/lib/buildinfo/arch.go b/lib/buildinfo/arch.go
index cf5f2d69a..fe9915062 100644
--- a/lib/buildinfo/arch.go
+++ b/lib/buildinfo/arch.go
@@ -1,4 +1,4 @@
-package buildinfo
+package buildinfo
import (
"runtime"
diff --git a/lib/buildinfo/cgo.go b/lib/buildinfo/cgo.go
index b30d86380..5ee64e108 100644
--- a/lib/buildinfo/cgo.go
+++ b/lib/buildinfo/cgo.go
@@ -1,4 +1,4 @@
-//go:build cgo
+//go:build cgo
package buildinfo
diff --git a/lib/buildinfo/osversion.go b/lib/buildinfo/osversion.go
index f04e56b4c..1909249d1 100644
--- a/lib/buildinfo/osversion.go
+++ b/lib/buildinfo/osversion.go
@@ -1,4 +1,4 @@
-//go:build !windows
+//go:build !windows
package buildinfo
diff --git a/lib/buildinfo/osversion_windows.go b/lib/buildinfo/osversion_windows.go
index 4fcb52457..050ca647b 100644
--- a/lib/buildinfo/osversion_windows.go
+++ b/lib/buildinfo/osversion_windows.go
@@ -1,4 +1,4 @@
-package buildinfo
+package buildinfo
import (
"regexp"
diff --git a/lib/buildinfo/snap.go b/lib/buildinfo/snap.go
index 64270f15e..c3948d099 100644
--- a/lib/buildinfo/snap.go
+++ b/lib/buildinfo/snap.go
@@ -1,4 +1,4 @@
-//go:build snap
+//go:build snap
package buildinfo
diff --git a/lib/buildinfo/tags.go b/lib/buildinfo/tags.go
index 945a1e445..de0fa4305 100644
--- a/lib/buildinfo/tags.go
+++ b/lib/buildinfo/tags.go
@@ -1,4 +1,4 @@
-// Package buildinfo provides build information.
+// Package buildinfo provides build information.
package buildinfo
import (
diff --git a/lib/cache/cache.go b/lib/cache/cache.go
index 5ef82a046..d40242461 100644
--- a/lib/cache/cache.go
+++ b/lib/cache/cache.go
@@ -1,4 +1,4 @@
-// Package cache implements a simple cache where the entries are
+// Package cache implements a simple cache where the entries are
// expired after a given time (5 minutes of disuse by default).
package cache
diff --git a/lib/cache/cache_test.go b/lib/cache/cache_test.go
index 7bf9322e0..0b435e079 100644
--- a/lib/cache/cache_test.go
+++ b/lib/cache/cache_test.go
@@ -1,4 +1,4 @@
-package cache
+package cache
import (
"errors"
diff --git a/lib/daemonize/daemon_other.go b/lib/daemonize/daemon_other.go
index a7d663d0e..c5657b968 100644
--- a/lib/daemonize/daemon_other.go
+++ b/lib/daemonize/daemon_other.go
@@ -1,4 +1,4 @@
-//go:build !unix
+//go:build !unix
// Package daemonize provides daemonization stub for non-Unix platforms.
package daemonize
diff --git a/lib/daemonize/daemon_unix.go b/lib/daemonize/daemon_unix.go
index 79fb96b2b..2ba0bd7dc 100644
--- a/lib/daemonize/daemon_unix.go
+++ b/lib/daemonize/daemon_unix.go
@@ -1,4 +1,4 @@
-//go:build unix
+//go:build unix
// Package daemonize provides daemonization interface for Unix platforms.
package daemonize
diff --git a/lib/debug/common.go b/lib/debug/common.go
index 14ea9ddc2..65888a0e4 100644
--- a/lib/debug/common.go
+++ b/lib/debug/common.go
@@ -1,4 +1,4 @@
-// Package debug contains functions for dealing with runtime/debug functions across go versions
+// Package debug contains functions for dealing with runtime/debug functions across go versions
package debug
import (
diff --git a/lib/dircache/dircache.go b/lib/dircache/dircache.go
index 7fdf9e1af..5174b14ea 100644
--- a/lib/dircache/dircache.go
+++ b/lib/dircache/dircache.go
@@ -1,4 +1,4 @@
-// Package dircache provides a simple cache for caching directory ID
+// Package dircache provides a simple cache for caching directory ID
// to path lookups and the inverse.
package dircache
diff --git a/lib/diskusage/diskusage.go b/lib/diskusage/diskusage.go
index a2caa93ae..0ce50f402 100644
--- a/lib/diskusage/diskusage.go
+++ b/lib/diskusage/diskusage.go
@@ -1,4 +1,4 @@
-// Package diskusage provides a cross platform version of the statfs
+// Package diskusage provides a cross platform version of the statfs
// system call to read disk space usage.
package diskusage
diff --git a/lib/diskusage/diskusage_netbsd.go b/lib/diskusage/diskusage_netbsd.go
index 973bd1d86..09bdf7e4a 100644
--- a/lib/diskusage/diskusage_netbsd.go
+++ b/lib/diskusage/diskusage_netbsd.go
@@ -1,4 +1,4 @@
-//go:build netbsd
+//go:build netbsd
package diskusage
diff --git a/lib/diskusage/diskusage_openbsd.go b/lib/diskusage/diskusage_openbsd.go
index ff0e1f7a4..cf6dd5acc 100644
--- a/lib/diskusage/diskusage_openbsd.go
+++ b/lib/diskusage/diskusage_openbsd.go
@@ -1,4 +1,4 @@
-//go:build openbsd
+//go:build openbsd
package diskusage
diff --git a/lib/diskusage/diskusage_test.go b/lib/diskusage/diskusage_test.go
index f0263bfb5..0da7966b3 100644
--- a/lib/diskusage/diskusage_test.go
+++ b/lib/diskusage/diskusage_test.go
@@ -1,4 +1,4 @@
-package diskusage
+package diskusage
import (
"testing"
diff --git a/lib/diskusage/diskusage_unix.go b/lib/diskusage/diskusage_unix.go
index 822cd96f7..0ea5d39a8 100644
--- a/lib/diskusage/diskusage_unix.go
+++ b/lib/diskusage/diskusage_unix.go
@@ -1,4 +1,4 @@
-//go:build aix || android || darwin || dragonfly || freebsd || ios || linux
+//go:build aix || android || darwin || dragonfly || freebsd || ios || linux
package diskusage
diff --git a/lib/diskusage/diskusage_unsupported.go b/lib/diskusage/diskusage_unsupported.go
index 1e4bddc40..1befdae48 100644
--- a/lib/diskusage/diskusage_unsupported.go
+++ b/lib/diskusage/diskusage_unsupported.go
@@ -1,4 +1,4 @@
-//go:build illumos || js || plan9 || solaris
+//go:build illumos || js || plan9 || solaris
package diskusage
diff --git a/lib/diskusage/diskusage_windows.go b/lib/diskusage/diskusage_windows.go
index e7a957c5d..8643b4cf5 100644
--- a/lib/diskusage/diskusage_windows.go
+++ b/lib/diskusage/diskusage_windows.go
@@ -1,4 +1,4 @@
-//go:build windows
+//go:build windows
package diskusage
diff --git a/lib/encoder/encoder.go b/lib/encoder/encoder.go
index c8f41d76c..ce533395d 100644
--- a/lib/encoder/encoder.go
+++ b/lib/encoder/encoder.go
@@ -1,4 +1,4 @@
-// Package encoder provides functionality to translate file names
+// Package encoder provides functionality to translate file names
// for usage on restrictive storage systems.
//
// The restricted set of characters are mapped to a unicode equivalent version
@@ -184,8 +184,8 @@ func (mask MultiEncoder) String() string {
// Set converts a string into a MultiEncoder
func (mask *MultiEncoder) Set(in string) error {
var out MultiEncoder
- parts := strings.Split(in, ",")
- for _, part := range parts {
+ parts := strings.SplitSeq(in, ",")
+ for part := range parts {
part = strings.TrimSpace(part)
if bits, ok := nameToEncoding[part]; ok {
out |= bits
diff --git a/lib/encoder/encoder_cases_test.go b/lib/encoder/encoder_cases_test.go
index 69631db63..9e255cedc 100644
--- a/lib/encoder/encoder_cases_test.go
+++ b/lib/encoder/encoder_cases_test.go
@@ -1,4 +1,4 @@
-// Code generated by ./internal/gen/main.go. DO NOT EDIT.
+// Code generated by ./internal/gen/main.go. DO NOT EDIT.
//go:generate go run ./internal/gen/main.go
diff --git a/lib/encoder/encoder_test.go b/lib/encoder/encoder_test.go
index b36339824..ad5d6a301 100644
--- a/lib/encoder/encoder_test.go
+++ b/lib/encoder/encoder_test.go
@@ -1,4 +1,4 @@
-package encoder
+package encoder
import (
"fmt"
diff --git a/lib/encoder/filename/decode.go b/lib/encoder/filename/decode.go
index 6e859fd31..2fc461a79 100644
--- a/lib/encoder/filename/decode.go
+++ b/lib/encoder/filename/decode.go
@@ -1,4 +1,4 @@
-package filename
+package filename
import (
"bytes"
diff --git a/lib/encoder/filename/decode_test.go b/lib/encoder/filename/decode_test.go
index f2a3f0609..3ce469759 100644
--- a/lib/encoder/filename/decode_test.go
+++ b/lib/encoder/filename/decode_test.go
@@ -1,4 +1,4 @@
-package filename
+package filename
import (
"testing"
diff --git a/lib/encoder/filename/encode.go b/lib/encoder/filename/encode.go
index 031dbd07a..4080c4a6a 100644
--- a/lib/encoder/filename/encode.go
+++ b/lib/encoder/filename/encode.go
@@ -1,4 +1,4 @@
-package filename
+package filename
import (
"encoding/base64"
diff --git a/lib/encoder/filename/fuzz.go b/lib/encoder/filename/fuzz.go
index 63fb94e29..4991e0d53 100644
--- a/lib/encoder/filename/fuzz.go
+++ b/lib/encoder/filename/fuzz.go
@@ -1,4 +1,4 @@
-//go:build gofuzz
+//go:build gofuzz
package filename
diff --git a/lib/encoder/filename/gentable.go b/lib/encoder/filename/gentable.go
index 2d45b6e8c..a05952c52 100644
--- a/lib/encoder/filename/gentable.go
+++ b/lib/encoder/filename/gentable.go
@@ -1,4 +1,4 @@
-//go:build ignore
+//go:build ignore
package main
diff --git a/lib/encoder/filename/init.go b/lib/encoder/filename/init.go
index ea206cb9e..a4bf4c91c 100644
--- a/lib/encoder/filename/init.go
+++ b/lib/encoder/filename/init.go
@@ -1,4 +1,4 @@
-// Package filename provides utilities for encoder.
+// Package filename provides utilities for encoder.
package filename
import (
diff --git a/lib/encoder/internal/gen/main.go b/lib/encoder/internal/gen/main.go
index 1812091e9..6182aa801 100644
--- a/lib/encoder/internal/gen/main.go
+++ b/lib/encoder/internal/gen/main.go
@@ -1,4 +1,4 @@
-// Package main provides utilities for encoder.
+// Package main provides utilities for encoder.
package main
import (
diff --git a/lib/encoder/os_darwin.go b/lib/encoder/os_darwin.go
index 053afc3a1..24b254afa 100644
--- a/lib/encoder/os_darwin.go
+++ b/lib/encoder/os_darwin.go
@@ -1,4 +1,4 @@
-//go:build darwin
+//go:build darwin
package encoder
diff --git a/lib/encoder/os_other.go b/lib/encoder/os_other.go
index 34279d7ca..96c8a1f49 100644
--- a/lib/encoder/os_other.go
+++ b/lib/encoder/os_other.go
@@ -1,4 +1,4 @@
-//go:build !windows && !darwin
+//go:build !windows && !darwin
package encoder
diff --git a/lib/encoder/os_windows.go b/lib/encoder/os_windows.go
index 1c450dc1c..564bebe91 100644
--- a/lib/encoder/os_windows.go
+++ b/lib/encoder/os_windows.go
@@ -1,4 +1,4 @@
-//go:build windows
+//go:build windows
package encoder
diff --git a/lib/encoder/standard.go b/lib/encoder/standard.go
index 94180de1a..764edaf75 100644
--- a/lib/encoder/standard.go
+++ b/lib/encoder/standard.go
@@ -1,4 +1,4 @@
-package encoder
+package encoder
// Standard defines the encoding that is used for paths in- and output by rclone.
//
diff --git a/lib/env/env.go b/lib/env/env.go
index b8feb2f73..037a7d232 100644
--- a/lib/env/env.go
+++ b/lib/env/env.go
@@ -1,4 +1,4 @@
-// Package env contains functions for dealing with environment variables
+// Package env contains functions for dealing with environment variables
package env
import (
diff --git a/lib/env/env_test.go b/lib/env/env_test.go
index 9f601542a..018499c93 100644
--- a/lib/env/env_test.go
+++ b/lib/env/env_test.go
@@ -1,4 +1,4 @@
-package env
+package env
import (
"os"
diff --git a/lib/errcount/errcount.go b/lib/errcount/errcount.go
index a22c8da81..baadde317 100644
--- a/lib/errcount/errcount.go
+++ b/lib/errcount/errcount.go
@@ -1,4 +1,4 @@
-// Package errcount provides an easy to use error counter which
+// Package errcount provides an easy to use error counter which
// returns error count and last error so as to not overwhelm the user
// with errors.
package errcount
diff --git a/lib/errcount/errcount_test.go b/lib/errcount/errcount_test.go
index c45f897be..e0cb3734a 100644
--- a/lib/errcount/errcount_test.go
+++ b/lib/errcount/errcount_test.go
@@ -1,4 +1,4 @@
-package errcount
+package errcount
import (
"errors"
diff --git a/lib/errors/errors.go b/lib/errors/errors.go
index ea8d70318..d01788d65 100644
--- a/lib/errors/errors.go
+++ b/lib/errors/errors.go
@@ -1,4 +1,4 @@
-// Package errors provides error handling utilities.
+// Package errors provides error handling utilities.
package errors
import (
diff --git a/lib/errors/errors_test.go b/lib/errors/errors_test.go
index c04b6d669..ceaa96756 100644
--- a/lib/errors/errors_test.go
+++ b/lib/errors/errors_test.go
@@ -1,4 +1,4 @@
-package errors
+package errors
import (
"errors"
diff --git a/lib/exitcode/exitcode.go b/lib/exitcode/exitcode.go
index afe886197..12a3b3b7f 100644
--- a/lib/exitcode/exitcode.go
+++ b/lib/exitcode/exitcode.go
@@ -1,4 +1,4 @@
-// Package exitcode exports rclone's exit status numbers.
+// Package exitcode exports rclone's exit status numbers.
package exitcode
const (
diff --git a/lib/file/driveletter_other.go b/lib/file/driveletter_other.go
index 1ae5284a6..e223c22f3 100644
--- a/lib/file/driveletter_other.go
+++ b/lib/file/driveletter_other.go
@@ -1,4 +1,4 @@
-//go:build !windows
+//go:build !windows
package file
diff --git a/lib/file/driveletter_windows.go b/lib/file/driveletter_windows.go
index ad71ad3c2..07dd77209 100644
--- a/lib/file/driveletter_windows.go
+++ b/lib/file/driveletter_windows.go
@@ -1,4 +1,4 @@
-//go:build windows
+//go:build windows
package file
diff --git a/lib/file/file.go b/lib/file/file.go
index a82c144e8..e1e70cfe9 100644
--- a/lib/file/file.go
+++ b/lib/file/file.go
@@ -1,4 +1,4 @@
-// Package file provides a version of os.OpenFile, the handles of
+// Package file provides a version of os.OpenFile, the handles of
// which can be renamed and deleted under Windows.
package file
diff --git a/lib/file/file_other.go b/lib/file/file_other.go
index d97a4250b..ab331d867 100644
--- a/lib/file/file_other.go
+++ b/lib/file/file_other.go
@@ -1,4 +1,4 @@
-//go:build !windows
+//go:build !windows
package file
diff --git a/lib/file/file_test.go b/lib/file/file_test.go
index eab7bf7c1..fdfcaa62f 100644
--- a/lib/file/file_test.go
+++ b/lib/file/file_test.go
@@ -1,4 +1,4 @@
-package file
+package file
import (
"fmt"
diff --git a/lib/file/file_windows.go b/lib/file/file_windows.go
index cefd4f2f6..cb537a139 100644
--- a/lib/file/file_windows.go
+++ b/lib/file/file_windows.go
@@ -1,4 +1,4 @@
-//go:build windows
+//go:build windows
package file
diff --git a/lib/file/mkdir.go b/lib/file/mkdir.go
index 0a2b350d7..3abfc4ad8 100644
--- a/lib/file/mkdir.go
+++ b/lib/file/mkdir.go
@@ -1,4 +1,4 @@
-package file
+package file
import "os"
diff --git a/lib/file/preallocate.go b/lib/file/preallocate.go
index a87c26b20..dfe6d4fc0 100644
--- a/lib/file/preallocate.go
+++ b/lib/file/preallocate.go
@@ -1,4 +1,4 @@
-package file
+package file
import "errors"
diff --git a/lib/file/preallocate_other.go b/lib/file/preallocate_other.go
index f9620cad1..c6d62d04a 100644
--- a/lib/file/preallocate_other.go
+++ b/lib/file/preallocate_other.go
@@ -1,4 +1,4 @@
-//go:build !windows && !linux
+//go:build !windows && !linux
package file
diff --git a/lib/file/preallocate_unix.go b/lib/file/preallocate_unix.go
index 9af350576..1b0c220f2 100644
--- a/lib/file/preallocate_unix.go
+++ b/lib/file/preallocate_unix.go
@@ -1,4 +1,4 @@
-//go:build linux
+//go:build linux
package file
diff --git a/lib/file/preallocate_windows.go b/lib/file/preallocate_windows.go
index 928276c1b..252008b57 100644
--- a/lib/file/preallocate_windows.go
+++ b/lib/file/preallocate_windows.go
@@ -1,4 +1,4 @@
-//go:build windows
+//go:build windows
package file
diff --git a/lib/file/unc.go b/lib/file/unc.go
index 9832f7ab5..056ffbe60 100644
--- a/lib/file/unc.go
+++ b/lib/file/unc.go
@@ -1,4 +1,4 @@
-//go:build !windows
+//go:build !windows
package file
diff --git a/lib/file/unc_test.go b/lib/file/unc_test.go
index bf82ddba7..04a0a68a5 100644
--- a/lib/file/unc_test.go
+++ b/lib/file/unc_test.go
@@ -1,4 +1,4 @@
-//go:build windows
+//go:build windows
package file
diff --git a/lib/file/unc_windows.go b/lib/file/unc_windows.go
index 5038b2198..c222e4446 100644
--- a/lib/file/unc_windows.go
+++ b/lib/file/unc_windows.go
@@ -1,4 +1,4 @@
-//go:build windows
+//go:build windows
package file
diff --git a/lib/http/auth.go b/lib/http/auth.go
index 6fead3827..c83511f5d 100644
--- a/lib/http/auth.go
+++ b/lib/http/auth.go
@@ -1,4 +1,4 @@
-package http
+package http
import (
"bytes"
@@ -20,7 +20,7 @@ You can either use an htpasswd file which can take lots of users, or
set a single username and password with the ` + "`--{{ .Prefix }}user` and `--{{ .Prefix }}pass`" + ` flags.
Alternatively, you can have the reverse proxy manage authentication and use the
-username provided in the configured header with ` + "`--user-from-header`" + ` (e.g., ` + "`--{{ .Prefix }}--user-from-header=x-remote-user`" + `).
+username provided in the configured header with ` + "`--user-from-header`" + ` (e.g., ` + "`--{{ .Prefix }}user-from-header=x-remote-user`" + `).
Ensure the proxy is trusted and headers cannot be spoofed, as misconfiguration
may lead to unauthorized access.
diff --git a/lib/http/auth_test.go b/lib/http/auth_test.go
index dec115a12..b5b333feb 100644
--- a/lib/http/auth_test.go
+++ b/lib/http/auth_test.go
@@ -1,4 +1,4 @@
-package http
+package http
import (
"strings"
diff --git a/lib/http/context.go b/lib/http/context.go
index e2f56132f..d725863e0 100644
--- a/lib/http/context.go
+++ b/lib/http/context.go
@@ -1,4 +1,4 @@
-package http
+package http
import (
"context"
diff --git a/lib/http/middleware.go b/lib/http/middleware.go
index d2cb36db1..1cad66ee8 100644
--- a/lib/http/middleware.go
+++ b/lib/http/middleware.go
@@ -1,4 +1,4 @@
-package http
+package http
import (
"context"
diff --git a/lib/http/middleware_test.go b/lib/http/middleware_test.go
index d1f79b725..5340943e5 100644
--- a/lib/http/middleware_test.go
+++ b/lib/http/middleware_test.go
@@ -1,4 +1,4 @@
-package http
+package http
import (
"context"
diff --git a/lib/http/serve/dir.go b/lib/http/serve/dir.go
index ae1ead6a0..44ca90dcd 100644
--- a/lib/http/serve/dir.go
+++ b/lib/http/serve/dir.go
@@ -1,4 +1,4 @@
-package serve
+package serve
import (
"bytes"
@@ -21,6 +21,7 @@ import (
type DirEntry struct {
remote string
URL string
+ ZipURL string
Leaf string
IsDir bool
Size int64
@@ -32,6 +33,8 @@ type Directory struct {
DirRemote string
Title string
Name string
+ ZipURL string
+ DisableZip bool
Entries []DirEntry
Query string
HTMLTemplate *template.Template
@@ -70,6 +73,7 @@ func NewDirectory(dirRemote string, htmlTemplate *template.Template) *Directory
DirRemote: dirRemote,
Title: fmt.Sprintf("Directory listing of /%s", dirRemote),
Name: fmt.Sprintf("/%s", dirRemote),
+ ZipURL: "?download=zip",
HTMLTemplate: htmlTemplate,
Breadcrumb: breadcrumb,
}
@@ -99,11 +103,15 @@ func (d *Directory) AddHTMLEntry(remote string, isDir bool, size int64, modTime
d.Entries = append(d.Entries, DirEntry{
remote: remote,
URL: rest.URLPathEscape(urlRemote) + d.Query,
+ ZipURL: "",
Leaf: leaf,
IsDir: isDir,
Size: size,
ModTime: modTime,
})
+ if isDir {
+ d.Entries[len(d.Entries)-1].ZipURL = rest.URLPathEscape(urlRemote) + "?download=zip"
+ }
}
// AddEntry adds an entry to that directory
diff --git a/lib/http/serve/dir_test.go b/lib/http/serve/dir_test.go
index 19ab22f57..ec42be70e 100644
--- a/lib/http/serve/dir_test.go
+++ b/lib/http/serve/dir_test.go
@@ -1,4 +1,4 @@
-package serve
+package serve
import (
"context"
@@ -46,11 +46,11 @@ func TestAddHTMLEntry(t *testing.T) {
d.AddHTMLEntry("a/b/c/colon:colon.txt", false, 64, modtime)
d.AddHTMLEntry("\"quotes\".txt", false, 64, modtime)
assert.Equal(t, []DirEntry{
- {remote: "", URL: "/", Leaf: "/", IsDir: true, Size: 0, ModTime: modtime},
- {remote: "dir", URL: "dir/", Leaf: "dir/", IsDir: true, Size: 0, ModTime: modtime},
- {remote: "a/b/c/d.txt", URL: "d.txt", Leaf: "d.txt", IsDir: false, Size: 64, ModTime: modtime},
- {remote: "a/b/c/colon:colon.txt", URL: "./colon:colon.txt", Leaf: "colon:colon.txt", IsDir: false, Size: 64, ModTime: modtime},
- {remote: "\"quotes\".txt", URL: "%22quotes%22.txt", Leaf: "\"quotes\".txt", Size: 64, IsDir: false, ModTime: modtime},
+ {remote: "", URL: "/", ZipURL: "/?download=zip", Leaf: "/", IsDir: true, Size: 0, ModTime: modtime},
+ {remote: "dir", URL: "dir/", ZipURL: "dir/?download=zip", Leaf: "dir/", IsDir: true, Size: 0, ModTime: modtime},
+ {remote: "a/b/c/d.txt", URL: "d.txt", ZipURL: "", Leaf: "d.txt", IsDir: false, Size: 64, ModTime: modtime},
+ {remote: "a/b/c/colon:colon.txt", URL: "./colon:colon.txt", ZipURL: "", Leaf: "colon:colon.txt", IsDir: false, Size: 64, ModTime: modtime},
+ {remote: "\"quotes\".txt", URL: "%22quotes%22.txt", ZipURL: "", Leaf: "\"quotes\".txt", Size: 64, IsDir: false, ModTime: modtime},
}, d.Entries)
// Now test with a query parameter
@@ -58,8 +58,8 @@ func TestAddHTMLEntry(t *testing.T) {
d.AddHTMLEntry("file", false, 64, modtime)
d.AddHTMLEntry("dir", true, 0, modtime)
assert.Equal(t, []DirEntry{
- {remote: "file", URL: "file?potato=42", Leaf: "file", IsDir: false, Size: 64, ModTime: modtime},
- {remote: "dir", URL: "dir/?potato=42", Leaf: "dir/", IsDir: true, Size: 0, ModTime: modtime},
+ {remote: "file", URL: "file?potato=42", ZipURL: "", Leaf: "file", IsDir: false, Size: 64, ModTime: modtime},
+ {remote: "dir", URL: "dir/?potato=42", ZipURL: "dir/?download=zip", Leaf: "dir/", IsDir: true, Size: 0, ModTime: modtime},
}, d.Entries)
}
diff --git a/lib/http/serve/serve.go b/lib/http/serve/serve.go
index 21f3c3322..c68b818ec 100644
--- a/lib/http/serve/serve.go
+++ b/lib/http/serve/serve.go
@@ -1,4 +1,4 @@
-// Package serve deals with serving objects over HTTP
+// Package serve deals with serving objects over HTTP
package serve
import (
diff --git a/lib/http/serve/serve_test.go b/lib/http/serve/serve_test.go
index 79daf773e..ce192cac1 100644
--- a/lib/http/serve/serve_test.go
+++ b/lib/http/serve/serve_test.go
@@ -1,4 +1,4 @@
-package serve
+package serve
import (
"context"
diff --git a/lib/http/server.go b/lib/http/server.go
index 46531351d..7a86fc8f8 100644
--- a/lib/http/server.go
+++ b/lib/http/server.go
@@ -1,4 +1,4 @@
-// Package http provides a registration interface for http services
+// Package http provides a registration interface for http services
package http
import (
@@ -59,6 +59,8 @@ inserts leading and trailing "/" on ` + "`--{{ .Prefix }}baseurl`" + `, so ` + "
` + "`--{{ .Prefix }}baseurl \"/rclone\"` and `--{{ .Prefix }}baseurl \"/rclone/\"`" + ` are all treated
identically.
+` + "`--{{ .Prefix }}disable-zip`" + ` may be set to disable the zipping download option.
+
#### TLS (SSL)
By default this will serve over http. If you want you can serve over
@@ -523,8 +525,6 @@ func (s *Server) initTLS() error {
func (s *Server) Serve() {
s.wg.Add(len(s.instances))
for _, ii := range s.instances {
- // TODO: decide how/when to log listening url
- // log.Printf("listening on %s", ii.url)
go ii.serve(&s.wg)
}
// Install an atexit handler to shutdown gracefully
diff --git a/lib/http/server_test.go b/lib/http/server_test.go
index 22e49580b..f38de5ad3 100644
--- a/lib/http/server_test.go
+++ b/lib/http/server_test.go
@@ -1,4 +1,4 @@
-package http
+package http
import (
"context"
diff --git a/lib/http/template.go b/lib/http/template.go
index 584e69c3d..23b82cbb8 100644
--- a/lib/http/template.go
+++ b/lib/http/template.go
@@ -1,4 +1,4 @@
-package http
+package http
import (
"bytes"
diff --git a/lib/http/template_test.go b/lib/http/template_test.go
index c6d787330..56d9ed303 100644
--- a/lib/http/template_test.go
+++ b/lib/http/template_test.go
@@ -1,4 +1,4 @@
-package http
+package http
import (
"strings"
diff --git a/lib/http/templates/index.html b/lib/http/templates/index.html
index 348050c02..cec58f1d7 100644
--- a/lib/http/templates/index.html
+++ b/lib/http/templates/index.html
@@ -21,7 +21,7 @@
-
@@ -206,6 +219,9 @@
+
+
+
@@ -233,6 +249,15 @@
{{range $i, $crumb := .Breadcrumb}}{{html $crumb.Text}}{{if ne $i 0}}/{{end}}{{end}}
+
+ {{- if not .DisableZip}}
+
+
+
+ {{- end}}
+
@@ -283,6 +308,13 @@
{{- end}}
{{html .Leaf}}
+ {{- if and .IsDir (not $.DisableZip)}}
+
+
+
+ {{- end}}
{{- if .IsDir}}
—
diff --git a/lib/israce/israce.go b/lib/israce/israce.go
index 450785e07..48682b16f 100644
--- a/lib/israce/israce.go
+++ b/lib/israce/israce.go
@@ -1,4 +1,4 @@
-//go:build race
+//go:build race
// Package israce reports if the Go race detector is enabled.
//
diff --git a/lib/israce/norace.go b/lib/israce/norace.go
index 556268fe0..047591a89 100644
--- a/lib/israce/norace.go
+++ b/lib/israce/norace.go
@@ -1,4 +1,4 @@
-//go:build !race
+//go:build !race
// Package israce reports if the Go race detector is enabled.
//
diff --git a/lib/jwtutil/jwtutil.go b/lib/jwtutil/jwtutil.go
index 3bf7e6081..570839ec0 100644
--- a/lib/jwtutil/jwtutil.go
+++ b/lib/jwtutil/jwtutil.go
@@ -1,4 +1,4 @@
-// Package jwtutil provides JWT utilities.
+// Package jwtutil provides JWT utilities.
package jwtutil
import (
diff --git a/lib/kv/bolt.go b/lib/kv/bolt.go
index d6ccb7aaf..cd364ae22 100644
--- a/lib/kv/bolt.go
+++ b/lib/kv/bolt.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !js
+//go:build !plan9 && !js
// Package kv provides key/value database.
package kv
diff --git a/lib/kv/internal_test.go b/lib/kv/internal_test.go
index f101f720e..1a2f9514f 100644
--- a/lib/kv/internal_test.go
+++ b/lib/kv/internal_test.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !js
+//go:build !plan9 && !js
package kv
diff --git a/lib/kv/types.go b/lib/kv/types.go
index 601dd21da..ce9236725 100644
--- a/lib/kv/types.go
+++ b/lib/kv/types.go
@@ -1,4 +1,4 @@
-package kv
+package kv
import (
"context"
diff --git a/lib/kv/unsupported.go b/lib/kv/unsupported.go
index c4bb0deff..8a85d6f17 100644
--- a/lib/kv/unsupported.go
+++ b/lib/kv/unsupported.go
@@ -1,4 +1,4 @@
-//go:build plan9 || js
+//go:build plan9 || js
// Package kv provides key/value database.
package kv
diff --git a/lib/mmap/mmap.go b/lib/mmap/mmap.go
index 4f323c938..8bddd3f4a 100644
--- a/lib/mmap/mmap.go
+++ b/lib/mmap/mmap.go
@@ -1,4 +1,4 @@
-// Package mmap provides memory mapped related utilities.
+// Package mmap provides memory mapped related utilities.
package mmap
import "os"
diff --git a/lib/mmap/mmap_test.go b/lib/mmap/mmap_test.go
index 0fb0b797f..5c02e7818 100644
--- a/lib/mmap/mmap_test.go
+++ b/lib/mmap/mmap_test.go
@@ -1,4 +1,4 @@
-package mmap
+package mmap
import (
"fmt"
@@ -31,7 +31,7 @@ func BenchmarkAllocFree(b *testing.B) {
for _, dirty := range []bool{false, true} {
for size := 4096; size <= 32*1024*1024; size *= 2 {
b.Run(fmt.Sprintf("%dk,dirty=%v", size>>10, dirty), func(b *testing.B) {
- for i := 0; i < b.N; i++ {
+ for b.Loop() {
mem := MustAlloc(size)
if dirty {
mem[0] ^= 0xFF
@@ -62,7 +62,7 @@ func BenchmarkAllocFreeWithLotsOfAllocations(b *testing.B) {
for preAllocs := 1; preAllocs <= maxAllocs; preAllocs *= 2 {
allocs := alloc(preAllocs)
b.Run(fmt.Sprintf("%d", preAllocs), func(b *testing.B) {
- for i := 0; i < b.N; i++ {
+ for b.Loop() {
mem := MustAlloc(size)
mem[0] ^= 0xFF
MustFree(mem)
@@ -90,7 +90,7 @@ func BenchmarkAllocFreeForLotsOfAllocations(b *testing.B) {
}
for preAllocs := 1; preAllocs <= maxAllocs; preAllocs *= 2 {
b.Run(fmt.Sprintf("%d", preAllocs), func(b *testing.B) {
- for i := 0; i < b.N; i++ {
+ for b.Loop() {
allocs := alloc(preAllocs)
free(allocs)
}
diff --git a/lib/mmap/mmap_unix.go b/lib/mmap/mmap_unix.go
index 8bc9e2e22..960dd2b6c 100644
--- a/lib/mmap/mmap_unix.go
+++ b/lib/mmap/mmap_unix.go
@@ -1,4 +1,4 @@
-// Package mmap implements a large block memory allocator using
+// Package mmap implements a large block memory allocator using
// anonymous memory maps.
//go:build !plan9 && !windows && !js
diff --git a/lib/mmap/mmap_unsupported.go b/lib/mmap/mmap_unsupported.go
index e78817da7..37e635849 100644
--- a/lib/mmap/mmap_unsupported.go
+++ b/lib/mmap/mmap_unsupported.go
@@ -1,4 +1,4 @@
-// Fallback Alloc and Free for unsupported OSes
+// Fallback Alloc and Free for unsupported OSes
//go:build plan9 || js
diff --git a/lib/mmap/mmap_windows.go b/lib/mmap/mmap_windows.go
index b64ba41f1..8c052281a 100644
--- a/lib/mmap/mmap_windows.go
+++ b/lib/mmap/mmap_windows.go
@@ -1,4 +1,4 @@
-// Package mmap implements a large block memory allocator using
+// Package mmap implements a large block memory allocator using
// anonymous memory maps.
//go:build windows
diff --git a/lib/multipart/multipart.go b/lib/multipart/multipart.go
index b8d0a6a84..c526dbd60 100644
--- a/lib/multipart/multipart.go
+++ b/lib/multipart/multipart.go
@@ -1,4 +1,4 @@
-// Package multipart implements generic multipart uploading.
+// Package multipart implements generic multipart uploading.
package multipart
import (
diff --git a/lib/oauthutil/oauthutil.go b/lib/oauthutil/oauthutil.go
index a44ae8028..8ee70112b 100644
--- a/lib/oauthutil/oauthutil.go
+++ b/lib/oauthutil/oauthutil.go
@@ -1,4 +1,4 @@
-// Package oauthutil provides OAuth utilities.
+// Package oauthutil provides OAuth utilities.
package oauthutil
import (
@@ -250,9 +250,7 @@ func (ts *TokenSource) reReadToken() (changed bool) {
return false
}
- if !newToken.Valid() {
- fs.Debugf(ts.name, "Loaded invalid token from config file - ignoring")
- } else {
+ if newToken.Valid() {
fs.Debugf(ts.name, "Loaded fresh token from config file")
changed = true
}
@@ -264,6 +262,8 @@ func (ts *TokenSource) reReadToken() (changed bool) {
if changed {
ts.token = newToken
ts.tokenSource = nil // invalidate since we changed the token
+ } else {
+ fs.Debugf(ts.name, "No updated token found in the config file")
}
return changed
}
@@ -319,6 +319,8 @@ func (ts *TokenSource) Token() (*oauth2.Token, error) {
return ts.token, nil
}
+ fs.Debug(ts.name, "Token expired")
+
// Try getting the token a few times
for i := 1; i <= maxTries; i++ {
// Try reading the token from the config file in case it has
@@ -344,6 +346,7 @@ func (ts *TokenSource) Token() (*oauth2.Token, error) {
token, err = ts.tokenSource.Token()
if err == nil {
+ fs.Debug(ts.name, "Token refresh successful")
break
}
if newErr := maybeWrapOAuthError(err, ts.name); newErr != err {
diff --git a/lib/oauthutil/renew.go b/lib/oauthutil/renew.go
index 40eab7843..2b10d46e9 100644
--- a/lib/oauthutil/renew.go
+++ b/lib/oauthutil/renew.go
@@ -1,4 +1,4 @@
-package oauthutil
+package oauthutil
import (
"sync"
@@ -47,16 +47,14 @@ func (r *Renew) renewOnExpiry() {
}
uploads := r.uploads.Load()
if uploads != 0 {
- fs.Debugf(r.name, "Token expired - %d uploads in progress - refreshing", uploads)
+ fs.Debugf(r.name, "Background refresher detected expired token - %d uploads in progress - refreshing", uploads)
// Do a transaction
err := r.run()
- if err == nil {
- fs.Debugf(r.name, "Token refresh successful")
- } else {
- fs.Errorf(r.name, "Token refresh failed: %v", err)
+ if err != nil {
+ fs.Errorf(r.name, "Background token refresher failed: %v", err)
}
} else {
- fs.Debugf(r.name, "Token expired but no uploads in progress - doing nothing")
+ fs.Debugf(r.name, "Background refresher detected expired token but no uploads in progress - doing nothing")
}
}
}
diff --git a/lib/pacer/pacer.go b/lib/pacer/pacer.go
index 1c7cc51e5..34ea543ab 100644
--- a/lib/pacer/pacer.go
+++ b/lib/pacer/pacer.go
@@ -1,9 +1,11 @@
-// Package pacer makes pacing and retrying API calls easy
+// Package pacer makes pacing and retrying API calls easy
package pacer
import (
"errors"
"fmt"
+ "runtime"
+ "strings"
"sync"
"time"
@@ -153,13 +155,13 @@ func (p *Pacer) ModifyCalculator(f func(Calculator)) {
// This must be called as a pair with endCall.
//
// This waits for the pacer token
-func (p *Pacer) beginCall() {
+func (p *Pacer) beginCall(limitConnections bool) {
// pacer starts with a token in and whenever we take one out
// XXX ms later we put another in. We could do this with a
// Ticker more accurately, but then we'd have to work out how
// not to run it when it wasn't needed
<-p.pacer
- if p.maxConnections > 0 {
+ if limitConnections {
<-p.connTokens
}
@@ -176,8 +178,8 @@ func (p *Pacer) beginCall() {
//
// This should calculate a new sleepTime. It takes a boolean as to
// whether the operation should be retried or not.
-func (p *Pacer) endCall(retry bool, err error) {
- if p.maxConnections > 0 {
+func (p *Pacer) endCall(retry bool, err error, limitConnections bool) {
+ if limitConnections {
p.connTokens <- struct{}{}
}
p.mu.Lock()
@@ -191,13 +193,44 @@ func (p *Pacer) endCall(retry bool, err error) {
p.mu.Unlock()
}
+// Detect the pacer being called reentrantly.
+//
+// This looks for Pacer.call in the call stack and returns true if it
+// is found.
+//
+// Ideally we would do this by passing a context about but there are
+// an awful lot of Pacer calls!
+//
+// This is only needed when p.maxConnections > 0 which isn't a common
+// configuration so adding a bit of extra slowdown here is not a
+// problem.
+func pacerReentered() bool {
+ var pcs [48]uintptr
+ n := runtime.Callers(3, pcs[:]) // skip runtime.Callers, pacerReentered and call
+ frames := runtime.CallersFrames(pcs[:n])
+ for {
+ f, more := frames.Next()
+ if strings.HasSuffix(f.Function, "(*Pacer).call") {
+ return true
+ }
+ if !more {
+ break
+ }
+ }
+ return false
+}
+
// call implements Call but with settable retries
func (p *Pacer) call(fn Paced, retries int) (err error) {
var retry bool
+ limitConnections := false
+ if p.maxConnections > 0 && !pacerReentered() {
+ limitConnections = true
+ }
for i := 1; i <= retries; i++ {
- p.beginCall()
+ p.beginCall(limitConnections)
retry, err = p.invoker(i, retries, fn)
- p.endCall(retry, err)
+ p.endCall(retry, err, limitConnections)
if !retry {
break
}
diff --git a/lib/pacer/pacer_test.go b/lib/pacer/pacer_test.go
index 3ac9c3741..9ce555497 100644
--- a/lib/pacer/pacer_test.go
+++ b/lib/pacer/pacer_test.go
@@ -1,4 +1,4 @@
-package pacer
+package pacer
import (
"errors"
@@ -108,7 +108,7 @@ func waitForPace(p *Pacer, duration time.Duration) (when time.Time) {
func TestBeginCall(t *testing.T) {
p := New(MaxConnectionsOption(10), CalculatorOption(NewDefault(MinSleep(1*time.Millisecond))))
emptyTokens(p)
- go p.beginCall()
+ go p.beginCall(true)
if !waitForPace(p, 10*time.Millisecond).IsZero() {
t.Errorf("beginSleep fired too early #1")
}
@@ -131,7 +131,7 @@ func TestBeginCall(t *testing.T) {
func TestBeginCallZeroConnections(t *testing.T) {
p := New(MaxConnectionsOption(0), CalculatorOption(NewDefault(MinSleep(1*time.Millisecond))))
emptyTokens(p)
- go p.beginCall()
+ go p.beginCall(false)
if !waitForPace(p, 10*time.Millisecond).IsZero() {
t.Errorf("beginSleep fired too early #1")
}
@@ -257,7 +257,7 @@ func TestEndCall(t *testing.T) {
p := New(MaxConnectionsOption(5))
emptyTokens(p)
p.state.ConsecutiveRetries = 1
- p.endCall(true, nil)
+ p.endCall(true, nil, true)
assert.Equal(t, 1, len(p.connTokens))
assert.Equal(t, 2, p.state.ConsecutiveRetries)
}
@@ -266,7 +266,7 @@ func TestEndCallZeroConnections(t *testing.T) {
p := New(MaxConnectionsOption(0))
emptyTokens(p)
p.state.ConsecutiveRetries = 1
- p.endCall(false, nil)
+ p.endCall(false, nil, false)
assert.Equal(t, 0, len(p.connTokens))
assert.Equal(t, 0, p.state.ConsecutiveRetries)
}
@@ -353,6 +353,41 @@ func TestCallParallel(t *testing.T) {
wait.Broadcast()
}
+func BenchmarkPacerReentered(b *testing.B) {
+ for b.Loop() {
+ _ = pacerReentered()
+ }
+}
+
+func BenchmarkPacerReentered100(b *testing.B) {
+ var fn func(level int)
+ fn = func(level int) {
+ if level > 0 {
+ fn(level - 1)
+ return
+ }
+ for b.Loop() {
+ _ = pacerReentered()
+ }
+
+ }
+ fn(100)
+}
+
+func TestCallMaxConnectionsRecursiveDeadlock(t *testing.T) {
+ p := New(CalculatorOption(NewDefault(MinSleep(1*time.Millisecond), MaxSleep(2*time.Millisecond))))
+ p.SetMaxConnections(1)
+ dp := &dummyPaced{retry: false}
+ err := p.Call(func() (bool, error) {
+ // check we have taken the connection token
+ // no tokens left means deadlock on the recursive call
+ assert.Equal(t, 0, len(p.connTokens))
+ return false, p.Call(dp.fn)
+ })
+ assert.Equal(t, 1, dp.called)
+ assert.Equal(t, errFoo, err)
+}
+
func TestRetryAfterError_NonNilErr(t *testing.T) {
orig := errors.New("test failure")
dur := 2 * time.Second
diff --git a/lib/pacer/pacers.go b/lib/pacer/pacers.go
index 45a3f6095..98f647973 100644
--- a/lib/pacer/pacers.go
+++ b/lib/pacer/pacers.go
@@ -1,4 +1,4 @@
-package pacer
+package pacer
import (
"math/rand"
diff --git a/lib/pacer/tokens.go b/lib/pacer/tokens.go
index b32e65648..53d049b7d 100644
--- a/lib/pacer/tokens.go
+++ b/lib/pacer/tokens.go
@@ -1,4 +1,4 @@
-// Tokens for controlling concurrency
+// Tokens for controlling concurrency
package pacer
diff --git a/lib/pacer/tokens_test.go b/lib/pacer/tokens_test.go
index 715c5c072..10b16d05c 100644
--- a/lib/pacer/tokens_test.go
+++ b/lib/pacer/tokens_test.go
@@ -1,4 +1,4 @@
-package pacer
+package pacer
import (
"testing"
diff --git a/lib/plugin/package.go b/lib/plugin/package.go
index d962d713e..be0def102 100644
--- a/lib/plugin/package.go
+++ b/lib/plugin/package.go
@@ -1,4 +1,4 @@
-// Package plugin implements loading out-of-tree storage backends
+// Package plugin implements loading out-of-tree storage backends
// using https://golang.org/pkg/plugin/ on Linux and macOS.
//
// If the $RCLONE_PLUGIN_PATH is present, any Go plugins in that dir
diff --git a/lib/plugin/plugin.go b/lib/plugin/plugin.go
index b8bf9fc33..4cf440102 100644
--- a/lib/plugin/plugin.go
+++ b/lib/plugin/plugin.go
@@ -1,4 +1,4 @@
-//go:build (darwin || linux) && !gccgo
+//go:build (darwin || linux) && !gccgo
package plugin
diff --git a/lib/pool/pool.go b/lib/pool/pool.go
index 654ae444f..27c03fdee 100644
--- a/lib/pool/pool.go
+++ b/lib/pool/pool.go
@@ -1,4 +1,4 @@
-// Package pool implements a memory pool similar in concept to
+// Package pool implements a memory pool similar in concept to
// sync.Pool but with more determinism.
package pool
diff --git a/lib/pool/pool_test.go b/lib/pool/pool_test.go
index 0be3e2400..f1251dce0 100644
--- a/lib/pool/pool_test.go
+++ b/lib/pool/pool_test.go
@@ -1,4 +1,4 @@
-package pool
+package pool
import (
"context"
@@ -16,23 +16,21 @@ import (
// makes the allocations be unreliable
func makeUnreliable(bp *Pool) {
- const maxFailsInARow = 10
- var allocFails int
+ var allocCount int
+ tests := rand.Intn(4) + 1
bp.alloc = func(size int) ([]byte, error) {
- if rand.Intn(3) != 0 && allocFails < maxFailsInARow {
- allocFails++
+ allocCount++
+ if allocCount%tests != 0 {
return nil, errors.New("failed to allocate memory")
}
- allocFails = 0
return make([]byte, size), nil
}
- var freeFails int
+ var freeCount int
bp.free = func(b []byte) error {
- if rand.Intn(3) != 0 && freeFails < maxFailsInARow {
- freeFails++
+ freeCount++
+ if freeCount%tests != 0 {
return errors.New("failed to free memory")
}
- freeFails = 0
return nil
}
}
@@ -290,22 +288,24 @@ func TestPoolMaxBufferMemory(t *testing.T) {
}
}
)
- for i := 0; i < 20; i++ {
+ const trials = 50
+ for i := range trials {
wg.Add(1)
go func() {
defer wg.Done()
- if i < 4 {
- buf := bp.GetN(i + 1)
- countBuf(i + 1)
- time.Sleep(100 * time.Millisecond)
+ if i < trials/2 {
+ n := i%4 + 1
+ buf := bp.GetN(n)
+ countBuf(n)
+ time.Sleep(1 * time.Millisecond)
+ countBuf(-n)
bp.PutN(buf)
- countBuf(-(i + 1))
} else {
buf := bp.Get()
countBuf(1)
- time.Sleep(100 * time.Millisecond)
- bp.Put(buf)
+ time.Sleep(1 * time.Millisecond)
countBuf(-1)
+ bp.Put(buf)
}
}()
}
diff --git a/lib/pool/reader_writer.go b/lib/pool/reader_writer.go
index 8ea398ddb..7089f6c37 100644
--- a/lib/pool/reader_writer.go
+++ b/lib/pool/reader_writer.go
@@ -1,4 +1,4 @@
-package pool
+package pool
import (
"context"
diff --git a/lib/pool/reader_writer_test.go b/lib/pool/reader_writer_test.go
index bd6a91631..0dbe6cbb9 100644
--- a/lib/pool/reader_writer_test.go
+++ b/lib/pool/reader_writer_test.go
@@ -1,4 +1,4 @@
-package pool
+package pool
import (
"bytes"
diff --git a/lib/proxy/http.go b/lib/proxy/http.go
index 616d2c4c1..64d78a804 100644
--- a/lib/proxy/http.go
+++ b/lib/proxy/http.go
@@ -1,4 +1,4 @@
-package proxy
+package proxy
import (
"bufio"
diff --git a/lib/proxy/socks.go b/lib/proxy/socks.go
index 1f8e3c40a..4e813ca82 100644
--- a/lib/proxy/socks.go
+++ b/lib/proxy/socks.go
@@ -1,4 +1,4 @@
-// Package proxy enables SOCKS5 proxy dialling
+// Package proxy enables SOCKS5 proxy dialling
package proxy
import (
diff --git a/lib/random/random.go b/lib/random/random.go
index de9d05c05..3ab887f32 100644
--- a/lib/random/random.go
+++ b/lib/random/random.go
@@ -1,4 +1,4 @@
-// Package random holds a few functions for working with random numbers
+// Package random holds a few functions for working with random numbers
package random
import (
diff --git a/lib/random/random_test.go b/lib/random/random_test.go
index 78944f74b..a952340a4 100644
--- a/lib/random/random_test.go
+++ b/lib/random/random_test.go
@@ -1,4 +1,4 @@
-package random
+package random
import (
"testing"
diff --git a/lib/ranges/ranges.go b/lib/ranges/ranges.go
index d14377cef..43cd77ed3 100644
--- a/lib/ranges/ranges.go
+++ b/lib/ranges/ranges.go
@@ -1,4 +1,4 @@
-// Package ranges provides the Ranges type for keeping track of byte
+// Package ranges provides the Ranges type for keeping track of byte
// ranges which may or may not be present in an object.
package ranges
diff --git a/lib/ranges/ranges_test.go b/lib/ranges/ranges_test.go
index c172add29..9fa979bb5 100644
--- a/lib/ranges/ranges_test.go
+++ b/lib/ranges/ranges_test.go
@@ -1,4 +1,4 @@
-package ranges
+package ranges
import (
"fmt"
diff --git a/lib/readers/context.go b/lib/readers/context.go
index 177b18809..88bc41b82 100644
--- a/lib/readers/context.go
+++ b/lib/readers/context.go
@@ -1,4 +1,4 @@
-// Package readers provides io.Reader related utilities.
+// Package readers provides io.Reader related utilities.
package readers
import (
diff --git a/lib/readers/context_test.go b/lib/readers/context_test.go
index 3bd70e4d5..0fe5cab96 100644
--- a/lib/readers/context_test.go
+++ b/lib/readers/context_test.go
@@ -1,4 +1,4 @@
-package readers
+package readers
import (
"context"
diff --git a/lib/readers/counting_reader.go b/lib/readers/counting_reader.go
index 872b4c50e..bf7813324 100644
--- a/lib/readers/counting_reader.go
+++ b/lib/readers/counting_reader.go
@@ -1,4 +1,4 @@
-package readers
+package readers
import "io"
diff --git a/lib/readers/error.go b/lib/readers/error.go
index 13d4f077b..07d178aed 100644
--- a/lib/readers/error.go
+++ b/lib/readers/error.go
@@ -1,4 +1,4 @@
-package readers
+package readers
// ErrorReader wraps an error to return on Read
type ErrorReader struct {
diff --git a/lib/readers/error_test.go b/lib/readers/error_test.go
index e8c73f980..4e9653408 100644
--- a/lib/readers/error_test.go
+++ b/lib/readers/error_test.go
@@ -1,4 +1,4 @@
-package readers
+package readers
import (
"errors"
diff --git a/lib/readers/fakeseeker.go b/lib/readers/fakeseeker.go
index 844fc0422..1d3869f1f 100644
--- a/lib/readers/fakeseeker.go
+++ b/lib/readers/fakeseeker.go
@@ -1,4 +1,4 @@
-package readers
+package readers
import (
"errors"
diff --git a/lib/readers/fakeseeker_test.go b/lib/readers/fakeseeker_test.go
index 0702b7cbb..9636d3d19 100644
--- a/lib/readers/fakeseeker_test.go
+++ b/lib/readers/fakeseeker_test.go
@@ -1,4 +1,4 @@
-package readers
+package readers
import (
"bytes"
diff --git a/lib/readers/gzip.go b/lib/readers/gzip.go
index e018f2880..d2a976ed0 100644
--- a/lib/readers/gzip.go
+++ b/lib/readers/gzip.go
@@ -1,4 +1,4 @@
-package readers
+package readers
import (
"compress/gzip"
diff --git a/lib/readers/gzip_test.go b/lib/readers/gzip_test.go
index 3999e3336..6354c4743 100644
--- a/lib/readers/gzip_test.go
+++ b/lib/readers/gzip_test.go
@@ -1,4 +1,4 @@
-package readers
+package readers
import (
"bytes"
diff --git a/lib/readers/limited.go b/lib/readers/limited.go
index 2b346f47e..3d761214f 100644
--- a/lib/readers/limited.go
+++ b/lib/readers/limited.go
@@ -1,4 +1,4 @@
-package readers
+package readers
import (
"io"
diff --git a/lib/readers/noclose.go b/lib/readers/noclose.go
index dc36e8be2..9e4289ad6 100644
--- a/lib/readers/noclose.go
+++ b/lib/readers/noclose.go
@@ -1,4 +1,4 @@
-package readers
+package readers
import "io"
diff --git a/lib/readers/noclose_test.go b/lib/readers/noclose_test.go
index e954d9c72..847218cda 100644
--- a/lib/readers/noclose_test.go
+++ b/lib/readers/noclose_test.go
@@ -1,4 +1,4 @@
-package readers
+package readers
import (
"errors"
diff --git a/lib/readers/noseeker.go b/lib/readers/noseeker.go
index 1cd7e762c..e90f4c78a 100644
--- a/lib/readers/noseeker.go
+++ b/lib/readers/noseeker.go
@@ -1,4 +1,4 @@
-package readers
+package readers
import (
"errors"
diff --git a/lib/readers/noseeker_test.go b/lib/readers/noseeker_test.go
index 3f8d88c2f..aa1d71f64 100644
--- a/lib/readers/noseeker_test.go
+++ b/lib/readers/noseeker_test.go
@@ -1,4 +1,4 @@
-package readers
+package readers
import (
"bytes"
diff --git a/lib/readers/pattern_reader.go b/lib/readers/pattern_reader.go
index edceef24d..2a337f72a 100644
--- a/lib/readers/pattern_reader.go
+++ b/lib/readers/pattern_reader.go
@@ -1,4 +1,4 @@
-package readers
+package readers
import (
"errors"
diff --git a/lib/readers/pattern_reader_test.go b/lib/readers/pattern_reader_test.go
index 36f57c921..4ae4c136f 100644
--- a/lib/readers/pattern_reader_test.go
+++ b/lib/readers/pattern_reader_test.go
@@ -1,4 +1,4 @@
-package readers
+package readers
import (
"io"
diff --git a/lib/readers/readfill.go b/lib/readers/readfill.go
index 64b5de44e..18a223bdf 100644
--- a/lib/readers/readfill.go
+++ b/lib/readers/readfill.go
@@ -1,4 +1,4 @@
-package readers
+package readers
import "io"
diff --git a/lib/readers/readfill_test.go b/lib/readers/readfill_test.go
index 264fc721e..ef75262c4 100644
--- a/lib/readers/readfill_test.go
+++ b/lib/readers/readfill_test.go
@@ -1,4 +1,4 @@
-package readers
+package readers
import (
"io"
diff --git a/lib/readers/repeatable.go b/lib/readers/repeatable.go
index 73de3fe90..24cf59217 100644
--- a/lib/readers/repeatable.go
+++ b/lib/readers/repeatable.go
@@ -1,4 +1,4 @@
-package readers
+package readers
import (
"errors"
diff --git a/lib/readers/repeatable_test.go b/lib/readers/repeatable_test.go
index d6a4c560b..894c87e67 100644
--- a/lib/readers/repeatable_test.go
+++ b/lib/readers/repeatable_test.go
@@ -1,4 +1,4 @@
-package readers
+package readers
import (
"bytes"
diff --git a/lib/rest/headers.go b/lib/rest/headers.go
index 3e52fbdaa..fd74269a5 100644
--- a/lib/rest/headers.go
+++ b/lib/rest/headers.go
@@ -1,4 +1,4 @@
-package rest
+package rest
import (
"net/http"
diff --git a/lib/rest/headers_test.go b/lib/rest/headers_test.go
index 23400062d..1bb9b1956 100644
--- a/lib/rest/headers_test.go
+++ b/lib/rest/headers_test.go
@@ -1,4 +1,4 @@
-package rest
+package rest
import (
"net/http"
diff --git a/lib/rest/rest.go b/lib/rest/rest.go
index 2557b68c3..ddacd8dc2 100644
--- a/lib/rest/rest.go
+++ b/lib/rest/rest.go
@@ -1,4 +1,4 @@
-// Package rest implements a simple REST wrapper
+// Package rest implements a simple REST wrapper
//
// All methods are safe for concurrent calling.
package rest
diff --git a/lib/rest/url.go b/lib/rest/url.go
index fcda35693..1ef17d06c 100644
--- a/lib/rest/url.go
+++ b/lib/rest/url.go
@@ -1,4 +1,4 @@
-package rest
+package rest
import (
"fmt"
diff --git a/lib/rest/url_test.go b/lib/rest/url_test.go
index 263634879..233ca8e03 100644
--- a/lib/rest/url_test.go
+++ b/lib/rest/url_test.go
@@ -1,4 +1,4 @@
-package rest
+package rest
import (
"fmt"
diff --git a/lib/sdactivation/sdactivation_stub.go b/lib/sdactivation/sdactivation_stub.go
index e5fbff553..955ce1e11 100644
--- a/lib/sdactivation/sdactivation_stub.go
+++ b/lib/sdactivation/sdactivation_stub.go
@@ -1,4 +1,4 @@
-//go:build windows || plan9
+//go:build windows || plan9
// +build windows plan9
// Package sdactivation provides support for systemd socket activation,
diff --git a/lib/sdactivation/sdactivation_unix.go b/lib/sdactivation/sdactivation_unix.go
index 523321bcf..8074632b0 100644
--- a/lib/sdactivation/sdactivation_unix.go
+++ b/lib/sdactivation/sdactivation_unix.go
@@ -1,4 +1,4 @@
-//go:build !windows && !plan9
+//go:build !windows && !plan9
// +build !windows,!plan9
// Package sdactivation provides support for systemd socket activation, wrapping
diff --git a/lib/structs/structs.go b/lib/structs/structs.go
index f4ad54441..88cb6ef20 100644
--- a/lib/structs/structs.go
+++ b/lib/structs/structs.go
@@ -1,4 +1,4 @@
-// Package structs is for manipulating structures with reflection
+// Package structs is for manipulating structures with reflection
package structs
import (
diff --git a/lib/structs/structs_test.go b/lib/structs/structs_test.go
index fe13b19ed..b2c8f6b04 100644
--- a/lib/structs/structs_test.go
+++ b/lib/structs/structs_test.go
@@ -1,4 +1,4 @@
-package structs
+package structs
import (
"fmt"
diff --git a/lib/systemd/doc.go b/lib/systemd/doc.go
index 5786e6552..a2fb193b3 100644
--- a/lib/systemd/doc.go
+++ b/lib/systemd/doc.go
@@ -1,2 +1,2 @@
-// Package systemd contains utilities for communication with the systemd service manager.
+// Package systemd contains utilities for communication with the systemd service manager.
package systemd
diff --git a/lib/systemd/notify.go b/lib/systemd/notify.go
index 056a8628d..9b56e1962 100644
--- a/lib/systemd/notify.go
+++ b/lib/systemd/notify.go
@@ -1,4 +1,4 @@
-package systemd
+package systemd
import (
"fmt"
diff --git a/lib/terminal/hidden_other.go b/lib/terminal/hidden_other.go
index cab0caf51..66d5043ce 100644
--- a/lib/terminal/hidden_other.go
+++ b/lib/terminal/hidden_other.go
@@ -1,4 +1,4 @@
-//go:build !windows
+//go:build !windows
package terminal
diff --git a/lib/terminal/hidden_windows.go b/lib/terminal/hidden_windows.go
index 1bc7f5e77..14a1ea2de 100644
--- a/lib/terminal/hidden_windows.go
+++ b/lib/terminal/hidden_windows.go
@@ -1,4 +1,4 @@
-//go:build windows
+//go:build windows
package terminal
diff --git a/lib/terminal/terminal.go b/lib/terminal/terminal.go
index e695dac70..fbbba5331 100644
--- a/lib/terminal/terminal.go
+++ b/lib/terminal/terminal.go
@@ -1,4 +1,4 @@
-// Package terminal provides VT100 terminal codes and a windows
+// Package terminal provides VT100 terminal codes and a windows
// implementation of that.
package terminal
diff --git a/lib/terminal/terminal_normal.go b/lib/terminal/terminal_normal.go
index a1b399a47..2c006ce38 100644
--- a/lib/terminal/terminal_normal.go
+++ b/lib/terminal/terminal_normal.go
@@ -1,4 +1,4 @@
-//go:build !js
+//go:build !js
package terminal
diff --git a/lib/terminal/terminal_unsupported.go b/lib/terminal/terminal_unsupported.go
index 6a2c98ceb..cb22cf396 100644
--- a/lib/terminal/terminal_unsupported.go
+++ b/lib/terminal/terminal_unsupported.go
@@ -1,4 +1,4 @@
-//go:build js
+//go:build js
package terminal
diff --git a/lib/transform/cmap.go b/lib/transform/cmap.go
index b5341c63e..a679a6f0e 100644
--- a/lib/transform/cmap.go
+++ b/lib/transform/cmap.go
@@ -1,4 +1,4 @@
-package transform
+package transform
import (
"fmt"
diff --git a/lib/transform/gen_help.go b/lib/transform/gen_help.go
index ec93ea2a0..4e503561e 100644
--- a/lib/transform/gen_help.go
+++ b/lib/transform/gen_help.go
@@ -1,4 +1,4 @@
-// Create the help text for transform
+// Create the help text for transform
//
// Run with go generate (defined in transform.go)
//
diff --git a/lib/transform/options.go b/lib/transform/options.go
index 47024c740..069ad1d78 100644
--- a/lib/transform/options.go
+++ b/lib/transform/options.go
@@ -1,4 +1,4 @@
-package transform
+package transform
import (
"context"
diff --git a/lib/transform/transform.go b/lib/transform/transform.go
index 0ae81cea5..e4db66195 100644
--- a/lib/transform/transform.go
+++ b/lib/transform/transform.go
@@ -1,4 +1,4 @@
-// Package transform holds functions for path name transformations
+// Package transform holds functions for path name transformations
//
//go:generate go run gen_help.go transform.md
package transform
diff --git a/lib/transform/transform_test.go b/lib/transform/transform_test.go
index c10eaf1b9..18f417091 100644
--- a/lib/transform/transform_test.go
+++ b/lib/transform/transform_test.go
@@ -1,4 +1,4 @@
-package transform
+package transform
import (
"context"
diff --git a/lib/version/version.go b/lib/version/version.go
index e8a4c17f6..759177280 100644
--- a/lib/version/version.go
+++ b/lib/version/version.go
@@ -1,4 +1,4 @@
-// Package version provides machinery for versioning file names
+// Package version provides machinery for versioning file names
// with a timestamp-based version string
package version
diff --git a/lib/version/version_test.go b/lib/version/version_test.go
index d514b1918..7bf8c6412 100644
--- a/lib/version/version_test.go
+++ b/lib/version/version_test.go
@@ -1,4 +1,4 @@
-package version_test
+package version_test
import (
"testing"
diff --git a/librclone/gomobile/gomobile.go b/librclone/gomobile/gomobile.go
index ff3f451c2..e16d0e664 100644
--- a/librclone/gomobile/gomobile.go
+++ b/librclone/gomobile/gomobile.go
@@ -1,4 +1,4 @@
-// Package gomobile exports shims for gomobile use
+// Package gomobile exports shims for gomobile use
package gomobile
import (
diff --git a/librclone/librclone.go b/librclone/librclone.go
index ef92b6d8a..6bcfc0583 100644
--- a/librclone/librclone.go
+++ b/librclone/librclone.go
@@ -1,4 +1,4 @@
-// Package librclone exports shims for C library use
+// Package librclone exports shims for C library use
//
// This directory contains code to build rclone as a C library and the
// shims for accessing rclone from C.
diff --git a/librclone/librclone/librclone.go b/librclone/librclone/librclone.go
index 1e7dd6e11..3c68b77ab 100644
--- a/librclone/librclone/librclone.go
+++ b/librclone/librclone/librclone.go
@@ -1,4 +1,4 @@
-// Package librclone exports shims for library use
+// Package librclone exports shims for library use
//
// This is the internal implementation which is used for C and
// Gomobile libraries which need slightly different export styles.
diff --git a/rclone.1 b/rclone.1
index d2617c70d..6a565dcd2 100644
--- a/rclone.1
+++ b/rclone.1
@@ -30907,7 +30907,7 @@ Flags for general networking and HTTP stuff.
--tpslimit float Limit HTTP transactions per second to this
--tpslimit-burst int Max burst of transactions for --tpslimit (default 1)
--use-cookies Enable session cookiejar
- --user-agent string Set the user-agent to a specified string (default \[dq]rclone/v1.73.1\[dq])
+ --user-agent string Set the user-agent to a specified string (default \[dq]rclone/v1.73.2\[dq])
\f[R]
.fi
.SS Performance
diff --git a/rclone.go b/rclone.go
index 74fde16ab..ee6bfa399 100644
--- a/rclone.go
+++ b/rclone.go
@@ -1,4 +1,4 @@
-// Sync files and directories to and from local and remote object stores
+// Sync files and directories to and from local and remote object stores
//
// Nick Craig-Wood
package main
diff --git a/vfs/dir.go b/vfs/dir.go
index 174b7d001..77ae24ce9 100644
--- a/vfs/dir.go
+++ b/vfs/dir.go
@@ -1,4 +1,4 @@
-package vfs
+package vfs
import (
"context"
diff --git a/vfs/dir_handle.go b/vfs/dir_handle.go
index d2ef0f9e9..7cddeead5 100644
--- a/vfs/dir_handle.go
+++ b/vfs/dir_handle.go
@@ -1,4 +1,4 @@
-package vfs
+package vfs
import (
"io"
diff --git a/vfs/dir_handle_test.go b/vfs/dir_handle_test.go
index cfb215c2f..f6300a8e3 100644
--- a/vfs/dir_handle_test.go
+++ b/vfs/dir_handle_test.go
@@ -1,4 +1,4 @@
-package vfs
+package vfs
import (
"context"
diff --git a/vfs/dir_test.go b/vfs/dir_test.go
index 3d7ab8cee..8645b7a2a 100644
--- a/vfs/dir_test.go
+++ b/vfs/dir_test.go
@@ -1,4 +1,4 @@
-package vfs
+package vfs
import (
"context"
diff --git a/vfs/errors.go b/vfs/errors.go
index c54b4dd91..5b0d569e9 100644
--- a/vfs/errors.go
+++ b/vfs/errors.go
@@ -1,4 +1,4 @@
-// Cross platform errors
+// Cross platform errors
package vfs
diff --git a/vfs/errors_test.go b/vfs/errors_test.go
index 7865fb6f3..e252636ad 100644
--- a/vfs/errors_test.go
+++ b/vfs/errors_test.go
@@ -1,4 +1,4 @@
-package vfs
+package vfs
import (
"testing"
diff --git a/vfs/file.go b/vfs/file.go
index 76aa03b7d..aad021252 100644
--- a/vfs/file.go
+++ b/vfs/file.go
@@ -1,4 +1,4 @@
-package vfs
+package vfs
import (
"context"
diff --git a/vfs/file_test.go b/vfs/file_test.go
index 2a4b5fd47..dace65f04 100644
--- a/vfs/file_test.go
+++ b/vfs/file_test.go
@@ -1,4 +1,4 @@
-package vfs
+package vfs
import (
"context"
diff --git a/vfs/make_open_tests.go b/vfs/make_open_tests.go
index 01df4d0a9..7369c46fc 100644
--- a/vfs/make_open_tests.go
+++ b/vfs/make_open_tests.go
@@ -1,4 +1,4 @@
-// This makes the open test suite. It tries to open a file (existing
+// This makes the open test suite. It tries to open a file (existing
// or not existing) with all possible file modes and writes a test
// matrix.
//
diff --git a/vfs/open_test.go b/vfs/open_test.go
index 5fae18691..da5683a98 100644
--- a/vfs/open_test.go
+++ b/vfs/open_test.go
@@ -1,4 +1,4 @@
-// Code generated by make_open_tests.go - use go generate to rebuild - DO NOT EDIT
+// Code generated by make_open_tests.go - use go generate to rebuild - DO NOT EDIT
package vfs
diff --git a/vfs/rc.go b/vfs/rc.go
index d780048bd..b7d268dec 100644
--- a/vfs/rc.go
+++ b/vfs/rc.go
@@ -1,4 +1,4 @@
-package vfs
+package vfs
import (
"context"
diff --git a/vfs/rc_test.go b/vfs/rc_test.go
index 9c70d526a..660528fe5 100644
--- a/vfs/rc_test.go
+++ b/vfs/rc_test.go
@@ -1,4 +1,4 @@
-package vfs
+package vfs
import (
"context"
diff --git a/vfs/read.go b/vfs/read.go
index 777946f26..c9701f8f2 100644
--- a/vfs/read.go
+++ b/vfs/read.go
@@ -1,4 +1,4 @@
-package vfs
+package vfs
import (
"context"
diff --git a/vfs/read_test.go b/vfs/read_test.go
index 6b7d55c4e..144bf790b 100644
--- a/vfs/read_test.go
+++ b/vfs/read_test.go
@@ -1,4 +1,4 @@
-package vfs
+package vfs
import (
"context"
diff --git a/vfs/read_write.go b/vfs/read_write.go
index 56b9c467d..c399540b3 100644
--- a/vfs/read_write.go
+++ b/vfs/read_write.go
@@ -1,4 +1,4 @@
-package vfs
+package vfs
import (
"fmt"
diff --git a/vfs/read_write_test.go b/vfs/read_write_test.go
index 5b30ed675..a6c733f82 100644
--- a/vfs/read_write_test.go
+++ b/vfs/read_write_test.go
@@ -1,4 +1,4 @@
-package vfs
+package vfs
import (
"context"
diff --git a/vfs/sighup.go b/vfs/sighup.go
index fadcbcdda..3bf232341 100644
--- a/vfs/sighup.go
+++ b/vfs/sighup.go
@@ -1,4 +1,4 @@
-//go:build !plan9 && !js
+//go:build !plan9 && !js
package vfs
diff --git a/vfs/sighup_unsupported.go b/vfs/sighup_unsupported.go
index 19e08a34b..8d450c391 100644
--- a/vfs/sighup_unsupported.go
+++ b/vfs/sighup_unsupported.go
@@ -1,4 +1,4 @@
-//go:build plan9 || js
+//go:build plan9 || js
package vfs
diff --git a/vfs/test_vfs/test_vfs.go b/vfs/test_vfs/test_vfs.go
index a38f72e67..5911b386b 100644
--- a/vfs/test_vfs/test_vfs.go
+++ b/vfs/test_vfs/test_vfs.go
@@ -1,4 +1,4 @@
-// Test the VFS to exhaustion, specifically looking for deadlocks
+// Test the VFS to exhaustion, specifically looking for deadlocks
//
// Run on a mounted filesystem
package main
diff --git a/vfs/vfs.go b/vfs/vfs.go
index 1d2f71dd1..52b8d1ddc 100644
--- a/vfs/vfs.go
+++ b/vfs/vfs.go
@@ -1,4 +1,4 @@
-// Package vfs provides a virtual filing system layer over rclone's
+// Package vfs provides a virtual filing system layer over rclone's
// native objects.
//
// It attempts to behave in a similar way to Go's filing system
diff --git a/vfs/vfs_case_test.go b/vfs/vfs_case_test.go
index 26acd2210..3d1eae289 100644
--- a/vfs/vfs_case_test.go
+++ b/vfs/vfs_case_test.go
@@ -1,4 +1,4 @@
-package vfs
+package vfs
import (
"context"
diff --git a/vfs/vfs_test.go b/vfs/vfs_test.go
index f175e0efc..040f7f476 100644
--- a/vfs/vfs_test.go
+++ b/vfs/vfs_test.go
@@ -1,4 +1,4 @@
-// Test suite for vfs
+// Test suite for vfs
package vfs
diff --git a/vfs/vfscache/cache.go b/vfs/vfscache/cache.go
index 0ffa30c76..d989f4cd5 100644
--- a/vfs/vfscache/cache.go
+++ b/vfs/vfscache/cache.go
@@ -1,4 +1,4 @@
-// Package vfscache deals with caching of files locally for the VFS layer
+// Package vfscache deals with caching of files locally for the VFS layer
package vfscache
import (
diff --git a/vfs/vfscache/cache_test.go b/vfs/vfscache/cache_test.go
index 8c5510f6d..e36a488e4 100644
--- a/vfs/vfscache/cache_test.go
+++ b/vfs/vfscache/cache_test.go
@@ -1,4 +1,4 @@
-package vfscache
+package vfscache
import (
"context"
diff --git a/vfs/vfscache/downloaders/downloaders.go b/vfs/vfscache/downloaders/downloaders.go
index 87783fb6b..ea19f4bd1 100644
--- a/vfs/vfscache/downloaders/downloaders.go
+++ b/vfs/vfscache/downloaders/downloaders.go
@@ -1,4 +1,4 @@
-// Package downloaders provides utilities for the VFS layer
+// Package downloaders provides utilities for the VFS layer
package downloaders
import (
diff --git a/vfs/vfscache/downloaders/downloaders_test.go b/vfs/vfscache/downloaders/downloaders_test.go
index 8d82c3f99..cc4f8e563 100644
--- a/vfs/vfscache/downloaders/downloaders_test.go
+++ b/vfs/vfscache/downloaders/downloaders_test.go
@@ -1,4 +1,4 @@
-package downloaders
+package downloaders
import (
"context"
diff --git a/vfs/vfscache/item.go b/vfs/vfscache/item.go
index 66adc9b66..53b2ae39e 100644
--- a/vfs/vfscache/item.go
+++ b/vfs/vfscache/item.go
@@ -1,4 +1,4 @@
-package vfscache
+package vfscache
import (
"context"
diff --git a/vfs/vfscache/item_test.go b/vfs/vfscache/item_test.go
index 048261b62..22ae60028 100644
--- a/vfs/vfscache/item_test.go
+++ b/vfs/vfscache/item_test.go
@@ -1,4 +1,4 @@
-package vfscache
+package vfscache
// FIXME need to test async writeback here
diff --git a/vfs/vfscache/writeback/writeback.go b/vfs/vfscache/writeback/writeback.go
index fe7b9ed62..26155dc59 100644
--- a/vfs/vfscache/writeback/writeback.go
+++ b/vfs/vfscache/writeback/writeback.go
@@ -1,4 +1,4 @@
-// Package writeback keeps track of the files which need to be written
+// Package writeback keeps track of the files which need to be written
// back to storage
package writeback
diff --git a/vfs/vfscache/writeback/writeback_test.go b/vfs/vfscache/writeback/writeback_test.go
index 87c067627..8e77df9d2 100644
--- a/vfs/vfscache/writeback/writeback_test.go
+++ b/vfs/vfscache/writeback/writeback_test.go
@@ -1,4 +1,4 @@
-package writeback
+package writeback
import (
"container/heap"
diff --git a/vfs/vfscommon/cachemode.go b/vfs/vfscommon/cachemode.go
index ab1ccb3dc..2dd7609a5 100644
--- a/vfs/vfscommon/cachemode.go
+++ b/vfs/vfscommon/cachemode.go
@@ -1,4 +1,4 @@
-// Package vfscommon provides utilities for VFS.
+// Package vfscommon provides utilities for VFS.
package vfscommon
import (
diff --git a/vfs/vfscommon/cachemode_test.go b/vfs/vfscommon/cachemode_test.go
index fb8f86734..d4ad50566 100644
--- a/vfs/vfscommon/cachemode_test.go
+++ b/vfs/vfscommon/cachemode_test.go
@@ -1,4 +1,4 @@
-package vfscommon
+package vfscommon
import (
"encoding/json"
diff --git a/vfs/vfscommon/filemode.go b/vfs/vfscommon/filemode.go
index 109db6c18..8b882eb47 100644
--- a/vfs/vfscommon/filemode.go
+++ b/vfs/vfscommon/filemode.go
@@ -1,4 +1,4 @@
-package vfscommon
+package vfscommon
import (
"fmt"
diff --git a/vfs/vfscommon/filemode_test.go b/vfs/vfscommon/filemode_test.go
index 310194ca4..f440a3b3b 100644
--- a/vfs/vfscommon/filemode_test.go
+++ b/vfs/vfscommon/filemode_test.go
@@ -1,4 +1,4 @@
-package vfscommon
+package vfscommon
import (
"encoding/json"
diff --git a/vfs/vfscommon/options.go b/vfs/vfscommon/options.go
index 7f222edac..60c619284 100644
--- a/vfs/vfscommon/options.go
+++ b/vfs/vfscommon/options.go
@@ -1,4 +1,4 @@
-package vfscommon
+package vfscommon
import (
"context"
diff --git a/vfs/vfscommon/path.go b/vfs/vfscommon/path.go
index 93b42a93e..17031e3e1 100644
--- a/vfs/vfscommon/path.go
+++ b/vfs/vfscommon/path.go
@@ -1,4 +1,4 @@
-package vfscommon
+package vfscommon
import (
"path"
diff --git a/vfs/vfscommon/vfsflags_non_unix.go b/vfs/vfscommon/vfsflags_non_unix.go
index 0cf18a200..47d9b1755 100644
--- a/vfs/vfscommon/vfsflags_non_unix.go
+++ b/vfs/vfscommon/vfsflags_non_unix.go
@@ -1,4 +1,4 @@
-//go:build !linux && !darwin && !freebsd
+//go:build !linux && !darwin && !freebsd
package vfscommon
diff --git a/vfs/vfscommon/vfsflags_unix.go b/vfs/vfscommon/vfsflags_unix.go
index 48ea7ef2e..6ccab007b 100644
--- a/vfs/vfscommon/vfsflags_unix.go
+++ b/vfs/vfscommon/vfsflags_unix.go
@@ -1,4 +1,4 @@
-//go:build linux || darwin || freebsd
+//go:build linux || darwin || freebsd
package vfscommon
diff --git a/vfs/vfsflags/vfsflags.go b/vfs/vfsflags/vfsflags.go
index f5709d7a8..b6058528f 100644
--- a/vfs/vfsflags/vfsflags.go
+++ b/vfs/vfsflags/vfsflags.go
@@ -1,4 +1,4 @@
-// Package vfsflags implements command line flags to set up a vfs
+// Package vfsflags implements command line flags to set up a vfs
package vfsflags
import (
diff --git a/vfs/vfstest/dir.go b/vfs/vfstest/dir.go
index 0cf02ed0f..c88d51234 100644
--- a/vfs/vfstest/dir.go
+++ b/vfs/vfstest/dir.go
@@ -1,4 +1,4 @@
-package vfstest
+package vfstest
import (
"context"
diff --git a/vfs/vfstest/edge_cases.go b/vfs/vfstest/edge_cases.go
index a54b8cc90..b77750036 100644
--- a/vfs/vfstest/edge_cases.go
+++ b/vfs/vfstest/edge_cases.go
@@ -1,4 +1,4 @@
-package vfstest
+package vfstest
import (
"runtime"
diff --git a/vfs/vfstest/file.go b/vfs/vfstest/file.go
index e923b521f..9fc5ce02d 100644
--- a/vfs/vfstest/file.go
+++ b/vfs/vfstest/file.go
@@ -1,4 +1,4 @@
-package vfstest
+package vfstest
import (
"os"
diff --git a/vfs/vfstest/fs.go b/vfs/vfstest/fs.go
index c9db4fa19..a2c8a6b08 100644
--- a/vfs/vfstest/fs.go
+++ b/vfs/vfstest/fs.go
@@ -1,4 +1,4 @@
-// Test suite for rclonefs
+// Test suite for rclonefs
package vfstest
@@ -212,7 +212,7 @@ type dirMap map[string]struct{}
// Create a dirMap from a string
func newDirMap(dirString string) (dm dirMap) {
dm = make(dirMap)
- for _, entry := range strings.Split(dirString, "|") {
+ for entry := range strings.SplitSeq(dirString, "|") {
if entry != "" {
dm[entry] = struct{}{}
}
diff --git a/vfs/vfstest/os.go b/vfs/vfstest/os.go
index ea6d00641..b18491daa 100644
--- a/vfs/vfstest/os.go
+++ b/vfs/vfstest/os.go
@@ -1,4 +1,4 @@
-package vfstest
+package vfstest
import (
"os"
diff --git a/vfs/vfstest/read.go b/vfs/vfstest/read.go
index 518d5021f..51d68c7c1 100644
--- a/vfs/vfstest/read.go
+++ b/vfs/vfstest/read.go
@@ -1,4 +1,4 @@
-package vfstest
+package vfstest
import (
"io"
diff --git a/vfs/vfstest/read_non_unix.go b/vfs/vfstest/read_non_unix.go
index d2328c8e3..628e3843f 100644
--- a/vfs/vfstest/read_non_unix.go
+++ b/vfs/vfstest/read_non_unix.go
@@ -1,4 +1,4 @@
-//go:build !linux && !darwin && !freebsd
+//go:build !linux && !darwin && !freebsd
package vfstest
diff --git a/vfs/vfstest/read_unix.go b/vfs/vfstest/read_unix.go
index c5b1e881f..557b9210d 100644
--- a/vfs/vfstest/read_unix.go
+++ b/vfs/vfstest/read_unix.go
@@ -1,4 +1,4 @@
-//go:build linux || darwin || freebsd
+//go:build linux || darwin || freebsd
package vfstest
diff --git a/vfs/vfstest/submount.go b/vfs/vfstest/submount.go
index ac6400cdf..13b4e76e5 100644
--- a/vfs/vfstest/submount.go
+++ b/vfs/vfstest/submount.go
@@ -1,4 +1,4 @@
-package vfstest
+package vfstest
import (
"bufio"
diff --git a/vfs/vfstest/vfs.go b/vfs/vfstest/vfs.go
index ad56fd42e..ad0838303 100644
--- a/vfs/vfstest/vfs.go
+++ b/vfs/vfstest/vfs.go
@@ -1,4 +1,4 @@
-// Package vfstest provides tests for VFS.
+// Package vfstest provides tests for VFS.
package vfstest
import (
diff --git a/vfs/vfstest/write.go b/vfs/vfstest/write.go
index 6d883cbf0..3f6658f62 100644
--- a/vfs/vfstest/write.go
+++ b/vfs/vfstest/write.go
@@ -1,4 +1,4 @@
-package vfstest
+package vfstest
import (
"os"
diff --git a/vfs/vfstest/write_other.go b/vfs/vfstest/write_other.go
index f95c3efa1..998125302 100644
--- a/vfs/vfstest/write_other.go
+++ b/vfs/vfstest/write_other.go
@@ -1,4 +1,4 @@
-//go:build !linux && !darwin && !freebsd && !windows
+//go:build !linux && !darwin && !freebsd && !windows
// +build !linux,!darwin,!freebsd,!windows
package vfstest
diff --git a/vfs/vfstest/write_unix.go b/vfs/vfstest/write_unix.go
index bbd9c2657..c0f60e383 100644
--- a/vfs/vfstest/write_unix.go
+++ b/vfs/vfstest/write_unix.go
@@ -1,4 +1,4 @@
-//go:build linux || darwin || freebsd
+//go:build linux || darwin || freebsd
package vfstest
diff --git a/vfs/vfstest/write_windows.go b/vfs/vfstest/write_windows.go
index 227e302a3..f5beb0da4 100644
--- a/vfs/vfstest/write_windows.go
+++ b/vfs/vfstest/write_windows.go
@@ -1,4 +1,4 @@
-//go:build windows
+//go:build windows
package vfstest
diff --git a/vfs/vfstest_test.go b/vfs/vfstest_test.go
index df351736e..34c34da5a 100644
--- a/vfs/vfstest_test.go
+++ b/vfs/vfstest_test.go
@@ -1,4 +1,4 @@
-// Run the more functional vfstest package on the vfs
+// Run the more functional vfstest package on the vfs
package vfs_test
diff --git a/vfs/vstate_string.go b/vfs/vstate_string.go
index fd9fe4487..7ceb135f3 100644
--- a/vfs/vstate_string.go
+++ b/vfs/vstate_string.go
@@ -1,4 +1,4 @@
-// Code generated by "stringer -type=vState"; DO NOT EDIT.
+// Code generated by "stringer -type=vState"; DO NOT EDIT.
package vfs
diff --git a/vfs/write.go b/vfs/write.go
index 8e74b45a8..434dff83c 100644
--- a/vfs/write.go
+++ b/vfs/write.go
@@ -1,4 +1,4 @@
-package vfs
+package vfs
import (
"context"
diff --git a/vfs/write_test.go b/vfs/write_test.go
index 77173bdf5..853f8401e 100644
--- a/vfs/write_test.go
+++ b/vfs/write_test.go
@@ -1,4 +1,4 @@
-package vfs
+package vfs
import (
"context"
diff --git a/vfs/zip.go b/vfs/zip.go
new file mode 100644
index 000000000..d202caa58
--- /dev/null
+++ b/vfs/zip.go
@@ -0,0 +1,73 @@
+package vfs
+
+import (
+ "archive/zip"
+ "context"
+ "fmt"
+ "io"
+ "os"
+
+ "github.com/rclone/rclone/fs"
+)
+
+// CreateZip creates a zip file from a vfs.Dir writing it to w
+func CreateZip(ctx context.Context, dir *Dir, w io.Writer) (err error) {
+ zipWriter := zip.NewWriter(w)
+ defer fs.CheckClose(zipWriter, &err)
+ var walk func(dir *Dir, root string) error
+ walk = func(dir *Dir, root string) error {
+ nodes, err := dir.ReadDirAll()
+ if err != nil {
+ return fmt.Errorf("create zip directory read: %w", err)
+ }
+ for _, node := range nodes {
+ switch e := node.(type) {
+ case *File:
+ in, err := e.Open(os.O_RDONLY)
+ if err != nil {
+ return fmt.Errorf("create zip open file: %w", err)
+ }
+ header := &zip.FileHeader{
+ Name: root + e.Name(),
+ Method: zip.Deflate,
+ Modified: e.ModTime(),
+ }
+ fileWriter, err := zipWriter.CreateHeader(header)
+ if err != nil {
+ fs.CheckClose(in, &err)
+ return fmt.Errorf("create zip file header: %w", err)
+ }
+ _, err = io.Copy(fileWriter, in)
+ if err != nil {
+ fs.CheckClose(in, &err)
+ return fmt.Errorf("create zip copy: %w", err)
+ }
+ fs.CheckClose(in, &err)
+ case *Dir:
+ name := root + e.Path()
+ if name != "" && name[len(name)-1] != '/' {
+ name += "/"
+ }
+ header := &zip.FileHeader{
+ Name: name,
+ Method: zip.Store,
+ Modified: e.ModTime(),
+ }
+ _, err := zipWriter.CreateHeader(header)
+ if err != nil {
+ return fmt.Errorf("create zip directory header: %w", err)
+ }
+ err = walk(e, name)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+ }
+ err = walk(dir, "")
+ if err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/vfs/zip_test.go b/vfs/zip_test.go
new file mode 100644
index 000000000..20bae004e
--- /dev/null
+++ b/vfs/zip_test.go
@@ -0,0 +1,160 @@
+package vfs
+
+import (
+ "archive/zip"
+ "bytes"
+ "context"
+ "crypto/sha256"
+ "fmt"
+ "io"
+ "strings"
+ "testing"
+
+ "github.com/rclone/rclone/fstest"
+ "github.com/rclone/rclone/lib/random"
+ "github.com/stretchr/testify/require"
+)
+
+func readZip(t *testing.T, buf *bytes.Buffer) *zip.Reader {
+ t.Helper()
+ r, err := zip.NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
+ require.NoError(t, err)
+ return r
+}
+
+func mustCreateZip(t *testing.T, d *Dir) *bytes.Buffer {
+ t.Helper()
+ var buf bytes.Buffer
+ require.NoError(t, CreateZip(context.Background(), d, &buf))
+ return &buf
+}
+
+func zipReadFile(t *testing.T, zr *zip.Reader, match func(name string) bool) ([]byte, string) {
+ t.Helper()
+ for _, f := range zr.File {
+ if strings.HasSuffix(f.Name, "/") {
+ continue
+ }
+ if match(f.Name) {
+ rc, err := f.Open()
+ require.NoError(t, err)
+ defer func() { require.NoError(t, rc.Close()) }()
+ b, err := io.ReadAll(rc)
+ require.NoError(t, err)
+ return b, f.Name
+ }
+ }
+ t.Fatalf("zip entry matching predicate not found")
+ return nil, ""
+}
+
+func TestZipManyFiles(t *testing.T) {
+ r, vfs := newTestVFS(t)
+
+ const N = 5
+ want := make(map[string]string, N)
+ items := make([]fstest.Item, 0, N)
+
+ for i := range N {
+ name := fmt.Sprintf("flat/f%03d.txt", i)
+ data := strings.Repeat(fmt.Sprintf("line-%d\n", i), (i%5)+1)
+ it := r.WriteObject(context.Background(), name, data, t1)
+ items = append(items, it)
+ want[name[strings.LastIndex(name, "/")+1:]] = data
+ }
+ r.CheckRemoteItems(t, items...)
+
+ node, err := vfs.Stat("flat")
+ require.NoError(t, err)
+ dir := node.(*Dir)
+
+ buf := mustCreateZip(t, dir)
+ zr := readZip(t, buf)
+
+ // count only file entries (skip dir entries with trailing "/")
+ files := 0
+ for _, f := range zr.File {
+ if !strings.HasSuffix(f.Name, "/") {
+ files++
+ }
+ }
+ require.Equal(t, N, files)
+
+ // validate contents by base name
+ for base, data := range want {
+ got, _ := zipReadFile(t, zr, func(name string) bool { return name == base })
+ require.Equal(t, data, string(got), "mismatch for %s", base)
+ }
+}
+
+func TestZipManySubDirs(t *testing.T) {
+ r, vfs := newTestVFS(t)
+
+ r.WriteObject(context.Background(), "a/top.txt", "top", t1)
+ r.WriteObject(context.Background(), "a/b/mid.txt", "mid", t1)
+ r.WriteObject(context.Background(), "a/b/c/deep.txt", "deep", t1)
+
+ node, err := vfs.Stat("a")
+ require.NoError(t, err)
+ dir := node.(*Dir)
+
+ buf := mustCreateZip(t, dir)
+ zr := readZip(t, buf)
+
+ // paths may include directory prefixes; assert by suffix
+ got, name := zipReadFile(t, zr, func(n string) bool { return strings.HasSuffix(n, "/top.txt") || n == "top.txt" })
+ require.Equal(t, "top", string(got), "bad content for %s", name)
+
+ got, name = zipReadFile(t, zr, func(n string) bool { return strings.HasSuffix(n, "/mid.txt") || n == "mid.txt" })
+ require.Equal(t, "mid", string(got), "bad content for %s", name)
+
+ got, name = zipReadFile(t, zr, func(n string) bool { return strings.HasSuffix(n, "/deep.txt") || n == "deep.txt" })
+ require.Equal(t, "deep", string(got), "bad content for %s", name)
+}
+
+func TestZipLargeFiles(t *testing.T) {
+ r, vfs := newTestVFS(t)
+
+ if strings.HasPrefix(r.Fremote.Name(), "TestChunker") {
+ t.Skip("skipping test as chunker too slow")
+ }
+
+ data := random.String(5 * 1024 * 1024)
+ sum := sha256.Sum256([]byte(data))
+
+ r.WriteObject(context.Background(), "bigdir/big.bin", data, t1)
+
+ node, err := vfs.Stat("bigdir")
+ require.NoError(t, err)
+ dir := node.(*Dir)
+
+ buf := mustCreateZip(t, dir)
+ zr := readZip(t, buf)
+
+ got, _ := zipReadFile(t, zr, func(n string) bool { return n == "big.bin" || strings.HasSuffix(n, "/big.bin") })
+ require.Equal(t, sum, sha256.Sum256(got))
+}
+
+func TestZipDirsInRoot(t *testing.T) {
+ r, vfs := newTestVFS(t)
+
+ r.WriteObject(context.Background(), "dir1/a.txt", "x", t1)
+ r.WriteObject(context.Background(), "dir2/b.txt", "y", t1)
+ r.WriteObject(context.Background(), "dir3/c.txt", "z", t1)
+
+ root, err := vfs.Root()
+ require.NoError(t, err)
+
+ buf := mustCreateZip(t, root)
+ zr := readZip(t, buf)
+
+ // Check each file exists (ignore exact directory-entry names)
+ gx, _ := zipReadFile(t, zr, func(n string) bool { return strings.HasSuffix(n, "/a.txt") })
+ require.Equal(t, "x", string(gx))
+
+ gy, _ := zipReadFile(t, zr, func(n string) bool { return strings.HasSuffix(n, "/b.txt") })
+ require.Equal(t, "y", string(gy))
+
+ gz, _ := zipReadFile(t, zr, func(n string) bool { return strings.HasSuffix(n, "/c.txt") })
+ require.Equal(t, "z", string(gz))
+}