mirror of
https://github.com/rclone/rclone.git
synced 2026-02-25 08:53:37 +00:00
Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
699e4cfb23 | ||
|
|
bc5cad8792 | ||
|
|
9024962fc4 | ||
|
|
aa1f4ace64 | ||
|
|
dd1d750c55 | ||
|
|
5a2564c6e2 | ||
|
|
82cc80cc6f | ||
|
|
7d0a8bf850 | ||
|
|
fd8b28d36d | ||
|
|
c63ecace41 | ||
|
|
0c8c3d8fb9 | ||
|
|
5042f360f0 | ||
|
|
9601dbce87 | ||
|
|
e06f0b0595 | ||
|
|
b2866f0291 | ||
|
|
cf97f250df | ||
|
|
627b763d4b | ||
|
|
f14945f9c1 | ||
|
|
391661fdb4 | ||
|
|
faffd0a6f1 | ||
|
|
6cc3356f8e | ||
|
|
07e76419c9 | ||
|
|
60c4f35b56 | ||
|
|
15a9c0fd36 | ||
|
|
8b85ffbf03 | ||
|
|
26fb659fe4 | ||
|
|
7aa3d8a32f | ||
|
|
b7ebec865b | ||
|
|
a60d09c43d | ||
|
|
14a47937c0 | ||
|
|
64d6916161 | ||
|
|
ae778f1413 | ||
|
|
33859568d6 | ||
|
|
4b3aa5aea0 | ||
|
|
349487bb7f | ||
|
|
b70b2fff16 | ||
|
|
32307e9226 | ||
|
|
2bd6630c2e | ||
|
|
54c2078f25 | ||
|
|
4f284614a4 | ||
|
|
eef0b39a2c | ||
|
|
37f6336636 | ||
|
|
1049f88a1d | ||
|
|
327ca25a4d | ||
|
|
673e24a60f | ||
|
|
43db4c5dc7 | ||
|
|
88b484722a | ||
|
|
ed5bd327c0 | ||
|
|
341ce61a2a | ||
|
|
9abf9d38c0 |
22
.github/workflows/build.yml
vendored
22
.github/workflows/build.yml
vendored
@@ -29,12 +29,12 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
job_name: ['linux', 'linux_386', 'mac_amd64', 'mac_arm64', 'windows', 'other_os', 'go1.24']
|
job_name: ['linux', 'linux_386', 'mac_amd64', 'mac_arm64', 'windows', 'other_os', 'go1.25']
|
||||||
|
|
||||||
include:
|
include:
|
||||||
- job_name: linux
|
- job_name: linux
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
go: '>=1.25.0-rc.1'
|
go: '~1.26.0'
|
||||||
gotags: cmount
|
gotags: cmount
|
||||||
build_flags: '-include "^linux/"'
|
build_flags: '-include "^linux/"'
|
||||||
check: true
|
check: true
|
||||||
@@ -45,14 +45,14 @@ jobs:
|
|||||||
|
|
||||||
- job_name: linux_386
|
- job_name: linux_386
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
go: '>=1.25.0-rc.1'
|
go: '~1.26.0'
|
||||||
goarch: 386
|
goarch: 386
|
||||||
gotags: cmount
|
gotags: cmount
|
||||||
quicktest: true
|
quicktest: true
|
||||||
|
|
||||||
- job_name: mac_amd64
|
- job_name: mac_amd64
|
||||||
os: macos-latest
|
os: macos-latest
|
||||||
go: '>=1.25.0-rc.1'
|
go: '~1.26.0'
|
||||||
gotags: 'cmount'
|
gotags: 'cmount'
|
||||||
build_flags: '-include "^darwin/amd64" -cgo'
|
build_flags: '-include "^darwin/amd64" -cgo'
|
||||||
quicktest: true
|
quicktest: true
|
||||||
@@ -61,14 +61,14 @@ jobs:
|
|||||||
|
|
||||||
- job_name: mac_arm64
|
- job_name: mac_arm64
|
||||||
os: macos-latest
|
os: macos-latest
|
||||||
go: '>=1.25.0-rc.1'
|
go: '~1.26.0'
|
||||||
gotags: 'cmount'
|
gotags: 'cmount'
|
||||||
build_flags: '-include "^darwin/arm64" -cgo -macos-arch arm64 -cgo-cflags=-I/usr/local/include -cgo-ldflags=-L/usr/local/lib'
|
build_flags: '-include "^darwin/arm64" -cgo -macos-arch arm64 -cgo-cflags=-I/usr/local/include -cgo-ldflags=-L/usr/local/lib'
|
||||||
deploy: true
|
deploy: true
|
||||||
|
|
||||||
- job_name: windows
|
- job_name: windows
|
||||||
os: windows-latest
|
os: windows-latest
|
||||||
go: '>=1.25.0-rc.1'
|
go: '~1.26.0'
|
||||||
gotags: cmount
|
gotags: cmount
|
||||||
cgo: '0'
|
cgo: '0'
|
||||||
build_flags: '-include "^windows/"'
|
build_flags: '-include "^windows/"'
|
||||||
@@ -78,14 +78,14 @@ jobs:
|
|||||||
|
|
||||||
- job_name: other_os
|
- job_name: other_os
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
go: '>=1.25.0-rc.1'
|
go: '~1.26.0'
|
||||||
build_flags: '-exclude "^(windows/|darwin/|linux/)"'
|
build_flags: '-exclude "^(windows/|darwin/|linux/)"'
|
||||||
compile_all: true
|
compile_all: true
|
||||||
deploy: true
|
deploy: true
|
||||||
|
|
||||||
- job_name: go1.24
|
- job_name: go1.25
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
go: '1.24'
|
go: '~1.25.7'
|
||||||
quicktest: true
|
quicktest: true
|
||||||
racequicktest: true
|
racequicktest: true
|
||||||
|
|
||||||
@@ -224,7 +224,7 @@ jobs:
|
|||||||
id: setup-go
|
id: setup-go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: '>=1.24.0-rc.1'
|
go-version: '~1.26.0'
|
||||||
check-latest: true
|
check-latest: true
|
||||||
cache: false
|
cache: false
|
||||||
|
|
||||||
@@ -315,7 +315,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: '>=1.25.0-rc.1'
|
go-version: '~1.26.0'
|
||||||
|
|
||||||
- name: Set global environment variables
|
- name: Set global environment variables
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ directories to and from different cloud storage providers.
|
|||||||
- Dropbox [:page_facing_up:](https://rclone.org/dropbox/)
|
- Dropbox [:page_facing_up:](https://rclone.org/dropbox/)
|
||||||
- Enterprise File Fabric [:page_facing_up:](https://rclone.org/filefabric/)
|
- Enterprise File Fabric [:page_facing_up:](https://rclone.org/filefabric/)
|
||||||
- Exaba [:page_facing_up:](https://rclone.org/s3/#exaba)
|
- Exaba [:page_facing_up:](https://rclone.org/s3/#exaba)
|
||||||
|
- Fastly Object Storage [:page_facing_up:](https://rclone.org/s3/#fastly)
|
||||||
- Fastmail Files [:page_facing_up:](https://rclone.org/webdav/#fastmail-files)
|
- Fastmail Files [:page_facing_up:](https://rclone.org/webdav/#fastmail-files)
|
||||||
- FileLu [:page_facing_up:](https://rclone.org/filelu/)
|
- FileLu [:page_facing_up:](https://rclone.org/filelu/)
|
||||||
- Filen [:page_facing_up:](https://rclone.org/filen/)
|
- Filen [:page_facing_up:](https://rclone.org/filen/)
|
||||||
@@ -117,7 +118,6 @@ directories to and from different cloud storage providers.
|
|||||||
- Shade [:page_facing_up:](https://rclone.org/shade/)
|
- Shade [:page_facing_up:](https://rclone.org/shade/)
|
||||||
- SMB / CIFS [:page_facing_up:](https://rclone.org/smb/)
|
- SMB / CIFS [:page_facing_up:](https://rclone.org/smb/)
|
||||||
- Spectra Logic [:page_facing_up:](https://rclone.org/s3/#spectralogic)
|
- 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/)
|
- Storj [:page_facing_up:](https://rclone.org/storj/)
|
||||||
- SugarSync [:page_facing_up:](https://rclone.org/sugarsync/)
|
- SugarSync [:page_facing_up:](https://rclone.org/sugarsync/)
|
||||||
- Synology C2 Object Storage [:page_facing_up:](https://rclone.org/s3/#synology-c2)
|
- Synology C2 Object Storage [:page_facing_up:](https://rclone.org/s3/#synology-c2)
|
||||||
@@ -126,6 +126,7 @@ directories to and from different cloud storage providers.
|
|||||||
- Wasabi [:page_facing_up:](https://rclone.org/s3/#wasabi)
|
- Wasabi [:page_facing_up:](https://rclone.org/s3/#wasabi)
|
||||||
- WebDAV [:page_facing_up:](https://rclone.org/webdav/)
|
- WebDAV [:page_facing_up:](https://rclone.org/webdav/)
|
||||||
- Yandex Disk [:page_facing_up:](https://rclone.org/yandex/)
|
- Yandex Disk [:page_facing_up:](https://rclone.org/yandex/)
|
||||||
|
- Zadara Object Storage [:page_facing_up:](https://rclone.org/s3/#zadara)
|
||||||
- Zoho WorkDrive [:page_facing_up:](https://rclone.org/zoho/)
|
- Zoho WorkDrive [:page_facing_up:](https://rclone.org/zoho/)
|
||||||
- Zata.ai [:page_facing_up:](https://rclone.org/s3/#Zata)
|
- Zata.ai [:page_facing_up:](https://rclone.org/s3/#Zata)
|
||||||
- The local filesystem [:page_facing_up:](https://rclone.org/local/)
|
- The local filesystem [:page_facing_up:](https://rclone.org/local/)
|
||||||
|
|||||||
53
RELEASE.md
53
RELEASE.md
@@ -109,6 +109,59 @@ go run github.com/icholy/gomajor@latest list -major
|
|||||||
|
|
||||||
Expect API breakage when updating major versions.
|
Expect API breakage when updating major versions.
|
||||||
|
|
||||||
|
## Updating Go
|
||||||
|
|
||||||
|
When a new Go stable is released update to it. We support the current
|
||||||
|
stable Go and the previous release which is in line with the rest of
|
||||||
|
the Go ecosystem.
|
||||||
|
|
||||||
|
These files will need editing:
|
||||||
|
|
||||||
|
- `.github/workflows/build.yml` - change current and previous Go versions
|
||||||
|
- `docs/content/install.md` - change minimum Go version required
|
||||||
|
- `fs/versioncheck.go` - update minimum Go version required
|
||||||
|
- `go.mod` - update minimum Go version required
|
||||||
|
|
||||||
|
Check it builds
|
||||||
|
|
||||||
|
- `make GOTAGS=cmount`
|
||||||
|
- `make compiletest`
|
||||||
|
|
||||||
|
Assuming `go1.XX` is current and `go1.YY` is previous version:
|
||||||
|
|
||||||
|
Use `git grep go1.YY` and `git grep go1.YY` to look for opportunities
|
||||||
|
to remove build tags we no longer need.
|
||||||
|
|
||||||
|
Commit with message like this:
|
||||||
|
|
||||||
|
```text
|
||||||
|
build: update to go1.YY and make go1.YY the minimum required version
|
||||||
|
```
|
||||||
|
|
||||||
|
Send to CI and if it passes, merge.
|
||||||
|
|
||||||
|
### gofix
|
||||||
|
|
||||||
|
Updating the minimum required version of Go is a good opportunity to
|
||||||
|
run the `go fix` command to modernize Go usage.
|
||||||
|
|
||||||
|
This needs to be run for all architectures.
|
||||||
|
|
||||||
|
```console
|
||||||
|
GOOS=linux go fix -tags cmount ./...
|
||||||
|
GOOS=freebsd go fix -tags cmount ./...
|
||||||
|
GOOS=windows go fix -tags cmount ./...
|
||||||
|
GOOS=darwin go fix -tags cmount ./...
|
||||||
|
```
|
||||||
|
|
||||||
|
Examine the diff carefully.
|
||||||
|
|
||||||
|
Commit with message
|
||||||
|
|
||||||
|
```text
|
||||||
|
build: modernize Go code with go fix for go1.YY
|
||||||
|
```
|
||||||
|
|
||||||
## Tidy beta
|
## Tidy beta
|
||||||
|
|
||||||
At some point after the release run
|
At some point after the release run
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"maps"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path"
|
"path"
|
||||||
"slices"
|
"slices"
|
||||||
@@ -729,8 +730,8 @@ func parseXMsTags(s string) (map[string]string, error) {
|
|||||||
return map[string]string{}, nil
|
return map[string]string{}, nil
|
||||||
}
|
}
|
||||||
out := make(map[string]string)
|
out := make(map[string]string)
|
||||||
parts := strings.Split(s, ",")
|
parts := strings.SplitSeq(s, ",")
|
||||||
for _, p := range parts {
|
for p := range parts {
|
||||||
p = strings.TrimSpace(p)
|
p = strings.TrimSpace(p)
|
||||||
if p == "" {
|
if p == "" {
|
||||||
continue
|
continue
|
||||||
@@ -893,9 +894,7 @@ func assembleCopyParams(ctx context.Context, f *Fs, src fs.Object, srcProps *blo
|
|||||||
if meta == nil {
|
if meta == nil {
|
||||||
meta = make(map[string]*string, len(userMeta))
|
meta = make(map[string]*string, len(userMeta))
|
||||||
}
|
}
|
||||||
for k, v := range userMeta {
|
maps.Copy(meta, userMeta)
|
||||||
meta[k] = v
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Apply tags if any
|
// Apply tags if any
|
||||||
if len(mappedTags) > 0 {
|
if len(mappedTags) > 0 {
|
||||||
@@ -992,9 +991,7 @@ func (o *Object) applyMappedMetadata(ctx context.Context, src fs.ObjectInfo, ui
|
|||||||
if o.tags == nil {
|
if o.tags == nil {
|
||||||
o.tags = make(map[string]string, len(tags))
|
o.tags = make(map[string]string, len(tags))
|
||||||
}
|
}
|
||||||
for k, v := range tags {
|
maps.Copy(o.tags, tags)
|
||||||
o.tags[k] = v
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if mappedModTime != nil {
|
if mappedModTime != nil {
|
||||||
@@ -1859,9 +1856,7 @@ func (f *Fs) copySinglepart(ctx context.Context, remote, dstContainer, dstPath s
|
|||||||
// Apply tags and post-copy headers only when mapping requested changes
|
// Apply tags and post-copy headers only when mapping requested changes
|
||||||
if len(tags) > 0 {
|
if len(tags) > 0 {
|
||||||
options.BlobTags = make(map[string]string, len(tags))
|
options.BlobTags = make(map[string]string, len(tags))
|
||||||
for k, v := range tags {
|
maps.Copy(options.BlobTags, tags)
|
||||||
options.BlobTags[k] = v
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if hadMapping {
|
if hadMapping {
|
||||||
// Only set metadata explicitly when mapping was requested; otherwise
|
// Only set metadata explicitly when mapping was requested; otherwise
|
||||||
@@ -2062,9 +2057,7 @@ func (o *Object) Metadata(ctx context.Context) (fs.Metadata, error) {
|
|||||||
|
|
||||||
// Merge user metadata (already lower-cased keys)
|
// Merge user metadata (already lower-cased keys)
|
||||||
metadataMu.Lock()
|
metadataMu.Lock()
|
||||||
for k, v := range o.meta {
|
maps.Copy(m, o.meta)
|
||||||
m[k] = v
|
|
||||||
}
|
|
||||||
metadataMu.Unlock()
|
metadataMu.Unlock()
|
||||||
|
|
||||||
return m, nil
|
return m, nil
|
||||||
|
|||||||
@@ -287,13 +287,13 @@ type StartLargeFileRequest struct {
|
|||||||
|
|
||||||
// StartLargeFileResponse is the response to StartLargeFileRequest
|
// StartLargeFileResponse is the response to StartLargeFileRequest
|
||||||
type StartLargeFileResponse struct {
|
type StartLargeFileResponse struct {
|
||||||
ID string `json:"fileId"` // The unique identifier for this version of this file. Used with b2_get_file_info, b2_download_file_by_id, and b2_delete_file_version.
|
ID string `json:"fileId"` // The unique identifier for this version of this file. Used with b2_get_file_info, b2_download_file_by_id, and b2_delete_file_version.
|
||||||
Name string `json:"fileName"` // The name of this file, which can be used with b2_download_file_by_name.
|
Name string `json:"fileName"` // The name of this file, which can be used with b2_download_file_by_name.
|
||||||
AccountID string `json:"accountId"` // The identifier for the account.
|
AccountID string `json:"accountId"` // The identifier for the account.
|
||||||
BucketID string `json:"bucketId"` // The unique ID of the bucket.
|
BucketID string `json:"bucketId"` // The unique ID of the bucket.
|
||||||
ContentType string `json:"contentType"` // The MIME type of the file.
|
ContentType string `json:"contentType"` // The MIME type of the file.
|
||||||
Info map[string]string `json:"fileInfo"` // The custom information that was uploaded with the file. This is a JSON object, holding the name/value pairs that were uploaded with the file.
|
Info map[string]string `json:"fileInfo"` // The custom information that was uploaded with the file. This is a JSON object, holding the name/value pairs that were uploaded with the file.
|
||||||
UploadTimestamp Timestamp `json:"uploadTimestamp,omitempty"` // This is a UTC time when this file was uploaded.
|
UploadTimestamp Timestamp `json:"uploadTimestamp"` // This is a UTC time when this file was uploaded.
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUploadPartURLRequest is passed to b2_get_upload_part_url
|
// GetUploadPartURLRequest is passed to b2_get_upload_part_url
|
||||||
|
|||||||
@@ -173,6 +173,7 @@ type MultiPartCreateRequest struct {
|
|||||||
Extension string `json:"extension"`
|
Extension string `json:"extension"`
|
||||||
ParentID json.Number `json:"parent_id"`
|
ParentID json.Number `json:"parent_id"`
|
||||||
RelativePath string `json:"relativePath"`
|
RelativePath string `json:"relativePath"`
|
||||||
|
WorkspaceID string `json:"workspaceId,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// MultiPartCreateResponse is returned by POST /s3/multipart/create
|
// MultiPartCreateResponse is returned by POST /s3/multipart/create
|
||||||
@@ -235,3 +236,11 @@ type MultiPartAbort struct {
|
|||||||
UploadID string `json:"uploadId"`
|
UploadID string `json:"uploadId"`
|
||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SpaceUsageResponse is returned by GET /user/space-usage
|
||||||
|
type SpaceUsageResponse struct {
|
||||||
|
Used int64 `json:"used"`
|
||||||
|
Available int64 `json:"available"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
SEO any `json:"seo"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -476,8 +476,12 @@ func (f *Fs) createDir(ctx context.Context, pathID, leaf string, modTime time.Ti
|
|||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
var result api.CreateFolderResponse
|
var result api.CreateFolderResponse
|
||||||
opts := rest.Opts{
|
opts := rest.Opts{
|
||||||
Method: "POST",
|
Method: "POST",
|
||||||
Path: "/folders",
|
Path: "/folders",
|
||||||
|
Parameters: url.Values{},
|
||||||
|
}
|
||||||
|
if f.opt.WorkspaceID != "" {
|
||||||
|
opts.Parameters.Set("workspaceId", f.opt.WorkspaceID)
|
||||||
}
|
}
|
||||||
mkdir := api.CreateFolderRequest{
|
mkdir := api.CreateFolderRequest{
|
||||||
Name: f.opt.Enc.FromStandardName(leaf),
|
Name: f.opt.Enc.FromStandardName(leaf),
|
||||||
@@ -779,8 +783,12 @@ func (f *Fs) patch(ctx context.Context, id, attribute string, value string) (ite
|
|||||||
}
|
}
|
||||||
var result api.UpdateItemResponse
|
var result api.UpdateItemResponse
|
||||||
opts := rest.Opts{
|
opts := rest.Opts{
|
||||||
Method: "PUT",
|
Method: "PUT",
|
||||||
Path: "/file-entries/" + id,
|
Path: "/file-entries/" + id,
|
||||||
|
Parameters: url.Values{},
|
||||||
|
}
|
||||||
|
if f.opt.WorkspaceID != "" {
|
||||||
|
opts.Parameters.Set("workspaceId", f.opt.WorkspaceID)
|
||||||
}
|
}
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
resp, err = f.srv.CallJSON(ctx, &opts, &request, &result)
|
resp, err = f.srv.CallJSON(ctx, &opts, &request, &result)
|
||||||
@@ -807,8 +815,12 @@ func (f *Fs) move(ctx context.Context, id, newDirID string) (err error) {
|
|||||||
}
|
}
|
||||||
var result api.MoveResponse
|
var result api.MoveResponse
|
||||||
opts := rest.Opts{
|
opts := rest.Opts{
|
||||||
Method: "POST",
|
Method: "POST",
|
||||||
Path: "/file-entries/move",
|
Path: "/file-entries/move",
|
||||||
|
Parameters: url.Values{},
|
||||||
|
}
|
||||||
|
if f.opt.WorkspaceID != "" {
|
||||||
|
opts.Parameters.Set("workspaceId", f.opt.WorkspaceID)
|
||||||
}
|
}
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
resp, err = f.srv.CallJSON(ctx, &opts, &request, &result)
|
resp, err = f.srv.CallJSON(ctx, &opts, &request, &result)
|
||||||
@@ -945,8 +957,12 @@ func (f *Fs) copy(ctx context.Context, id, newDirID string) (item *api.Item, err
|
|||||||
}
|
}
|
||||||
var result api.CopyResponse
|
var result api.CopyResponse
|
||||||
opts := rest.Opts{
|
opts := rest.Opts{
|
||||||
Method: "POST",
|
Method: "POST",
|
||||||
Path: "/file-entries/duplicate",
|
Path: "/file-entries/duplicate",
|
||||||
|
Parameters: url.Values{},
|
||||||
|
}
|
||||||
|
if f.opt.WorkspaceID != "" {
|
||||||
|
opts.Parameters.Set("workspaceId", f.opt.WorkspaceID)
|
||||||
}
|
}
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
resp, err = f.srv.CallJSON(ctx, &opts, &request, &result)
|
resp, err = f.srv.CallJSON(ctx, &opts, &request, &result)
|
||||||
@@ -1114,6 +1130,7 @@ func (f *Fs) OpenChunkWriter(ctx context.Context, remote string, src fs.ObjectIn
|
|||||||
Extension: strings.TrimPrefix(path.Ext(leaf), `.`),
|
Extension: strings.TrimPrefix(path.Ext(leaf), `.`),
|
||||||
ParentID: json.Number(directoryID),
|
ParentID: json.Number(directoryID),
|
||||||
RelativePath: f.opt.Enc.FromStandardPath(path.Join(f.root, remote)),
|
RelativePath: f.opt.Enc.FromStandardPath(path.Join(f.root, remote)),
|
||||||
|
WorkspaceID: f.opt.WorkspaceID,
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp api.MultiPartCreateResponse
|
var resp api.MultiPartCreateResponse
|
||||||
@@ -1344,6 +1361,37 @@ func (s *drimeChunkWriter) Abort(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// About gets quota information
|
||||||
|
func (f *Fs) About(ctx context.Context) (*fs.Usage, error) {
|
||||||
|
opts := rest.Opts{
|
||||||
|
Method: "GET",
|
||||||
|
Path: "/user/space-usage",
|
||||||
|
Parameters: url.Values{},
|
||||||
|
}
|
||||||
|
if f.opt.WorkspaceID != "" {
|
||||||
|
opts.Parameters.Set("workspaceId", f.opt.WorkspaceID)
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp *http.Response
|
||||||
|
var result api.SpaceUsageResponse
|
||||||
|
var err error
|
||||||
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
|
resp, err = f.srv.CallJSON(ctx, &opts, nil, &result)
|
||||||
|
return shouldRetry(ctx, resp, err)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get Drime Quota: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
usage := &fs.Usage{
|
||||||
|
Total: fs.NewUsageValue(result.Available),
|
||||||
|
Used: fs.NewUsageValue(result.Used),
|
||||||
|
Free: fs.NewUsageValue(result.Available - result.Used),
|
||||||
|
}
|
||||||
|
|
||||||
|
return usage, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
// Fs returns the parent Fs
|
// Fs returns the parent Fs
|
||||||
@@ -1509,6 +1557,7 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
|
|||||||
MultipartParams: url.Values{
|
MultipartParams: url.Values{
|
||||||
"parentId": {directoryID},
|
"parentId": {directoryID},
|
||||||
"relativePath": {encodedLeaf},
|
"relativePath": {encodedLeaf},
|
||||||
|
"workspaceId": {o.fs.opt.WorkspaceID},
|
||||||
},
|
},
|
||||||
MultipartContentName: "file",
|
MultipartContentName: "file",
|
||||||
MultipartFileName: encodedLeaf,
|
MultipartFileName: encodedLeaf,
|
||||||
@@ -1555,6 +1604,7 @@ var (
|
|||||||
_ fs.Mover = (*Fs)(nil)
|
_ fs.Mover = (*Fs)(nil)
|
||||||
_ fs.DirMover = (*Fs)(nil)
|
_ fs.DirMover = (*Fs)(nil)
|
||||||
_ fs.DirCacheFlusher = (*Fs)(nil)
|
_ fs.DirCacheFlusher = (*Fs)(nil)
|
||||||
|
_ fs.Abouter = (*Fs)(nil)
|
||||||
_ fs.OpenChunkWriter = (*Fs)(nil)
|
_ fs.OpenChunkWriter = (*Fs)(nil)
|
||||||
_ fs.Object = (*Object)(nil)
|
_ fs.Object = (*Object)(nil)
|
||||||
_ fs.IDer = (*Object)(nil)
|
_ fs.IDer = (*Object)(nil)
|
||||||
|
|||||||
@@ -3,6 +3,19 @@ package api
|
|||||||
|
|
||||||
import "encoding/json"
|
import "encoding/json"
|
||||||
|
|
||||||
|
// MultipartInitResponse represents the response from multipart/init.
|
||||||
|
type MultipartInitResponse struct {
|
||||||
|
Status int `json:"status"`
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
Result struct {
|
||||||
|
UploadID string `json:"upload_id"`
|
||||||
|
SessID string `json:"sess_id"`
|
||||||
|
Server string `json:"server"`
|
||||||
|
FolderID int64 `json:"folder_id"`
|
||||||
|
ObjectPath string `json:"object_path"`
|
||||||
|
} `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
// CreateFolderResponse represents the response for creating a folder.
|
// CreateFolderResponse represents the response for creating a folder.
|
||||||
type CreateFolderResponse struct {
|
type CreateFolderResponse struct {
|
||||||
Status int `json:"status"`
|
Status int `json:"status"`
|
||||||
|
|||||||
@@ -21,6 +21,11 @@ import (
|
|||||||
"github.com/rclone/rclone/lib/rest"
|
"github.com/rclone/rclone/lib/rest"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultUploadCutoff = fs.SizeSuffix(500 * 1024 * 1024)
|
||||||
|
defaultChunkSize = fs.SizeSuffix(64 * 1024 * 1024)
|
||||||
|
)
|
||||||
|
|
||||||
// Register the backend with Rclone
|
// Register the backend with Rclone
|
||||||
func init() {
|
func init() {
|
||||||
fs.Register(&fs.RegInfo{
|
fs.Register(&fs.RegInfo{
|
||||||
@@ -33,6 +38,17 @@ func init() {
|
|||||||
Required: true,
|
Required: true,
|
||||||
Sensitive: true,
|
Sensitive: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "upload_cutoff",
|
||||||
|
Help: "Cutoff for switching to chunked upload. Any files larger than this will be uploaded in chunks of chunk_size.",
|
||||||
|
Default: defaultUploadCutoff,
|
||||||
|
Advanced: true,
|
||||||
|
}, {
|
||||||
|
Name: "chunk_size",
|
||||||
|
Help: "Chunk size to use for uploading. Used for multipart uploads.",
|
||||||
|
Default: defaultChunkSize,
|
||||||
|
Advanced: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: config.ConfigEncoding,
|
Name: config.ConfigEncoding,
|
||||||
Help: config.ConfigEncodingHelp,
|
Help: config.ConfigEncodingHelp,
|
||||||
@@ -72,8 +88,10 @@ func init() {
|
|||||||
|
|
||||||
// Options defines the configuration for the FileLu backend
|
// Options defines the configuration for the FileLu backend
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Key string `config:"key"`
|
Key string `config:"key"`
|
||||||
Enc encoder.MultiEncoder `config:"encoding"`
|
Enc encoder.MultiEncoder `config:"encoding"`
|
||||||
|
UploadCutoff fs.SizeSuffix `config:"upload_cutoff"`
|
||||||
|
ChunkSize fs.SizeSuffix `config:"chunk_size"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fs represents the FileLu file system
|
// Fs represents the FileLu file system
|
||||||
@@ -189,7 +207,6 @@ func (f *Fs) Purge(ctx context.Context, dir string) error {
|
|||||||
return f.deleteFolder(ctx, fullPath)
|
return f.deleteFolder(ctx, fullPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// List returns a list of files and folders
|
|
||||||
// List returns a list of files and folders for the given directory
|
// List returns a list of files and folders for the given directory
|
||||||
func (f *Fs) List(ctx context.Context, dir string) (fs.DirEntries, error) {
|
func (f *Fs) List(ctx context.Context, dir string) (fs.DirEntries, error) {
|
||||||
// Compose full path for API call
|
// Compose full path for API call
|
||||||
@@ -250,23 +267,11 @@ func (f *Fs) List(ctx context.Context, dir string) (fs.DirEntries, error) {
|
|||||||
|
|
||||||
// Put uploads a file directly to the destination folder in the FileLu storage system.
|
// Put uploads a file directly to the destination folder in the FileLu storage system.
|
||||||
func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
|
func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
|
||||||
if src.Size() == 0 {
|
o := &Object{
|
||||||
return nil, fs.ErrorCantUploadEmptyFiles
|
fs: f,
|
||||||
|
remote: src.Remote(),
|
||||||
}
|
}
|
||||||
|
return o, o.Update(ctx, in, src, options...)
|
||||||
err := f.uploadFile(ctx, in, src.Remote())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
newObject := &Object{
|
|
||||||
fs: f,
|
|
||||||
remote: src.Remote(),
|
|
||||||
size: src.Size(),
|
|
||||||
modTime: src.ModTime(ctx),
|
|
||||||
}
|
|
||||||
fs.Infof(f, "Put: Successfully uploaded new file %q", src.Remote())
|
|
||||||
return newObject, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move moves the file to the specified location
|
// Move moves the file to the specified location
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
package filelu
|
package filelu
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -16,40 +14,82 @@ import (
|
|||||||
"github.com/rclone/rclone/lib/rest"
|
"github.com/rclone/rclone/lib/rest"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// multipartInit starts a new multipart upload and returns server details.
|
||||||
|
func (f *Fs) multipartInit(ctx context.Context, folderPath, filename string) (*api.MultipartInitResponse, error) {
|
||||||
|
opts := rest.Opts{
|
||||||
|
Method: "GET",
|
||||||
|
Path: "/multipart/init",
|
||||||
|
Parameters: url.Values{
|
||||||
|
"key": {f.opt.Key},
|
||||||
|
"filename": {filename},
|
||||||
|
"folder_path": {folderPath},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var result api.MultipartInitResponse
|
||||||
|
|
||||||
|
err := f.pacer.Call(func() (bool, error) {
|
||||||
|
_, err := f.srv.CallJSON(ctx, &opts, nil, &result)
|
||||||
|
return fserrors.ShouldRetry(err), err
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.Status != 200 {
|
||||||
|
return nil, fmt.Errorf("multipart init error: %s", result.Msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// completeMultipart finalizes the multipart upload on the file server.
|
||||||
|
func (f *Fs) completeMultipart(ctx context.Context, server string, uploadID string, sessID string, objectPath string) error {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "POST", server, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("X-RC-Upload-Id", uploadID)
|
||||||
|
req.Header.Set("X-Sess-ID", sessID)
|
||||||
|
req.Header.Set("X-Object-Path", objectPath)
|
||||||
|
|
||||||
|
resp, err := f.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
|
if resp.StatusCode != 202 {
|
||||||
|
body, _ := io.ReadAll(resp.Body)
|
||||||
|
return fmt.Errorf("completeMultipart failed %d: %s", resp.StatusCode, string(body))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// createFolder creates a folder at the specified path.
|
// createFolder creates a folder at the specified path.
|
||||||
func (f *Fs) createFolder(ctx context.Context, dirPath string) (*api.CreateFolderResponse, error) {
|
func (f *Fs) createFolder(ctx context.Context, dirPath string) (*api.CreateFolderResponse, error) {
|
||||||
encodedDir := f.fromStandardPath(dirPath)
|
encodedDir := f.fromStandardPath(dirPath)
|
||||||
apiURL := fmt.Sprintf("%s/folder/create?folder_path=%s&key=%s",
|
|
||||||
f.endpoint,
|
|
||||||
url.QueryEscape(encodedDir),
|
|
||||||
url.QueryEscape(f.opt.Key), // assuming f.opt.Key is the correct field
|
|
||||||
)
|
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", apiURL, nil)
|
opts := rest.Opts{
|
||||||
if err != nil {
|
Method: "GET",
|
||||||
return nil, fmt.Errorf("failed to create request: %w", err)
|
Path: "/folder/create",
|
||||||
|
Parameters: url.Values{
|
||||||
|
"folder_path": {encodedDir},
|
||||||
|
"key": {f.opt.Key},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp *http.Response
|
var result api.CreateFolderResponse
|
||||||
result := api.CreateFolderResponse{}
|
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err := f.pacer.Call(func() (bool, error) {
|
||||||
var innerErr error
|
_, err := f.srv.CallJSON(ctx, &opts, nil, &result)
|
||||||
resp, innerErr = f.client.Do(req)
|
return fserrors.ShouldRetry(err), err
|
||||||
return fserrors.ShouldRetry(innerErr), innerErr
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("request failed: %w", err)
|
return nil, fmt.Errorf("request failed: %w", err)
|
||||||
}
|
}
|
||||||
defer func() {
|
|
||||||
if err := resp.Body.Close(); err != nil {
|
|
||||||
fs.Logf(nil, "Failed to close response body: %v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
err = json.NewDecoder(resp.Body).Decode(&result)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error decoding response: %w", err)
|
|
||||||
}
|
|
||||||
if result.Status != 200 {
|
if result.Status != 200 {
|
||||||
return nil, fmt.Errorf("error: %s", result.Msg)
|
return nil, fmt.Errorf("error: %s", result.Msg)
|
||||||
}
|
}
|
||||||
@@ -61,44 +101,29 @@ func (f *Fs) createFolder(ctx context.Context, dirPath string) (*api.CreateFolde
|
|||||||
// getFolderList List both files and folders in a directory.
|
// getFolderList List both files and folders in a directory.
|
||||||
func (f *Fs) getFolderList(ctx context.Context, path string) (*api.FolderListResponse, error) {
|
func (f *Fs) getFolderList(ctx context.Context, path string) (*api.FolderListResponse, error) {
|
||||||
encodedDir := f.fromStandardPath(path)
|
encodedDir := f.fromStandardPath(path)
|
||||||
apiURL := fmt.Sprintf("%s/folder/list?folder_path=%s&key=%s",
|
|
||||||
f.endpoint,
|
|
||||||
url.QueryEscape(encodedDir),
|
|
||||||
url.QueryEscape(f.opt.Key),
|
|
||||||
)
|
|
||||||
|
|
||||||
var body []byte
|
opts := rest.Opts{
|
||||||
|
Method: "GET",
|
||||||
|
Path: "/folder/list",
|
||||||
|
Parameters: url.Values{
|
||||||
|
"folder_path": {encodedDir},
|
||||||
|
"key": {f.opt.Key},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var response api.FolderListResponse
|
||||||
|
|
||||||
err := f.pacer.Call(func() (bool, error) {
|
err := f.pacer.Call(func() (bool, error) {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", apiURL, nil)
|
_, err := f.srv.CallJSON(ctx, &opts, nil, &response)
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("failed to create request: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := f.client.Do(req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return shouldRetry(err), fmt.Errorf("failed to list directory: %w", err)
|
return shouldRetry(err), fmt.Errorf("failed to list directory: %w", err)
|
||||||
}
|
}
|
||||||
defer func() {
|
return false, nil
|
||||||
if err := resp.Body.Close(); err != nil {
|
|
||||||
fs.Logf(nil, "Failed to close response body: %v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
body, err = io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("error reading response body: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return shouldRetryHTTP(resp.StatusCode), nil
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var response api.FolderListResponse
|
|
||||||
if err := json.NewDecoder(bytes.NewReader(body)).Decode(&response); err != nil {
|
|
||||||
return nil, fmt.Errorf("error decoding response: %w", err)
|
|
||||||
}
|
|
||||||
if response.Status != 200 {
|
if response.Status != 200 {
|
||||||
if strings.Contains(response.Msg, "Folder not found") {
|
if strings.Contains(response.Msg, "Folder not found") {
|
||||||
return nil, fs.ErrorDirNotFound
|
return nil, fs.ErrorDirNotFound
|
||||||
@@ -115,42 +140,28 @@ func (f *Fs) getFolderList(ctx context.Context, path string) (*api.FolderListRes
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &response, nil
|
return &response, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteFolder deletes a folder at the specified path.
|
// deleteFolder deletes a folder at the specified path.
|
||||||
func (f *Fs) deleteFolder(ctx context.Context, fullPath string) error {
|
func (f *Fs) deleteFolder(ctx context.Context, fullPath string) error {
|
||||||
fullPath = f.fromStandardPath(fullPath)
|
fullPath = f.fromStandardPath(fullPath)
|
||||||
deleteURL := fmt.Sprintf("%s/folder/delete?folder_path=%s&key=%s",
|
|
||||||
f.endpoint,
|
opts := rest.Opts{
|
||||||
url.QueryEscape(fullPath),
|
Method: "GET",
|
||||||
url.QueryEscape(f.opt.Key),
|
Path: "/folder/delete",
|
||||||
)
|
Parameters: url.Values{
|
||||||
|
"folder_path": {fullPath},
|
||||||
|
"key": {f.opt.Key},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
delResp := api.DeleteFolderResponse{}
|
delResp := api.DeleteFolderResponse{}
|
||||||
|
|
||||||
err := f.pacer.Call(func() (bool, error) {
|
err := f.pacer.Call(func() (bool, error) {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", deleteURL, nil)
|
_, err := f.srv.CallJSON(ctx, &opts, nil, &delResp)
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
resp, err := f.client.Do(req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fserrors.ShouldRetry(err), err
|
return fserrors.ShouldRetry(err), err
|
||||||
}
|
}
|
||||||
defer func() {
|
|
||||||
if err := resp.Body.Close(); err != nil {
|
|
||||||
fs.Logf(nil, "Failed to close response body: %v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := json.Unmarshal(body, &delResp); err != nil {
|
|
||||||
return false, fmt.Errorf("error decoding delete response: %w", err)
|
|
||||||
}
|
|
||||||
if delResp.Status != 200 {
|
if delResp.Status != 200 {
|
||||||
return false, fmt.Errorf("delete error: %s", delResp.Msg)
|
return false, fmt.Errorf("delete error: %s", delResp.Msg)
|
||||||
}
|
}
|
||||||
@@ -167,38 +178,27 @@ func (f *Fs) deleteFolder(ctx context.Context, fullPath string) error {
|
|||||||
// getDirectLink of file from FileLu to download.
|
// getDirectLink of file from FileLu to download.
|
||||||
func (f *Fs) getDirectLink(ctx context.Context, filePath string) (string, int64, error) {
|
func (f *Fs) getDirectLink(ctx context.Context, filePath string) (string, int64, error) {
|
||||||
filePath = f.fromStandardPath(filePath)
|
filePath = f.fromStandardPath(filePath)
|
||||||
apiURL := fmt.Sprintf("%s/file/direct_link?file_path=%s&key=%s",
|
|
||||||
f.endpoint,
|
opts := rest.Opts{
|
||||||
url.QueryEscape(filePath),
|
Method: "GET",
|
||||||
url.QueryEscape(f.opt.Key),
|
Path: "/file/direct_link",
|
||||||
)
|
Parameters: url.Values{
|
||||||
|
"file_path": {filePath},
|
||||||
|
"key": {f.opt.Key},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
result := api.FileDirectLinkResponse{}
|
result := api.FileDirectLinkResponse{}
|
||||||
err := f.pacer.Call(func() (bool, error) {
|
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", apiURL, nil)
|
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("failed to create request: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := f.client.Do(req)
|
err := f.pacer.Call(func() (bool, error) {
|
||||||
|
_, err := f.srv.CallJSON(ctx, &opts, nil, &result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return shouldRetry(err), fmt.Errorf("failed to fetch direct link: %w", err)
|
return shouldRetry(err), fmt.Errorf("failed to fetch direct link: %w", err)
|
||||||
}
|
}
|
||||||
defer func() {
|
|
||||||
if err := resp.Body.Close(); err != nil {
|
|
||||||
fs.Logf(nil, "Failed to close response body: %v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
|
||||||
return false, fmt.Errorf("error decoding response: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if result.Status != 200 {
|
if result.Status != 200 {
|
||||||
return false, fmt.Errorf("API error: %s", result.Msg)
|
return false, fmt.Errorf("API error: %s", result.Msg)
|
||||||
}
|
}
|
||||||
|
return false, nil
|
||||||
return shouldRetryHTTP(resp.StatusCode), nil
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 0, err
|
return "", 0, err
|
||||||
@@ -210,39 +210,31 @@ func (f *Fs) getDirectLink(ctx context.Context, filePath string) (string, int64,
|
|||||||
// deleteFile deletes a file based on filePath
|
// deleteFile deletes a file based on filePath
|
||||||
func (f *Fs) deleteFile(ctx context.Context, filePath string) error {
|
func (f *Fs) deleteFile(ctx context.Context, filePath string) error {
|
||||||
filePath = f.fromStandardPath(filePath)
|
filePath = f.fromStandardPath(filePath)
|
||||||
apiURL := fmt.Sprintf("%s/file/remove?file_path=%s&key=%s",
|
|
||||||
f.endpoint,
|
opts := rest.Opts{
|
||||||
url.QueryEscape(filePath),
|
Method: "GET",
|
||||||
url.QueryEscape(f.opt.Key),
|
Path: "/file/remove",
|
||||||
)
|
Parameters: url.Values{
|
||||||
|
"file_path": {filePath},
|
||||||
|
"key": {f.opt.Key},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
result := api.DeleteFileResponse{}
|
result := api.DeleteFileResponse{}
|
||||||
|
|
||||||
err := f.pacer.Call(func() (bool, error) {
|
err := f.pacer.Call(func() (bool, error) {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", apiURL, nil)
|
_, err := f.srv.CallJSON(ctx, &opts, nil, &result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("failed to create request: %w", err)
|
return shouldRetry(err), fmt.Errorf("failed to delete file: %w", err)
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := f.client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return shouldRetry(err), fmt.Errorf("failed to fetch direct link: %w", err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := resp.Body.Close(); err != nil {
|
|
||||||
fs.Logf(nil, "Failed to close response body: %v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
|
||||||
return false, fmt.Errorf("error decoding response: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.Status != 200 {
|
if result.Status != 200 {
|
||||||
return false, fmt.Errorf("API error: %s", result.Msg)
|
return false, fmt.Errorf("API error: %s", result.Msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
return shouldRetryHTTP(resp.StatusCode), nil
|
return false, nil
|
||||||
})
|
})
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,46 +267,28 @@ func (f *Fs) getAccountInfo(ctx context.Context) (*api.AccountInfoResponse, erro
|
|||||||
|
|
||||||
// getFileInfo retrieves file information based on file code
|
// getFileInfo retrieves file information based on file code
|
||||||
func (f *Fs) getFileInfo(ctx context.Context, fileCode string) (*api.FileInfoResponse, error) {
|
func (f *Fs) getFileInfo(ctx context.Context, fileCode string) (*api.FileInfoResponse, error) {
|
||||||
u, _ := url.Parse(f.endpoint + "/file/info2")
|
|
||||||
q := u.Query()
|
|
||||||
q.Set("file_code", fileCode) // raw path — Go handles escaping properly here
|
|
||||||
q.Set("key", f.opt.Key)
|
|
||||||
u.RawQuery = q.Encode()
|
|
||||||
|
|
||||||
apiURL := f.endpoint + "/file/info2?" + u.RawQuery
|
opts := rest.Opts{
|
||||||
|
Method: "GET",
|
||||||
|
Path: "/file/info2",
|
||||||
|
Parameters: url.Values{
|
||||||
|
"file_code": {fileCode},
|
||||||
|
"key": {f.opt.Key},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
result := api.FileInfoResponse{}
|
||||||
|
|
||||||
var body []byte
|
|
||||||
err := f.pacer.Call(func() (bool, error) {
|
err := f.pacer.Call(func() (bool, error) {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", apiURL, nil)
|
_, err := f.srv.CallJSON(ctx, &opts, nil, &result)
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("failed to create request: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := f.client.Do(req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return shouldRetry(err), fmt.Errorf("failed to fetch file info: %w", err)
|
return shouldRetry(err), fmt.Errorf("failed to fetch file info: %w", err)
|
||||||
}
|
}
|
||||||
defer func() {
|
return false, nil
|
||||||
if err := resp.Body.Close(); err != nil {
|
|
||||||
fs.Logf(nil, "Failed to close response body: %v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
body, err = io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("error reading response body: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return shouldRetryHTTP(resp.StatusCode), nil
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
result := api.FileInfoResponse{}
|
|
||||||
|
|
||||||
if err := json.NewDecoder(bytes.NewReader(body)).Decode(&result); err != nil {
|
|
||||||
return nil, fmt.Errorf("error decoding response: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if result.Status != 200 || len(result.Result) == 0 {
|
if result.Status != 200 || len(result.Result) == 0 {
|
||||||
return nil, fs.ErrorObjectNotFound
|
return nil, fs.ErrorObjectNotFound
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package filelu
|
package filelu
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
@@ -13,8 +14,108 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
|
"github.com/rclone/rclone/lib/rest"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// multipartUpload uploads a file in fixed-size chunks using the multipart API.
|
||||||
|
func (f *Fs) multipartUpload(ctx context.Context, in io.Reader, remote string) error {
|
||||||
|
dir := path.Dir(remote)
|
||||||
|
if dir == "." {
|
||||||
|
dir = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if dir != "" {
|
||||||
|
_ = f.Mkdir(ctx, dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
folder := strings.Trim(dir, "/")
|
||||||
|
if folder != "" {
|
||||||
|
folder = "/" + folder
|
||||||
|
}
|
||||||
|
|
||||||
|
file := path.Base(remote)
|
||||||
|
|
||||||
|
initResp, err := f.multipartInit(ctx, folder, file)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("multipart init failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadID := initResp.Result.UploadID
|
||||||
|
sessID := initResp.Result.SessID
|
||||||
|
server := initResp.Result.Server
|
||||||
|
objectPath := initResp.Result.ObjectPath
|
||||||
|
|
||||||
|
chunkSize := int(f.opt.ChunkSize)
|
||||||
|
buf := make([]byte, 0, chunkSize)
|
||||||
|
tmp := make([]byte, 1024*1024)
|
||||||
|
partNo := 1
|
||||||
|
|
||||||
|
for {
|
||||||
|
n, errRead := in.Read(tmp)
|
||||||
|
if n > 0 {
|
||||||
|
buf = append(buf, tmp[:n]...)
|
||||||
|
|
||||||
|
// If buffer reached chunkSize, upload a full part
|
||||||
|
if len(buf) >= chunkSize {
|
||||||
|
err = f.uploadPart(ctx, server, uploadID, sessID, objectPath, partNo, bytes.NewReader(buf))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("upload part %d failed: %w", partNo, err)
|
||||||
|
}
|
||||||
|
partNo++
|
||||||
|
buf = buf[:0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if errRead == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if errRead != nil {
|
||||||
|
return fmt.Errorf("read failed: %w", errRead)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(buf) > 0 {
|
||||||
|
err = f.uploadPart(ctx, server, uploadID, sessID, objectPath, partNo, bytes.NewReader(buf))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("upload part %d failed: %w", partNo, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = f.completeMultipart(ctx, server, uploadID, sessID, objectPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("complete multipart failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// uploadPart sends a single multipart chunk to the upload server.
|
||||||
|
func (f *Fs) uploadPart(ctx context.Context, server, uploadID, sessID, objectPath string, partNo int, r io.Reader) error {
|
||||||
|
url := fmt.Sprintf("%s?partNumber=%d&uploadId=%s", server, partNo, uploadID)
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "PUT", url, r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("X-RC-Upload-Id", uploadID)
|
||||||
|
req.Header.Set("X-RC-Part-No", fmt.Sprintf("%d", partNo))
|
||||||
|
req.Header.Set("X-Sess-ID", sessID)
|
||||||
|
req.Header.Set("X-Object-Path", objectPath)
|
||||||
|
|
||||||
|
resp, err := f.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
return fmt.Errorf("uploadPart failed: %s", resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// uploadFile uploads a file to FileLu
|
// uploadFile uploads a file to FileLu
|
||||||
func (f *Fs) uploadFile(ctx context.Context, fileContent io.Reader, fileFullPath string) error {
|
func (f *Fs) uploadFile(ctx context.Context, fileContent io.Reader, fileFullPath string) error {
|
||||||
directory := path.Dir(fileFullPath)
|
directory := path.Dir(fileFullPath)
|
||||||
@@ -68,9 +169,15 @@ func (f *Fs) uploadFile(ctx context.Context, fileContent io.Reader, fileFullPath
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getUploadServer gets the upload server URL with proper key authentication
|
|
||||||
func (f *Fs) getUploadServer(ctx context.Context) (string, string, error) {
|
func (f *Fs) getUploadServer(ctx context.Context) (string, string, error) {
|
||||||
apiURL := fmt.Sprintf("%s/upload/server?key=%s", f.endpoint, url.QueryEscape(f.opt.Key))
|
|
||||||
|
opts := rest.Opts{
|
||||||
|
Method: "GET",
|
||||||
|
Path: "/upload/server",
|
||||||
|
Parameters: url.Values{
|
||||||
|
"key": {f.opt.Key},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
var result struct {
|
var result struct {
|
||||||
Status int `json:"status"`
|
Status int `json:"status"`
|
||||||
@@ -80,36 +187,21 @@ func (f *Fs) getUploadServer(ctx context.Context) (string, string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := f.pacer.Call(func() (bool, error) {
|
err := f.pacer.Call(func() (bool, error) {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", apiURL, nil)
|
_, err := f.srv.CallJSON(ctx, &opts, nil, &result)
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("failed to create request: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := f.client.Do(req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return shouldRetry(err), fmt.Errorf("failed to get upload server: %w", err)
|
return shouldRetry(err), fmt.Errorf("failed to get upload server: %w", err)
|
||||||
}
|
}
|
||||||
defer func() {
|
return false, nil
|
||||||
if err := resp.Body.Close(); err != nil {
|
|
||||||
fs.Logf(nil, "Failed to close response body: %v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
|
||||||
return false, fmt.Errorf("error decoding response: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if result.Status != 200 {
|
|
||||||
return false, fmt.Errorf("API error: %s", result.Msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
return shouldRetryHTTP(resp.StatusCode), nil
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if result.Status != 200 {
|
||||||
|
return "", "", fmt.Errorf("API error: %s", result.Msg)
|
||||||
|
}
|
||||||
|
|
||||||
return result.Result, result.SessID, nil
|
return result.Result, result.SessID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
package filelu
|
package filelu
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -16,6 +14,7 @@ import (
|
|||||||
|
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
"github.com/rclone/rclone/fs/hash"
|
"github.com/rclone/rclone/fs/hash"
|
||||||
|
"github.com/rclone/rclone/lib/rest"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Object describes a FileLu object
|
// Object describes a FileLu object
|
||||||
@@ -88,6 +87,7 @@ func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (io.ReadClo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wrap the response body to handle offset and count
|
||||||
var reader io.ReadCloser
|
var reader io.ReadCloser
|
||||||
err = o.fs.pacer.Call(func() (bool, error) {
|
err = o.fs.pacer.Call(func() (bool, error) {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", directLink, nil)
|
req, err := http.NewRequestWithContext(ctx, "GET", directLink, nil)
|
||||||
@@ -109,22 +109,25 @@ func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (io.ReadClo
|
|||||||
return false, fmt.Errorf("failed to download file: HTTP %d", resp.StatusCode)
|
return false, fmt.Errorf("failed to download file: HTTP %d", resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap the response body to handle offset and count
|
if offset > 0 {
|
||||||
currentContents, err := io.ReadAll(resp.Body)
|
_, err = io.CopyN(io.Discard, resp.Body, offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("failed to read response body: %w", err)
|
_ = resp.Body.Close()
|
||||||
|
return false, fmt.Errorf("failed to skip offset: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if offset > 0 {
|
if count > 0 {
|
||||||
if offset > int64(len(currentContents)) {
|
reader = struct {
|
||||||
return false, fmt.Errorf("offset %d exceeds file size %d", offset, len(currentContents))
|
io.Reader
|
||||||
|
io.Closer
|
||||||
|
}{
|
||||||
|
Reader: io.LimitReader(resp.Body, count),
|
||||||
|
Closer: resp.Body,
|
||||||
}
|
}
|
||||||
currentContents = currentContents[offset:]
|
} else {
|
||||||
|
reader = resp.Body
|
||||||
}
|
}
|
||||||
if count > 0 && count < int64(len(currentContents)) {
|
|
||||||
currentContents = currentContents[:count]
|
|
||||||
}
|
|
||||||
reader = io.NopCloser(bytes.NewReader(currentContents))
|
|
||||||
|
|
||||||
return false, nil
|
return false, nil
|
||||||
})
|
})
|
||||||
@@ -137,15 +140,23 @@ func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (io.ReadClo
|
|||||||
|
|
||||||
// Update updates the object with new data
|
// Update updates the object with new data
|
||||||
func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) error {
|
func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) error {
|
||||||
if src.Size() <= 0 {
|
size := src.Size()
|
||||||
return fs.ErrorCantUploadEmptyFiles
|
|
||||||
|
if size <= int64(o.fs.opt.UploadCutoff) {
|
||||||
|
err := o.fs.uploadFile(ctx, in, o.remote)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fullPath := path.Join(o.fs.root, o.remote)
|
||||||
|
err := o.fs.multipartUpload(ctx, in, fullPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to upload file: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err := o.fs.uploadFile(ctx, in, o.remote)
|
o.size = size
|
||||||
if err != nil {
|
o.modTime = src.ModTime(ctx)
|
||||||
return fmt.Errorf("failed to upload file: %w", err)
|
|
||||||
}
|
|
||||||
o.size = src.Size()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,8 +194,14 @@ func (o *Object) Hash(ctx context.Context, t hash.Type) (string, error) {
|
|||||||
return "", fmt.Errorf("no valid file code found in the remote path")
|
return "", fmt.Errorf("no valid file code found in the remote path")
|
||||||
}
|
}
|
||||||
|
|
||||||
apiURL := fmt.Sprintf("%s/file/info?file_code=%s&key=%s",
|
opts := rest.Opts{
|
||||||
o.fs.endpoint, url.QueryEscape(fileCode), url.QueryEscape(o.fs.opt.Key))
|
Method: "GET",
|
||||||
|
Path: "/file/info",
|
||||||
|
Parameters: url.Values{
|
||||||
|
"file_code": {fileCode},
|
||||||
|
"key": {o.fs.opt.Key},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
var result struct {
|
var result struct {
|
||||||
Status int `json:"status"`
|
Status int `json:"status"`
|
||||||
@@ -193,29 +210,18 @@ func (o *Object) Hash(ctx context.Context, t hash.Type) (string, error) {
|
|||||||
Hash string `json:"hash"`
|
Hash string `json:"hash"`
|
||||||
} `json:"result"`
|
} `json:"result"`
|
||||||
}
|
}
|
||||||
|
|
||||||
err := o.fs.pacer.Call(func() (bool, error) {
|
err := o.fs.pacer.Call(func() (bool, error) {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", apiURL, nil)
|
_, err := o.fs.srv.CallJSON(ctx, &opts, nil, &result)
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
resp, err := o.fs.client.Do(req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return shouldRetry(err), err
|
return shouldRetry(err), err
|
||||||
}
|
}
|
||||||
defer func() {
|
return false, nil
|
||||||
if err := resp.Body.Close(); err != nil {
|
|
||||||
fs.Logf(nil, "Failed to close response body: %v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return shouldRetryHTTP(resp.StatusCode), nil
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.Status != 200 || len(result.Result) == 0 {
|
if result.Status != 200 || len(result.Result) == 0 {
|
||||||
return "", fmt.Errorf("error: unable to fetch hash: %s", result.Msg)
|
return "", fmt.Errorf("error: unable to fetch hash: %s", result.Msg)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -355,19 +355,19 @@ type chunkWriter struct {
|
|||||||
chunkSize int64
|
chunkSize int64
|
||||||
|
|
||||||
chunksLock sync.Mutex
|
chunksLock sync.Mutex
|
||||||
knownChunks map[int][]byte // known chunks to be hashed
|
knownChunks map[int64][]byte // known chunks to be hashed
|
||||||
nextChunkToHash int
|
nextChunkToHash int64
|
||||||
|
|
||||||
sizeLock sync.Mutex
|
sizeLock sync.Mutex
|
||||||
size int64
|
size int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cw *chunkWriter) WriteChunk(ctx context.Context, chunkNumber int, reader io.ReadSeeker) (bytesWritten int64, err error) {
|
func (cw *chunkWriter) WriteChunk(ctx context.Context, chunkNumber int, reader io.ReadSeeker) (bytesWritten int64, err error) {
|
||||||
realChunkNumber := int(int64(chunkNumber) * (cw.chunkSize) / sdk.ChunkSize)
|
realChunkNumber := int64(chunkNumber) * (cw.chunkSize) / sdk.ChunkSize
|
||||||
chunk := make([]byte, sdk.ChunkSize, sdk.ChunkSize+cw.EncryptionKey.Cipher.Overhead())
|
chunk := make([]byte, sdk.ChunkSize, sdk.ChunkSize+cw.EncryptionKey.Cipher.Overhead())
|
||||||
|
|
||||||
totalWritten := int64(0)
|
totalWritten := int64(0)
|
||||||
for sliceStart := 0; sliceStart < int(cw.chunkSize); sliceStart += sdk.ChunkSize {
|
for sliceStart := int64(0); sliceStart < cw.chunkSize; sliceStart += sdk.ChunkSize {
|
||||||
chunk = chunk[:sdk.ChunkSize]
|
chunk = chunk[:sdk.ChunkSize]
|
||||||
chunkRead := 0
|
chunkRead := 0
|
||||||
for {
|
for {
|
||||||
@@ -415,13 +415,13 @@ func (cw *chunkWriter) WriteChunk(ctx context.Context, chunkNumber int, reader i
|
|||||||
return totalWritten, err
|
return totalWritten, err
|
||||||
}
|
}
|
||||||
resp, err := cw.filen.UploadChunk(ctx, &cw.FileUpload, realChunkNumber, chunkReadSlice)
|
resp, err := cw.filen.UploadChunk(ctx, &cw.FileUpload, realChunkNumber, chunkReadSlice)
|
||||||
|
if err != nil {
|
||||||
|
return totalWritten, err
|
||||||
|
}
|
||||||
select { // only care about getting this once
|
select { // only care about getting this once
|
||||||
case cw.bucketAndRegion <- *resp:
|
case cw.bucketAndRegion <- *resp:
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
if err != nil {
|
|
||||||
return totalWritten, err
|
|
||||||
}
|
|
||||||
totalWritten += int64(len(chunkReadSlice))
|
totalWritten += int64(len(chunkReadSlice))
|
||||||
realChunkNumber++
|
realChunkNumber++
|
||||||
}
|
}
|
||||||
@@ -496,7 +496,7 @@ func (f *Fs) OpenChunkWriter(ctx context.Context, remote string, src fs.ObjectIn
|
|||||||
filen: f.filen,
|
filen: f.filen,
|
||||||
chunkSize: chunkSize,
|
chunkSize: chunkSize,
|
||||||
bucketAndRegion: make(chan client.V3UploadResponse, 1),
|
bucketAndRegion: make(chan client.V3UploadResponse, 1),
|
||||||
knownChunks: make(map[int][]byte),
|
knownChunks: make(map[int64][]byte),
|
||||||
nextChunkToHash: 0,
|
nextChunkToHash: 0,
|
||||||
size: 0,
|
size: 0,
|
||||||
}, nil
|
}, nil
|
||||||
@@ -1122,8 +1122,8 @@ func (f *Fs) About(ctx context.Context) (*fs.Usage, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
total := int64(userInfo.MaxStorage)
|
total := userInfo.MaxStorage
|
||||||
used := int64(userInfo.UsedStorage)
|
used := userInfo.UsedStorage
|
||||||
free := total - used
|
free := total - used
|
||||||
return &fs.Usage{
|
return &fs.Usage{
|
||||||
Total: &total,
|
Total: &total,
|
||||||
|
|||||||
@@ -116,8 +116,8 @@ type Date struct {
|
|||||||
type DateFilter struct {
|
type DateFilter struct {
|
||||||
Dates []Date `json:"dates,omitempty"`
|
Dates []Date `json:"dates,omitempty"`
|
||||||
Ranges []struct {
|
Ranges []struct {
|
||||||
StartDate Date `json:"startDate,omitempty"`
|
StartDate Date `json:"startDate"`
|
||||||
EndDate Date `json:"endDate,omitempty"`
|
EndDate Date `json:"endDate"`
|
||||||
} `json:"ranges,omitempty"`
|
} `json:"ranges,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -583,9 +583,7 @@ func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err e
|
|||||||
entriesMu.Unlock()
|
entriesMu.Unlock()
|
||||||
}
|
}
|
||||||
for range checkers {
|
for range checkers {
|
||||||
wg.Add(1)
|
wg.Go(func() {
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
for remote := range in {
|
for remote := range in {
|
||||||
file := &Object{
|
file := &Object{
|
||||||
fs: f,
|
fs: f,
|
||||||
@@ -601,7 +599,7 @@ func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err e
|
|||||||
fs.Debugf(remote, "skipping because of error: %v", err)
|
fs.Debugf(remote, "skipping because of error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
isDir := name[len(name)-1] == '/'
|
isDir := name[len(name)-1] == '/'
|
||||||
|
|||||||
@@ -599,7 +599,7 @@ type UpdateFileInfo struct {
|
|||||||
Signature string `json:"signature,omitempty"`
|
Signature string `json:"signature,omitempty"`
|
||||||
Size int64 `json:"size,omitempty"`
|
Size int64 `json:"size,omitempty"`
|
||||||
WrappingKey string `json:"wrapping_key,omitempty"`
|
WrappingKey string `json:"wrapping_key,omitempty"`
|
||||||
} `json:"data,omitempty"`
|
} `json:"data"`
|
||||||
DocumentID string `json:"document_id"`
|
DocumentID string `json:"document_id"`
|
||||||
FileFlags FileFlags `json:"file_flags"`
|
FileFlags FileFlags `json:"file_flags"`
|
||||||
Mtime int64 `json:"mtime"`
|
Mtime int64 `json:"mtime"`
|
||||||
@@ -849,10 +849,10 @@ type DriveItem struct {
|
|||||||
NumberOfItems int64 `json:"numberOfItems"`
|
NumberOfItems int64 `json:"numberOfItems"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
Extension string `json:"extension,omitempty"`
|
Extension string `json:"extension,omitempty"`
|
||||||
DateModified time.Time `json:"dateModified,omitempty"`
|
DateModified time.Time `json:"dateModified"`
|
||||||
DateChanged time.Time `json:"dateChanged,omitempty"`
|
DateChanged time.Time `json:"dateChanged"`
|
||||||
Size int64 `json:"size,omitempty"`
|
Size int64 `json:"size,omitempty"`
|
||||||
LastOpenTime time.Time `json:"lastOpenTime,omitempty"`
|
LastOpenTime time.Time `json:"lastOpenTime"`
|
||||||
Urls struct {
|
Urls struct {
|
||||||
URLDownload string `json:"url_download"`
|
URLDownload string `json:"url_download"`
|
||||||
} `json:"urls"`
|
} `json:"urls"`
|
||||||
|
|||||||
@@ -13,8 +13,12 @@ import (
|
|||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
internxtauth "github.com/internxt/rclone-adapter/auth"
|
internxtauth "github.com/internxt/rclone-adapter/auth"
|
||||||
internxtconfig "github.com/internxt/rclone-adapter/config"
|
internxtconfig "github.com/internxt/rclone-adapter/config"
|
||||||
|
sdkerrors "github.com/internxt/rclone-adapter/errors"
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
"github.com/rclone/rclone/fs/config/configmap"
|
"github.com/rclone/rclone/fs/config/configmap"
|
||||||
|
"github.com/rclone/rclone/fs/config/obscure"
|
||||||
|
"github.com/rclone/rclone/fs/fserrors"
|
||||||
|
"github.com/rclone/rclone/fs/fshttp"
|
||||||
"github.com/rclone/rclone/lib/oauthutil"
|
"github.com/rclone/rclone/lib/oauthutil"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
@@ -101,7 +105,6 @@ func jwtToOAuth2Token(jwtString string) (*oauth2.Token, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// computeBasicAuthHeader creates the BasicAuthHeader for bucket operations
|
// computeBasicAuthHeader creates the BasicAuthHeader for bucket operations
|
||||||
// Following the pattern from SDK's auth/access.go:96-102
|
|
||||||
func computeBasicAuthHeader(bridgeUser, userID string) string {
|
func computeBasicAuthHeader(bridgeUser, userID string) string {
|
||||||
sum := sha256.Sum256([]byte(userID))
|
sum := sha256.Sum256([]byte(userID))
|
||||||
hexPass := hex.EncodeToString(sum[:])
|
hexPass := hex.EncodeToString(sum[:])
|
||||||
@@ -144,3 +147,100 @@ func refreshJWTToken(ctx context.Context, name string, m configmap.Mapper) error
|
|||||||
fs.Debugf(name, "Token refreshed successfully, new expiry: %v", token.Expiry)
|
fs.Debugf(name, "Token refreshed successfully, new expiry: %v", token.Expiry)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reLogin performs a full re-login using stored email+password credentials.
|
||||||
|
// Returns the AccessResponse on success, or an error if 2FA is required or login fails.
|
||||||
|
func (f *Fs) reLogin(ctx context.Context) (*internxtauth.AccessResponse, error) {
|
||||||
|
password, err := obscure.Reveal(f.opt.Pass)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("couldn't decrypt password: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := internxtconfig.NewDefaultToken("")
|
||||||
|
cfg.HTTPClient = fshttp.NewClient(ctx)
|
||||||
|
|
||||||
|
loginResp, err := internxtauth.Login(ctx, cfg, f.opt.Email)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("re-login check failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if loginResp.TFA {
|
||||||
|
return nil, errors.New("account requires 2FA - please run: rclone config reconnect " + f.name + ":")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := internxtauth.DoLogin(ctx, cfg, f.opt.Email, password, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("re-login failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// refreshOrReLogin tries to refresh the JWT token first; if that fails with 401,
|
||||||
|
// it falls back to a full re-login using stored credentials.
|
||||||
|
func (f *Fs) refreshOrReLogin(ctx context.Context) error {
|
||||||
|
refreshErr := refreshJWTToken(ctx, f.name, f.m)
|
||||||
|
if refreshErr == nil {
|
||||||
|
newToken, err := oauthutil.GetToken(f.name, f.m)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get refreshed token: %w", err)
|
||||||
|
}
|
||||||
|
f.cfg.Token = newToken.AccessToken
|
||||||
|
f.cfg.BasicAuthHeader = computeBasicAuthHeader(f.bridgeUser, f.userID)
|
||||||
|
fs.Debugf(f, "Token refresh succeeded")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var httpErr *sdkerrors.HTTPError
|
||||||
|
if !errors.As(refreshErr, &httpErr) || httpErr.StatusCode() != 401 {
|
||||||
|
if fserrors.ShouldRetry(refreshErr) {
|
||||||
|
return refreshErr
|
||||||
|
}
|
||||||
|
return refreshErr
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.Debugf(f, "Token refresh returned 401, attempting re-login with stored credentials")
|
||||||
|
|
||||||
|
resp, err := f.reLogin(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("re-login fallback failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
oauthToken, err := jwtToOAuth2Token(resp.NewToken)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse re-login token: %w", err)
|
||||||
|
}
|
||||||
|
err = oauthutil.PutToken(f.name, f.m, oauthToken, true)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to save re-login token: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.cfg.Token = oauthToken.AccessToken
|
||||||
|
f.bridgeUser = resp.User.BridgeUser
|
||||||
|
f.userID = resp.User.UserID
|
||||||
|
f.cfg.BasicAuthHeader = computeBasicAuthHeader(f.bridgeUser, f.userID)
|
||||||
|
f.cfg.Bucket = resp.User.Bucket
|
||||||
|
f.cfg.RootFolderID = resp.User.RootFolderID
|
||||||
|
|
||||||
|
fs.Debugf(f, "Re-login succeeded, new token expiry: %v", oauthToken.Expiry)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// reAuthorize is called after getting 401 from the server.
|
||||||
|
// It serializes re-auth attempts and uses a circuit-breaker to avoid infinite loops.
|
||||||
|
func (f *Fs) reAuthorize(ctx context.Context) error {
|
||||||
|
f.authMu.Lock()
|
||||||
|
defer f.authMu.Unlock()
|
||||||
|
|
||||||
|
if f.authFailed {
|
||||||
|
return errors.New("re-authorization permanently failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := f.refreshOrReLogin(ctx)
|
||||||
|
if err != nil {
|
||||||
|
f.authFailed = true
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/internxt/rclone-adapter/auth"
|
"github.com/internxt/rclone-adapter/auth"
|
||||||
@@ -41,16 +42,34 @@ const (
|
|||||||
decayConstant = 2 // bigger for slower decay, exponential
|
decayConstant = 2 // bigger for slower decay, exponential
|
||||||
)
|
)
|
||||||
|
|
||||||
// shouldRetry determines if an error should be retried
|
// shouldRetry determines if an error should be retried.
|
||||||
func shouldRetry(ctx context.Context, err error) (bool, error) {
|
// On 401, it attempts to re-authorize before retrying.
|
||||||
|
// On 429, it honours the server's rate limit retry delay.
|
||||||
|
func (f *Fs) shouldRetry(ctx context.Context, err error) (bool, error) {
|
||||||
if fserrors.ContextError(ctx, &err) {
|
if fserrors.ContextError(ctx, &err) {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
var httpErr *sdkerrors.HTTPError
|
var httpErr *sdkerrors.HTTPError
|
||||||
if errors.As(err, &httpErr) && httpErr.StatusCode() == 401 {
|
if errors.As(err, &httpErr) {
|
||||||
return true, err
|
switch httpErr.StatusCode() {
|
||||||
|
case 401:
|
||||||
|
if !f.authFailed {
|
||||||
|
authErr := f.reAuthorize(ctx)
|
||||||
|
if authErr != nil {
|
||||||
|
fs.Debugf(f, "Re-authorization failed: %v", authErr)
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
case 429:
|
||||||
|
delay := httpErr.RetryAfter()
|
||||||
|
if delay <= 0 {
|
||||||
|
delay = time.Second
|
||||||
|
}
|
||||||
|
return true, pacer.RetryAfterError(err, delay)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fserrors.ShouldRetry(err), err
|
return fserrors.ShouldRetry(err), err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,6 +203,7 @@ type Fs struct {
|
|||||||
name string
|
name string
|
||||||
root string
|
root string
|
||||||
opt Options
|
opt Options
|
||||||
|
m configmap.Mapper
|
||||||
dirCache *dircache.DirCache
|
dirCache *dircache.DirCache
|
||||||
cfg *config.Config
|
cfg *config.Config
|
||||||
features *fs.Features
|
features *fs.Features
|
||||||
@@ -191,6 +211,8 @@ type Fs struct {
|
|||||||
tokenRenewer *oauthutil.Renew
|
tokenRenewer *oauthutil.Renew
|
||||||
bridgeUser string
|
bridgeUser string
|
||||||
userID string
|
userID string
|
||||||
|
authMu sync.Mutex
|
||||||
|
authFailed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Object holds the data for a remote file object
|
// Object holds the data for a remote file object
|
||||||
@@ -263,45 +285,62 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e
|
|||||||
cfg.SkipHashValidation = opt.SkipHashValidation
|
cfg.SkipHashValidation = opt.SkipHashValidation
|
||||||
cfg.HTTPClient = fshttp.NewClient(ctx)
|
cfg.HTTPClient = fshttp.NewClient(ctx)
|
||||||
|
|
||||||
userInfo, err := getUserInfo(ctx, &userInfoConfig{Token: cfg.Token})
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to fetch user info: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg.RootFolderID = userInfo.RootFolderID
|
|
||||||
cfg.Bucket = userInfo.Bucket
|
|
||||||
cfg.BasicAuthHeader = computeBasicAuthHeader(userInfo.BridgeUser, userInfo.UserID)
|
|
||||||
|
|
||||||
f := &Fs{
|
f := &Fs{
|
||||||
name: name,
|
name: name,
|
||||||
root: strings.Trim(root, "/"),
|
root: strings.Trim(root, "/"),
|
||||||
opt: *opt,
|
opt: *opt,
|
||||||
cfg: cfg,
|
m: m,
|
||||||
bridgeUser: userInfo.BridgeUser,
|
cfg: cfg,
|
||||||
userID: userInfo.UserID,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
f.pacer = fs.NewPacer(ctx, pacer.NewDefault(pacer.MinSleep(minSleep), pacer.MaxSleep(maxSleep), pacer.DecayConstant(decayConstant)))
|
f.pacer = fs.NewPacer(ctx, pacer.NewDefault(pacer.MinSleep(minSleep), pacer.MaxSleep(maxSleep), pacer.DecayConstant(decayConstant)))
|
||||||
|
|
||||||
|
var userInfo *userInfo
|
||||||
|
const maxRetries = 3
|
||||||
|
for attempt := 1; attempt <= maxRetries; attempt++ {
|
||||||
|
userInfo, err = getUserInfo(ctx, &userInfoConfig{Token: f.cfg.Token})
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
var httpErr *sdkerrors.HTTPError
|
||||||
|
if errors.As(err, &httpErr) && httpErr.StatusCode() == 401 {
|
||||||
|
fs.Debugf(f, "getUserInfo returned 401, attempting re-auth")
|
||||||
|
authErr := f.refreshOrReLogin(ctx)
|
||||||
|
if authErr != nil {
|
||||||
|
return nil, fmt.Errorf("failed to fetch user info (re-auth failed): %w", err)
|
||||||
|
}
|
||||||
|
userInfo, err = getUserInfo(ctx, &userInfoConfig{Token: f.cfg.Token})
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("failed to fetch user info after re-auth: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fserrors.ShouldRetry(err) && attempt < maxRetries {
|
||||||
|
fs.Debugf(f, "getUserInfo transient error (attempt %d/%d): %v", attempt, maxRetries, err)
|
||||||
|
time.Sleep(time.Duration(attempt) * time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("failed to fetch user info: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.cfg.RootFolderID = userInfo.RootFolderID
|
||||||
|
f.cfg.Bucket = userInfo.Bucket
|
||||||
|
f.cfg.BasicAuthHeader = computeBasicAuthHeader(userInfo.BridgeUser, userInfo.UserID)
|
||||||
|
f.bridgeUser = userInfo.BridgeUser
|
||||||
|
f.userID = userInfo.UserID
|
||||||
|
|
||||||
f.features = (&fs.Features{
|
f.features = (&fs.Features{
|
||||||
CanHaveEmptyDirectories: true,
|
CanHaveEmptyDirectories: true,
|
||||||
}).Fill(ctx, f)
|
}).Fill(ctx, f)
|
||||||
|
|
||||||
if ts != nil {
|
if ts != nil {
|
||||||
f.tokenRenewer = oauthutil.NewRenew(f.String(), ts, func() error {
|
f.tokenRenewer = oauthutil.NewRenew(f.String(), ts, func() error {
|
||||||
err := refreshJWTToken(ctx, name, m)
|
f.authMu.Lock()
|
||||||
if err != nil {
|
defer f.authMu.Unlock()
|
||||||
return err
|
return f.refreshOrReLogin(ctx)
|
||||||
}
|
|
||||||
|
|
||||||
newToken, err := oauthutil.GetToken(name, m)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to get refreshed token: %w", err)
|
|
||||||
}
|
|
||||||
f.cfg.Token = newToken.AccessToken
|
|
||||||
f.cfg.BasicAuthHeader = computeBasicAuthHeader(f.bridgeUser, f.userID)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
})
|
||||||
f.tokenRenewer.Start()
|
f.tokenRenewer.Start()
|
||||||
}
|
}
|
||||||
@@ -312,9 +351,19 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
// Assume it might be a file
|
// Assume it might be a file
|
||||||
newRoot, remote := dircache.SplitPath(f.root)
|
newRoot, remote := dircache.SplitPath(f.root)
|
||||||
tempF := *f
|
tempF := &Fs{
|
||||||
tempF.dirCache = dircache.New(newRoot, f.cfg.RootFolderID, &tempF)
|
name: f.name,
|
||||||
tempF.root = newRoot
|
root: newRoot,
|
||||||
|
opt: f.opt,
|
||||||
|
m: f.m,
|
||||||
|
cfg: f.cfg,
|
||||||
|
features: f.features,
|
||||||
|
pacer: f.pacer,
|
||||||
|
tokenRenewer: f.tokenRenewer,
|
||||||
|
bridgeUser: f.bridgeUser,
|
||||||
|
userID: f.userID,
|
||||||
|
}
|
||||||
|
tempF.dirCache = dircache.New(newRoot, f.cfg.RootFolderID, tempF)
|
||||||
|
|
||||||
err = tempF.dirCache.FindRoot(ctx, false)
|
err = tempF.dirCache.FindRoot(ctx, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -367,7 +416,7 @@ func (f *Fs) Rmdir(ctx context.Context, dir string) error {
|
|||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
var err error
|
var err error
|
||||||
childFolders, err = folders.ListAllFolders(ctx, f.cfg, id)
|
childFolders, err = folders.ListAllFolders(ctx, f.cfg, id)
|
||||||
return shouldRetry(ctx, err)
|
return f.shouldRetry(ctx, err)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -380,7 +429,7 @@ func (f *Fs) Rmdir(ctx context.Context, dir string) error {
|
|||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
var err error
|
var err error
|
||||||
childFiles, err = folders.ListAllFiles(ctx, f.cfg, id)
|
childFiles, err = folders.ListAllFiles(ctx, f.cfg, id)
|
||||||
return shouldRetry(ctx, err)
|
return f.shouldRetry(ctx, err)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -395,7 +444,7 @@ func (f *Fs) Rmdir(ctx context.Context, dir string) error {
|
|||||||
if err != nil && strings.Contains(err.Error(), "404") {
|
if err != nil && strings.Contains(err.Error(), "404") {
|
||||||
return false, fs.ErrorDirNotFound
|
return false, fs.ErrorDirNotFound
|
||||||
}
|
}
|
||||||
return shouldRetry(ctx, err)
|
return f.shouldRetry(ctx, err)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -412,7 +461,7 @@ func (f *Fs) FindLeaf(ctx context.Context, pathID, leaf string) (string, bool, e
|
|||||||
err := f.pacer.Call(func() (bool, error) {
|
err := f.pacer.Call(func() (bool, error) {
|
||||||
var err error
|
var err error
|
||||||
entries, err = folders.ListAllFolders(ctx, f.cfg, pathID)
|
entries, err = folders.ListAllFolders(ctx, f.cfg, pathID)
|
||||||
return shouldRetry(ctx, err)
|
return f.shouldRetry(ctx, err)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", false, err
|
return "", false, err
|
||||||
@@ -437,7 +486,7 @@ func (f *Fs) CreateDir(ctx context.Context, pathID, leaf string) (string, error)
|
|||||||
err := f.pacer.CallNoRetry(func() (bool, error) {
|
err := f.pacer.CallNoRetry(func() (bool, error) {
|
||||||
var err error
|
var err error
|
||||||
resp, err = folders.CreateFolder(ctx, f.cfg, request)
|
resp, err = folders.CreateFolder(ctx, f.cfg, request)
|
||||||
return shouldRetry(ctx, err)
|
return f.shouldRetry(ctx, err)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If folder already exists (409 conflict), try to find it
|
// If folder already exists (409 conflict), try to find it
|
||||||
@@ -525,7 +574,7 @@ func (f *Fs) List(ctx context.Context, dir string) (fs.DirEntries, error) {
|
|||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
var err error
|
var err error
|
||||||
foldersList, err = folders.ListAllFolders(ctx, f.cfg, dirID)
|
foldersList, err = folders.ListAllFolders(ctx, f.cfg, dirID)
|
||||||
return shouldRetry(ctx, err)
|
return f.shouldRetry(ctx, err)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -538,7 +587,7 @@ func (f *Fs) List(ctx context.Context, dir string) (fs.DirEntries, error) {
|
|||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
var err error
|
var err error
|
||||||
filesList, err = folders.ListAllFiles(ctx, f.cfg, dirID)
|
filesList, err = folders.ListAllFiles(ctx, f.cfg, dirID)
|
||||||
return shouldRetry(ctx, err)
|
return f.shouldRetry(ctx, err)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -616,7 +665,7 @@ func (f *Fs) Remove(ctx context.Context, remote string) error {
|
|||||||
}
|
}
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
err := folders.DeleteFolder(ctx, f.cfg, dirID)
|
err := folders.DeleteFolder(ctx, f.cfg, dirID)
|
||||||
return shouldRetry(ctx, err)
|
return f.shouldRetry(ctx, err)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -642,7 +691,7 @@ func (f *Fs) NewObject(ctx context.Context, remote string) (fs.Object, error) {
|
|||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
var err error
|
var err error
|
||||||
files, err = folders.ListAllFiles(ctx, f.cfg, dirID)
|
files, err = folders.ListAllFiles(ctx, f.cfg, dirID)
|
||||||
return shouldRetry(ctx, err)
|
return f.shouldRetry(ctx, err)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -720,7 +769,7 @@ func (f *Fs) About(ctx context.Context) (*fs.Usage, error) {
|
|||||||
err := f.pacer.Call(func() (bool, error) {
|
err := f.pacer.Call(func() (bool, error) {
|
||||||
var err error
|
var err error
|
||||||
internxtLimit, err = users.GetLimit(ctx, f.cfg)
|
internxtLimit, err = users.GetLimit(ctx, f.cfg)
|
||||||
return shouldRetry(ctx, err)
|
return f.shouldRetry(ctx, err)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -730,7 +779,7 @@ func (f *Fs) About(ctx context.Context) (*fs.Usage, error) {
|
|||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
var err error
|
var err error
|
||||||
internxtUsage, err = users.GetUsage(ctx, f.cfg)
|
internxtUsage, err = users.GetUsage(ctx, f.cfg)
|
||||||
return shouldRetry(ctx, err)
|
return f.shouldRetry(ctx, err)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -776,7 +825,7 @@ func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (io.ReadClo
|
|||||||
err := o.f.pacer.Call(func() (bool, error) {
|
err := o.f.pacer.Call(func() (bool, error) {
|
||||||
var err error
|
var err error
|
||||||
stream, err = buckets.DownloadFileStream(ctx, o.f.cfg, o.id, rangeValue)
|
stream, err = buckets.DownloadFileStream(ctx, o.f.cfg, o.id, rangeValue)
|
||||||
return shouldRetry(ctx, err)
|
return o.f.shouldRetry(ctx, err)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -826,7 +875,7 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return shouldRetry(ctx, err)
|
return o.f.shouldRetry(ctx, err)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to rename existing file to backup: %w", err)
|
return fmt.Errorf("failed to rename existing file to backup: %w", err)
|
||||||
@@ -847,7 +896,7 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
|
|||||||
src.Size(),
|
src.Size(),
|
||||||
src.ModTime(ctx),
|
src.ModTime(ctx),
|
||||||
)
|
)
|
||||||
return shouldRetry(ctx, err)
|
return o.f.shouldRetry(ctx, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil && isEmptyFileLimitError(err) {
|
if err != nil && isEmptyFileLimitError(err) {
|
||||||
@@ -885,7 +934,7 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return shouldRetry(ctx, err)
|
return o.f.shouldRetry(ctx, err)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fs.Errorf(o.f, "Failed to delete backup file %s.%s (UUID: %s): %v. This may leave an orphaned backup file.",
|
fs.Errorf(o.f, "Failed to delete backup file %s.%s (UUID: %s): %v. This may leave an orphaned backup file.",
|
||||||
@@ -939,7 +988,7 @@ func (o *Object) recoverFromTimeoutConflict(ctx context.Context, uploadErr error
|
|||||||
checkErr := o.f.pacer.Call(func() (bool, error) {
|
checkErr := o.f.pacer.Call(func() (bool, error) {
|
||||||
existingFile, err := o.f.preUploadCheck(ctx, encodedName, dirID)
|
existingFile, err := o.f.preUploadCheck(ctx, encodedName, dirID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return shouldRetry(ctx, err)
|
return o.f.shouldRetry(ctx, err)
|
||||||
}
|
}
|
||||||
if existingFile != nil {
|
if existingFile != nil {
|
||||||
name := strings.TrimSuffix(baseName, filepath.Ext(baseName))
|
name := strings.TrimSuffix(baseName, filepath.Ext(baseName))
|
||||||
@@ -978,7 +1027,7 @@ func (o *Object) restoreBackupFile(ctx context.Context, backupUUID, origName, or
|
|||||||
_ = o.f.pacer.Call(func() (bool, error) {
|
_ = o.f.pacer.Call(func() (bool, error) {
|
||||||
err := files.RenameFile(ctx, o.f.cfg, backupUUID,
|
err := files.RenameFile(ctx, o.f.cfg, backupUUID,
|
||||||
o.f.opt.Encoding.FromStandardName(origName), origType)
|
o.f.opt.Encoding.FromStandardName(origName), origType)
|
||||||
return shouldRetry(ctx, err)
|
return o.f.shouldRetry(ctx, err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -986,6 +1035,6 @@ func (o *Object) restoreBackupFile(ctx context.Context, backupUUID, origName, or
|
|||||||
func (o *Object) Remove(ctx context.Context) error {
|
func (o *Object) Remove(ctx context.Context) error {
|
||||||
return o.f.pacer.Call(func() (bool, error) {
|
return o.f.pacer.Call(func() (bool, error) {
|
||||||
err := files.DeleteFile(ctx, o.f.cfg, o.uuid)
|
err := files.DeleteFile(ctx, o.f.cfg, o.uuid)
|
||||||
return shouldRetry(ctx, err)
|
return o.f.shouldRetry(ctx, err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,12 +33,10 @@ func TestRemove(t *testing.T) {
|
|||||||
assert.True(t, exists())
|
assert.True(t, exists())
|
||||||
// close the file in the background
|
// close the file in the background
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(1)
|
wg.Go(func() {
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
time.Sleep(250 * time.Millisecond)
|
time.Sleep(250 * time.Millisecond)
|
||||||
require.NoError(t, fd.Close())
|
require.NoError(t, fd.Close())
|
||||||
}()
|
})
|
||||||
// delete the open file
|
// delete the open file
|
||||||
err = remove(name)
|
err = remove(name)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
func remove(name string) (err error) {
|
func remove(name string) (err error) {
|
||||||
const maxTries = 10
|
const maxTries = 10
|
||||||
var sleepTime = 1 * time.Millisecond
|
var sleepTime = 1 * time.Millisecond
|
||||||
for i := 0; i < maxTries; i++ {
|
for i := range maxTries {
|
||||||
err = os.Remove(name)
|
err = os.Remove(name)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ type ListItem struct {
|
|||||||
Count struct {
|
Count struct {
|
||||||
Folders int `json:"folders"`
|
Folders int `json:"folders"`
|
||||||
Files int `json:"files"`
|
Files int `json:"files"`
|
||||||
} `json:"count,omitempty"`
|
} `json:"count"`
|
||||||
Kind string `json:"kind"`
|
Kind string `json:"kind"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
@@ -154,7 +154,7 @@ type FolderInfoResponse struct {
|
|||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Home string `json:"home"`
|
Home string `json:"home"`
|
||||||
List []ListItem `json:"list"`
|
List []ListItem `json:"list"`
|
||||||
} `json:"body,omitempty"`
|
} `json:"body"`
|
||||||
Time int64 `json:"time"`
|
Time int64 `json:"time"`
|
||||||
Status int `json:"status"`
|
Status int `json:"status"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
|
|||||||
@@ -50,12 +50,12 @@ type Identity struct {
|
|||||||
// to represent a set of identities associated with various events for
|
// to represent a set of identities associated with various events for
|
||||||
// an item, such as created by or last modified by.
|
// an item, such as created by or last modified by.
|
||||||
type IdentitySet struct {
|
type IdentitySet struct {
|
||||||
User Identity `json:"user,omitempty"`
|
User Identity `json:"user"`
|
||||||
Application Identity `json:"application,omitempty"`
|
Application Identity `json:"application"`
|
||||||
Device Identity `json:"device,omitempty"`
|
Device Identity `json:"device"`
|
||||||
Group Identity `json:"group,omitempty"`
|
Group Identity `json:"group"`
|
||||||
SiteGroup Identity `json:"siteGroup,omitempty"` // The SharePoint group associated with this action. Optional.
|
SiteGroup Identity `json:"siteGroup"` // The SharePoint group associated with this action. Optional.
|
||||||
SiteUser Identity `json:"siteUser,omitempty"` // The SharePoint user associated with this action. Optional.
|
SiteUser Identity `json:"siteUser"` // The SharePoint user associated with this action. Optional.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Quota groups storage space quota-related information on OneDrive into a single structure.
|
// Quota groups storage space quota-related information on OneDrive into a single structure.
|
||||||
@@ -155,8 +155,8 @@ type FileFacet struct {
|
|||||||
// facet can be used to specify the last modified date or created date
|
// facet can be used to specify the last modified date or created date
|
||||||
// of the item as it was on the local device.
|
// of the item as it was on the local device.
|
||||||
type FileSystemInfoFacet struct {
|
type FileSystemInfoFacet struct {
|
||||||
CreatedDateTime Timestamp `json:"createdDateTime,omitempty"` // The UTC date and time the file was created on a client.
|
CreatedDateTime Timestamp `json:"createdDateTime"` // The UTC date and time the file was created on a client.
|
||||||
LastModifiedDateTime Timestamp `json:"lastModifiedDateTime,omitempty"` // The UTC date and time the file was last modified on a client.
|
LastModifiedDateTime Timestamp `json:"lastModifiedDateTime"` // The UTC date and time the file was last modified on a client.
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeletedFacet indicates that the item on OneDrive has been
|
// DeletedFacet indicates that the item on OneDrive has been
|
||||||
@@ -175,10 +175,10 @@ type PackageFacet struct {
|
|||||||
// SharedType indicates a DriveItem has been shared with others. The resource includes information about how the item is shared.
|
// SharedType indicates a DriveItem has been shared with others. The resource includes information about how the item is shared.
|
||||||
// If a Driveitem has a non-null shared facet, the item has been shared.
|
// If a Driveitem has a non-null shared facet, the item has been shared.
|
||||||
type SharedType struct {
|
type SharedType struct {
|
||||||
Owner IdentitySet `json:"owner,omitempty"` // The identity of the owner of the shared item. Read-only.
|
Owner IdentitySet `json:"owner"` // The identity of the owner of the shared item. Read-only.
|
||||||
Scope string `json:"scope,omitempty"` // Indicates the scope of how the item is shared: anonymous, organization, or users. Read-only.
|
Scope string `json:"scope,omitempty"` // Indicates the scope of how the item is shared: anonymous, organization, or users. Read-only.
|
||||||
SharedBy IdentitySet `json:"sharedBy,omitempty"` // The identity of the user who shared the item. Read-only.
|
SharedBy IdentitySet `json:"sharedBy"` // The identity of the user who shared the item. Read-only.
|
||||||
SharedDateTime Timestamp `json:"sharedDateTime,omitempty"` // The UTC date and time when the item was shared. Read-only.
|
SharedDateTime Timestamp `json:"sharedDateTime"` // The UTC date and time when the item was shared. Read-only.
|
||||||
}
|
}
|
||||||
|
|
||||||
// SharingInvitationType groups invitation-related data items into a single structure.
|
// SharingInvitationType groups invitation-related data items into a single structure.
|
||||||
|
|||||||
@@ -75,8 +75,8 @@ type ErrorDetails struct {
|
|||||||
Type string `json:"@type,omitempty"`
|
Type string `json:"@type,omitempty"`
|
||||||
Reason string `json:"reason,omitempty"`
|
Reason string `json:"reason,omitempty"`
|
||||||
Domain string `json:"domain,omitempty"`
|
Domain string `json:"domain,omitempty"`
|
||||||
Metadata struct{} `json:"metadata,omitempty"` // TODO: undiscovered yet
|
Metadata struct{} `json:"metadata"` // TODO: undiscovered yet
|
||||||
Locale string `json:"locale,omitempty"` // e.g. "en"
|
Locale string `json:"locale,omitempty"` // e.g. "en"
|
||||||
Message string `json:"message,omitempty"`
|
Message string `json:"message,omitempty"`
|
||||||
StackEntries []any `json:"stack_entries,omitempty"` // TODO: undiscovered yet
|
StackEntries []any `json:"stack_entries,omitempty"` // TODO: undiscovered yet
|
||||||
Detail string `json:"detail,omitempty"`
|
Detail string `json:"detail,omitempty"`
|
||||||
@@ -189,8 +189,8 @@ type File struct {
|
|||||||
Apps []*FileApp `json:"apps,omitempty"`
|
Apps []*FileApp `json:"apps,omitempty"`
|
||||||
Audit *FileAudit `json:"audit,omitempty"`
|
Audit *FileAudit `json:"audit,omitempty"`
|
||||||
Collection string `json:"collection,omitempty"` // TODO
|
Collection string `json:"collection,omitempty"` // TODO
|
||||||
CreatedTime Time `json:"created_time,omitempty"`
|
CreatedTime Time `json:"created_time"`
|
||||||
DeleteTime Time `json:"delete_time,omitempty"`
|
DeleteTime Time `json:"delete_time"`
|
||||||
FileCategory string `json:"file_category,omitempty"` // "AUDIO", "VIDEO"
|
FileCategory string `json:"file_category,omitempty"` // "AUDIO", "VIDEO"
|
||||||
FileExtension string `json:"file_extension,omitempty"`
|
FileExtension string `json:"file_extension,omitempty"`
|
||||||
FolderType string `json:"folder_type,omitempty"`
|
FolderType string `json:"folder_type,omitempty"`
|
||||||
@@ -202,7 +202,7 @@ type File struct {
|
|||||||
Md5Checksum string `json:"md5_checksum,omitempty"`
|
Md5Checksum string `json:"md5_checksum,omitempty"`
|
||||||
Medias []*Media `json:"medias,omitempty"`
|
Medias []*Media `json:"medias,omitempty"`
|
||||||
MimeType string `json:"mime_type,omitempty"`
|
MimeType string `json:"mime_type,omitempty"`
|
||||||
ModifiedTime Time `json:"modified_time,omitempty"` // updated when renamed or moved
|
ModifiedTime Time `json:"modified_time"` // updated when renamed or moved
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
OriginalFileIndex int `json:"original_file_index,omitempty"` // TODO
|
OriginalFileIndex int `json:"original_file_index,omitempty"` // TODO
|
||||||
OriginalURL string `json:"original_url,omitempty"`
|
OriginalURL string `json:"original_url,omitempty"`
|
||||||
@@ -221,7 +221,7 @@ type File struct {
|
|||||||
ThumbnailLink string `json:"thumbnail_link,omitempty"`
|
ThumbnailLink string `json:"thumbnail_link,omitempty"`
|
||||||
Trashed bool `json:"trashed,omitempty"`
|
Trashed bool `json:"trashed,omitempty"`
|
||||||
UserID string `json:"user_id,omitempty"`
|
UserID string `json:"user_id,omitempty"`
|
||||||
UserModifiedTime Time `json:"user_modified_time,omitempty"`
|
UserModifiedTime Time `json:"user_modified_time"`
|
||||||
WebContentLink string `json:"web_content_link,omitempty"`
|
WebContentLink string `json:"web_content_link,omitempty"`
|
||||||
Writable bool `json:"writable,omitempty"`
|
Writable bool `json:"writable,omitempty"`
|
||||||
}
|
}
|
||||||
@@ -252,7 +252,7 @@ type Media struct {
|
|||||||
AudioCodec string `json:"audio_codec,omitempty"` // "pcm_bluray", "aac"
|
AudioCodec string `json:"audio_codec,omitempty"` // "pcm_bluray", "aac"
|
||||||
VideoType string `json:"video_type,omitempty"` // "mpegts"
|
VideoType string `json:"video_type,omitempty"` // "mpegts"
|
||||||
HdrType string `json:"hdr_type,omitempty"`
|
HdrType string `json:"hdr_type,omitempty"`
|
||||||
} `json:"video,omitempty"`
|
} `json:"video"`
|
||||||
Link *Link `json:"link,omitempty"`
|
Link *Link `json:"link,omitempty"`
|
||||||
NeedMoreQuota bool `json:"need_more_quota,omitempty"`
|
NeedMoreQuota bool `json:"need_more_quota,omitempty"`
|
||||||
VipTypes []any `json:"vip_types,omitempty"` // TODO maybe list of something?
|
VipTypes []any `json:"vip_types,omitempty"` // TODO maybe list of something?
|
||||||
@@ -290,11 +290,11 @@ type FileApp struct {
|
|||||||
NeedMoreQuota bool `json:"need_more_quota,omitempty"`
|
NeedMoreQuota bool `json:"need_more_quota,omitempty"`
|
||||||
IconLink string `json:"icon_link,omitempty"`
|
IconLink string `json:"icon_link,omitempty"`
|
||||||
IsDefault bool `json:"is_default,omitempty"`
|
IsDefault bool `json:"is_default,omitempty"`
|
||||||
Params struct{} `json:"params,omitempty"` // TODO
|
Params struct{} `json:"params"` // TODO
|
||||||
CategoryIDs []any `json:"category_ids,omitempty"`
|
CategoryIDs []any `json:"category_ids,omitempty"`
|
||||||
AdSceneType int `json:"ad_scene_type,omitempty"`
|
AdSceneType int `json:"ad_scene_type,omitempty"`
|
||||||
Space string `json:"space,omitempty"`
|
Space string `json:"space,omitempty"`
|
||||||
Links struct{} `json:"links,omitempty"` // TODO
|
Links struct{} `json:"links"` // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------
|
// ------------------------------------------------------------
|
||||||
@@ -320,8 +320,8 @@ type Task struct {
|
|||||||
FileName string `json:"file_name,omitempty"`
|
FileName string `json:"file_name,omitempty"`
|
||||||
FileSize string `json:"file_size,omitempty"`
|
FileSize string `json:"file_size,omitempty"`
|
||||||
Message string `json:"message,omitempty"` // e.g. "Saving"
|
Message string `json:"message,omitempty"` // e.g. "Saving"
|
||||||
CreatedTime Time `json:"created_time,omitempty"`
|
CreatedTime Time `json:"created_time"`
|
||||||
UpdatedTime Time `json:"updated_time,omitempty"`
|
UpdatedTime Time `json:"updated_time"`
|
||||||
ThirdTaskID string `json:"third_task_id,omitempty"` // TODO
|
ThirdTaskID string `json:"third_task_id,omitempty"` // TODO
|
||||||
Phase string `json:"phase,omitempty"` // e.g. "PHASE_TYPE_RUNNING"
|
Phase string `json:"phase,omitempty"` // e.g. "PHASE_TYPE_RUNNING"
|
||||||
Progress int `json:"progress,omitempty"`
|
Progress int `json:"progress,omitempty"`
|
||||||
@@ -368,7 +368,7 @@ type ResumableParams struct {
|
|||||||
AccessKeySecret string `json:"access_key_secret,omitempty"`
|
AccessKeySecret string `json:"access_key_secret,omitempty"`
|
||||||
Bucket string `json:"bucket,omitempty"`
|
Bucket string `json:"bucket,omitempty"`
|
||||||
Endpoint string `json:"endpoint,omitempty"`
|
Endpoint string `json:"endpoint,omitempty"`
|
||||||
Expiration Time `json:"expiration,omitempty"`
|
Expiration Time `json:"expiration"`
|
||||||
Key string `json:"key,omitempty"`
|
Key string `json:"key,omitempty"`
|
||||||
SecurityToken string `json:"security_token,omitempty"`
|
SecurityToken string `json:"security_token,omitempty"`
|
||||||
}
|
}
|
||||||
@@ -409,7 +409,7 @@ type About struct {
|
|||||||
Kind string `json:"kind,omitempty"` // "drive#about"
|
Kind string `json:"kind,omitempty"` // "drive#about"
|
||||||
Quota *Quota `json:"quota,omitempty"`
|
Quota *Quota `json:"quota,omitempty"`
|
||||||
ExpiresAt string `json:"expires_at,omitempty"`
|
ExpiresAt string `json:"expires_at,omitempty"`
|
||||||
Quotas struct{} `json:"quotas,omitempty"` // maybe []*Quota?
|
Quotas struct{} `json:"quotas"` // maybe []*Quota?
|
||||||
}
|
}
|
||||||
|
|
||||||
// Quota informs drive quota
|
// Quota informs drive quota
|
||||||
@@ -445,8 +445,8 @@ type User struct {
|
|||||||
PhoneNumber string `json:"phone_number,omitempty"`
|
PhoneNumber string `json:"phone_number,omitempty"`
|
||||||
Password string `json:"password,omitempty"` // "SET" if configured
|
Password string `json:"password,omitempty"` // "SET" if configured
|
||||||
Status string `json:"status,omitempty"` // "ACTIVE"
|
Status string `json:"status,omitempty"` // "ACTIVE"
|
||||||
CreatedAt Time `json:"created_at,omitempty"`
|
CreatedAt Time `json:"created_at"`
|
||||||
PasswordUpdatedAt Time `json:"password_updated_at,omitempty"`
|
PasswordUpdatedAt Time `json:"password_updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserProvider details third-party authentication
|
// UserProvider details third-party authentication
|
||||||
@@ -464,11 +464,11 @@ type VIP struct {
|
|||||||
Message string `json:"message,omitempty"`
|
Message string `json:"message,omitempty"`
|
||||||
RedirectURI string `json:"redirect_uri,omitempty"`
|
RedirectURI string `json:"redirect_uri,omitempty"`
|
||||||
Data struct {
|
Data struct {
|
||||||
Expire Time `json:"expire,omitempty"`
|
Expire Time `json:"expire"`
|
||||||
Status string `json:"status,omitempty"` // "invalid" or "ok"
|
Status string `json:"status,omitempty"` // "invalid" or "ok"
|
||||||
Type string `json:"type,omitempty"` // "novip" or "platinum"
|
Type string `json:"type,omitempty"` // "novip" or "platinum"
|
||||||
UserID string `json:"user_id,omitempty"` // same as User.Sub
|
UserID string `json:"user_id,omitempty"` // same as User.Sub
|
||||||
} `json:"data,omitempty"`
|
} `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecompressResult is a response to RequestDecompress
|
// DecompressResult is a response to RequestDecompress
|
||||||
@@ -538,7 +538,7 @@ type CaptchaToken struct {
|
|||||||
CaptchaToken string `json:"captcha_token"`
|
CaptchaToken string `json:"captcha_token"`
|
||||||
ExpiresIn int64 `json:"expires_in"` // currently 300s
|
ExpiresIn int64 `json:"expires_in"` // currently 300s
|
||||||
// API doesn't provide Expiry field and thus it should be populated from ExpiresIn on retrieval
|
// API doesn't provide Expiry field and thus it should be populated from ExpiresIn on retrieval
|
||||||
Expiry time.Time `json:"expiry,omitempty"`
|
Expiry time.Time `json:"expiry"`
|
||||||
URL string `json:"url,omitempty"` // a link for users to solve captcha
|
URL string `json:"url,omitempty"` // a link for users to solve captcha
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1608,7 +1608,7 @@ func (f *Fs) UserInfo(ctx context.Context) (userInfo map[string]string, err erro
|
|||||||
// ------------------------------------------------------------
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
// add offline download task for url
|
// add offline download task for url
|
||||||
func (f *Fs) addURL(ctx context.Context, url, path string) (*api.Task, error) {
|
func (f *Fs) addURL(ctx context.Context, url, name, path string) (*api.Task, error) {
|
||||||
req := api.RequestNewTask{
|
req := api.RequestNewTask{
|
||||||
Kind: api.KindOfFile,
|
Kind: api.KindOfFile,
|
||||||
UploadType: "UPLOAD_TYPE_URL",
|
UploadType: "UPLOAD_TYPE_URL",
|
||||||
@@ -1617,6 +1617,9 @@ func (f *Fs) addURL(ctx context.Context, url, path string) (*api.Task, error) {
|
|||||||
},
|
},
|
||||||
FolderType: "DOWNLOAD",
|
FolderType: "DOWNLOAD",
|
||||||
}
|
}
|
||||||
|
if name != "" {
|
||||||
|
req.Name = f.opt.Enc.FromStandardName(name)
|
||||||
|
}
|
||||||
if parentID, err := f.dirCache.FindDir(ctx, path, false); err == nil {
|
if parentID, err := f.dirCache.FindDir(ctx, path, false); err == nil {
|
||||||
req.ParentID = parentIDForRequest(parentID)
|
req.ParentID = parentIDForRequest(parentID)
|
||||||
req.FolderType = ""
|
req.FolderType = ""
|
||||||
@@ -1681,14 +1684,18 @@ var commandHelp = []fs.CommandHelp{{
|
|||||||
Short: "Add offline download task for url.",
|
Short: "Add offline download task for url.",
|
||||||
Long: `This command adds offline download task for url.
|
Long: `This command adds offline download task for url.
|
||||||
|
|
||||||
Usage example:
|
Usage examples:
|
||||||
|
|
||||||
` + "```console" + `
|
` + "```console" + `
|
||||||
rclone backend addurl pikpak:dirpath url
|
rclone backend addurl pikpak:dirpath url
|
||||||
|
rclone backend addurl pikpak:dirpath url -o name=custom_filename.zip
|
||||||
` + "```" + `
|
` + "```" + `
|
||||||
|
|
||||||
Downloads will be stored in 'dirpath'. If 'dirpath' is invalid,
|
Downloads will be stored in 'dirpath'. If 'dirpath' is invalid,
|
||||||
download will fallback to default 'My Pack' folder.`,
|
download will fallback to default 'My Pack' folder.`,
|
||||||
|
Opts: map[string]string{
|
||||||
|
"name": "Custom filename for the downloaded file.",
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
Name: "decompress",
|
Name: "decompress",
|
||||||
Short: "Request decompress of a file/files in a folder.",
|
Short: "Request decompress of a file/files in a folder.",
|
||||||
@@ -1732,7 +1739,11 @@ func (f *Fs) Command(ctx context.Context, name string, arg []string, opt map[str
|
|||||||
if len(arg) != 1 {
|
if len(arg) != 1 {
|
||||||
return nil, errors.New("need exactly 1 argument")
|
return nil, errors.New("need exactly 1 argument")
|
||||||
}
|
}
|
||||||
return f.addURL(ctx, arg[0], "")
|
filename := ""
|
||||||
|
if name, ok := opt["name"]; ok {
|
||||||
|
filename = name
|
||||||
|
}
|
||||||
|
return f.addURL(ctx, arg[0], filename, "")
|
||||||
case "decompress":
|
case "decompress":
|
||||||
filename := ""
|
filename := ""
|
||||||
if len(arg) > 0 {
|
if len(arg) > 0 {
|
||||||
|
|||||||
@@ -170,6 +170,7 @@ In `backend/s3/provider/YourProvider.yaml`
|
|||||||
UseUnsignedPayload *bool `yaml:"use_unsigned_payload,omitempty"`
|
UseUnsignedPayload *bool `yaml:"use_unsigned_payload,omitempty"`
|
||||||
UseXID *bool `yaml:"use_x_id,omitempty"`
|
UseXID *bool `yaml:"use_x_id,omitempty"`
|
||||||
SignAcceptEncoding *bool `yaml:"sign_accept_encoding,omitempty"`
|
SignAcceptEncoding *bool `yaml:"sign_accept_encoding,omitempty"`
|
||||||
|
EtagIsNotMD5 *bool `yaml:"etag_is_not_md5,omitempty"`
|
||||||
CopyCutoff *int64 `yaml:"copy_cutoff,omitempty"`
|
CopyCutoff *int64 `yaml:"copy_cutoff,omitempty"`
|
||||||
MaxUploadParts *int `yaml:"max_upload_parts,omitempty"`
|
MaxUploadParts *int `yaml:"max_upload_parts,omitempty"`
|
||||||
MinChunkSize *int64 `yaml:"min_chunk_size,omitempty"`
|
MinChunkSize *int64 `yaml:"min_chunk_size,omitempty"`
|
||||||
|
|||||||
16
backend/s3/provider/Fastly.yaml
Normal file
16
backend/s3/provider/Fastly.yaml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
name: Fastly
|
||||||
|
description: Fastly Object Storage
|
||||||
|
region:
|
||||||
|
us-east: US East
|
||||||
|
us-west: US West
|
||||||
|
eu-central: EU Central
|
||||||
|
endpoint:
|
||||||
|
us-east.object.fastlystorage.app: US East
|
||||||
|
us-west.object.fastlystorage.app: US West
|
||||||
|
eu-central.object.fastlystorage.app: EU Central
|
||||||
|
quirks:
|
||||||
|
force_path_style: true
|
||||||
|
use_already_exists: false
|
||||||
|
use_multipart_etag: false
|
||||||
|
use_multipart_uploads: false
|
||||||
|
etag_is_not_md5: true
|
||||||
@@ -15,7 +15,7 @@ endpoint:
|
|||||||
acl: {}
|
acl: {}
|
||||||
bucket_acl: true
|
bucket_acl: true
|
||||||
quirks:
|
quirks:
|
||||||
list_version: 1
|
list_version: 2
|
||||||
force_path_style: true
|
force_path_style: true
|
||||||
list_url_encode: false
|
list_url_encode: false
|
||||||
use_multipart_etag: false
|
use_multipart_etag: false
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
name: StackPath
|
|
||||||
description: StackPath Object Storage
|
|
||||||
region: {}
|
|
||||||
endpoint:
|
|
||||||
s3.us-east-2.stackpathstorage.com: US East Endpoint
|
|
||||||
s3.us-west-1.stackpathstorage.com: US West Endpoint
|
|
||||||
s3.eu-central-1.stackpathstorage.com: EU Endpoint
|
|
||||||
acl: {}
|
|
||||||
bucket_acl: true
|
|
||||||
quirks:
|
|
||||||
list_version: 1
|
|
||||||
force_path_style: true
|
|
||||||
list_url_encode: false
|
|
||||||
use_already_exists: false
|
|
||||||
9
backend/s3/provider/Zadara.yaml
Normal file
9
backend/s3/provider/Zadara.yaml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
name: Zadara
|
||||||
|
description: Zadara Object Storage
|
||||||
|
region:
|
||||||
|
us-east-1: |-
|
||||||
|
The default region.
|
||||||
|
Leave location constraint empty.
|
||||||
|
endpoint: {}
|
||||||
|
quirks:
|
||||||
|
force_path_style: true
|
||||||
@@ -32,6 +32,7 @@ type Quirks struct {
|
|||||||
UseUnsignedPayload *bool `yaml:"use_unsigned_payload,omitempty"`
|
UseUnsignedPayload *bool `yaml:"use_unsigned_payload,omitempty"`
|
||||||
UseXID *bool `yaml:"use_x_id,omitempty"`
|
UseXID *bool `yaml:"use_x_id,omitempty"`
|
||||||
SignAcceptEncoding *bool `yaml:"sign_accept_encoding,omitempty"`
|
SignAcceptEncoding *bool `yaml:"sign_accept_encoding,omitempty"`
|
||||||
|
EtagIsNotMD5 *bool `yaml:"etag_is_not_md5,omitempty"`
|
||||||
CopyCutoff *int64 `yaml:"copy_cutoff,omitempty"`
|
CopyCutoff *int64 `yaml:"copy_cutoff,omitempty"`
|
||||||
MaxUploadParts *int `yaml:"max_upload_parts,omitempty"`
|
MaxUploadParts *int `yaml:"max_upload_parts,omitempty"`
|
||||||
MinChunkSize *int64 `yaml:"min_chunk_size,omitempty"`
|
MinChunkSize *int64 `yaml:"min_chunk_size,omitempty"`
|
||||||
|
|||||||
386
backend/s3/s3.go
386
backend/s3/s3.go
@@ -828,6 +828,107 @@ use |-vv| to see the debug level logs.
|
|||||||
}, {
|
}, {
|
||||||
Name: "ibm_resource_instance_id",
|
Name: "ibm_resource_instance_id",
|
||||||
Help: "IBM service instance id",
|
Help: "IBM service instance id",
|
||||||
|
}, {
|
||||||
|
Name: "object_lock_mode",
|
||||||
|
Help: `Object Lock mode to apply when uploading or copying objects.
|
||||||
|
|
||||||
|
Set this to apply Object Lock retention mode to objects.
|
||||||
|
If not set, no Object Lock mode is applied (even with --metadata).
|
||||||
|
|
||||||
|
Note: To enable Object Lock retention, you must set BOTH object_lock_mode
|
||||||
|
AND object_lock_retain_until_date. Setting only one has no effect.
|
||||||
|
|
||||||
|
- GOVERNANCE: Set Object Lock mode to GOVERNANCE
|
||||||
|
- COMPLIANCE: Set Object Lock mode to COMPLIANCE
|
||||||
|
- copy: Copy the mode from the source object (requires --metadata)
|
||||||
|
|
||||||
|
See: https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lock.html`,
|
||||||
|
Advanced: true,
|
||||||
|
Examples: []fs.OptionExample{{
|
||||||
|
Value: "GOVERNANCE",
|
||||||
|
Help: "Set Object Lock mode to GOVERNANCE",
|
||||||
|
}, {
|
||||||
|
Value: "COMPLIANCE",
|
||||||
|
Help: "Set Object Lock mode to COMPLIANCE",
|
||||||
|
}, {
|
||||||
|
Value: "copy",
|
||||||
|
Help: "Copy from source object (requires --metadata)",
|
||||||
|
}},
|
||||||
|
}, {
|
||||||
|
Name: "object_lock_retain_until_date",
|
||||||
|
Help: `Object Lock retention until date to apply when uploading or copying objects.
|
||||||
|
|
||||||
|
Set this to apply Object Lock retention date to objects.
|
||||||
|
If not set, no retention date is applied (even with --metadata).
|
||||||
|
|
||||||
|
Note: To enable Object Lock retention, you must set BOTH object_lock_mode
|
||||||
|
AND object_lock_retain_until_date. Setting only one has no effect.
|
||||||
|
|
||||||
|
Accepts:
|
||||||
|
- RFC 3339 format: 2030-01-02T15:04:05Z
|
||||||
|
- Duration from now: 365d, 1y, 6M (days, years, months)
|
||||||
|
- copy: Copy the date from the source object (requires --metadata)
|
||||||
|
|
||||||
|
See: https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lock.html`,
|
||||||
|
Advanced: true,
|
||||||
|
Examples: []fs.OptionExample{{
|
||||||
|
Value: "copy",
|
||||||
|
Help: "Copy from source object (requires --metadata)",
|
||||||
|
}, {
|
||||||
|
Value: "2030-01-01T00:00:00Z",
|
||||||
|
Help: "Set specific date (RFC 3339 format)",
|
||||||
|
}, {
|
||||||
|
Value: "365d",
|
||||||
|
Help: "Set retention for 365 days from now",
|
||||||
|
}, {
|
||||||
|
Value: "1y",
|
||||||
|
Help: "Set retention for 1 year from now",
|
||||||
|
}},
|
||||||
|
}, {
|
||||||
|
Name: "object_lock_legal_hold_status",
|
||||||
|
Help: `Object Lock legal hold status to apply when uploading or copying objects.
|
||||||
|
|
||||||
|
Set this to apply Object Lock legal hold to objects.
|
||||||
|
If not set, no legal hold is applied (even with --metadata).
|
||||||
|
|
||||||
|
Note: Legal hold is independent of retention and can be set separately.
|
||||||
|
|
||||||
|
- ON: Enable legal hold
|
||||||
|
- OFF: Disable legal hold
|
||||||
|
- copy: Copy the legal hold status from the source object (requires --metadata)
|
||||||
|
|
||||||
|
See: https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lock.html`,
|
||||||
|
Advanced: true,
|
||||||
|
Examples: []fs.OptionExample{{
|
||||||
|
Value: "ON",
|
||||||
|
Help: "Enable legal hold",
|
||||||
|
}, {
|
||||||
|
Value: "OFF",
|
||||||
|
Help: "Disable legal hold",
|
||||||
|
}, {
|
||||||
|
Value: "copy",
|
||||||
|
Help: "Copy from source object (requires --metadata)",
|
||||||
|
}},
|
||||||
|
}, {
|
||||||
|
Name: "bypass_governance_retention",
|
||||||
|
Help: `Allow deleting or modifying objects locked with GOVERNANCE mode.`,
|
||||||
|
Default: false,
|
||||||
|
Advanced: true,
|
||||||
|
}, {
|
||||||
|
Name: "bucket_object_lock_enabled",
|
||||||
|
Help: `Enable Object Lock when creating new buckets.`,
|
||||||
|
Default: false,
|
||||||
|
}, {
|
||||||
|
Name: "object_lock_set_after_upload",
|
||||||
|
Help: `Set Object Lock via separate API calls after upload.
|
||||||
|
|
||||||
|
Use this for S3-compatible providers that don't support setting Object Lock
|
||||||
|
headers during PUT operations. When enabled, Object Lock is set via separate
|
||||||
|
PutObjectRetention and PutObjectLegalHold API calls after the upload completes.
|
||||||
|
|
||||||
|
This adds extra API calls per object, so only enable if your provider requires it.`,
|
||||||
|
Default: false,
|
||||||
|
Advanced: true,
|
||||||
},
|
},
|
||||||
}}))
|
}}))
|
||||||
}
|
}
|
||||||
@@ -922,6 +1023,21 @@ var systemMetadataInfo = map[string]fs.MetadataHelp{
|
|||||||
Example: "2006-01-02T15:04:05.999999999Z07:00",
|
Example: "2006-01-02T15:04:05.999999999Z07:00",
|
||||||
ReadOnly: true,
|
ReadOnly: true,
|
||||||
},
|
},
|
||||||
|
"object-lock-mode": {
|
||||||
|
Help: "Object Lock mode: GOVERNANCE or COMPLIANCE",
|
||||||
|
Type: "string",
|
||||||
|
Example: "GOVERNANCE",
|
||||||
|
},
|
||||||
|
"object-lock-retain-until-date": {
|
||||||
|
Help: "Object Lock retention until date",
|
||||||
|
Type: "RFC 3339",
|
||||||
|
Example: "2030-01-02T15:04:05Z",
|
||||||
|
},
|
||||||
|
"object-lock-legal-hold-status": {
|
||||||
|
Help: "Object Lock legal hold status: ON or OFF",
|
||||||
|
Type: "string",
|
||||||
|
Example: "OFF",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options defines the configuration for this backend
|
// Options defines the configuration for this backend
|
||||||
@@ -992,6 +1108,12 @@ type Options struct {
|
|||||||
IBMInstanceID string `config:"ibm_resource_instance_id"`
|
IBMInstanceID string `config:"ibm_resource_instance_id"`
|
||||||
UseXID fs.Tristate `config:"use_x_id"`
|
UseXID fs.Tristate `config:"use_x_id"`
|
||||||
SignAcceptEncoding fs.Tristate `config:"sign_accept_encoding"`
|
SignAcceptEncoding fs.Tristate `config:"sign_accept_encoding"`
|
||||||
|
ObjectLockMode string `config:"object_lock_mode"`
|
||||||
|
ObjectLockRetainUntilDate string `config:"object_lock_retain_until_date"`
|
||||||
|
ObjectLockLegalHoldStatus string `config:"object_lock_legal_hold_status"`
|
||||||
|
BypassGovernanceRetention bool `config:"bypass_governance_retention"`
|
||||||
|
BucketObjectLockEnabled bool `config:"bucket_object_lock_enabled"`
|
||||||
|
ObjectLockSetAfterUpload bool `config:"object_lock_set_after_upload"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fs represents a remote s3 server
|
// Fs represents a remote s3 server
|
||||||
@@ -1036,6 +1158,11 @@ type Object struct {
|
|||||||
contentDisposition *string // Content-Disposition: header
|
contentDisposition *string // Content-Disposition: header
|
||||||
contentEncoding *string // Content-Encoding: header
|
contentEncoding *string // Content-Encoding: header
|
||||||
contentLanguage *string // Content-Language: header
|
contentLanguage *string // Content-Language: header
|
||||||
|
|
||||||
|
// Object Lock metadata
|
||||||
|
objectLockMode *string // Object Lock mode: GOVERNANCE or COMPLIANCE
|
||||||
|
objectLockRetainUntilDate *time.Time // Object Lock retention until date
|
||||||
|
objectLockLegalHoldStatus *string // Object Lock legal hold: ON or OFF
|
||||||
}
|
}
|
||||||
|
|
||||||
// safely dereference the pointer, returning a zero T if nil
|
// safely dereference the pointer, returning a zero T if nil
|
||||||
@@ -1056,6 +1183,21 @@ func getHTTPStatusCode(err error) int {
|
|||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseRetainUntilDate parses a retain until date from a string.
|
||||||
|
// It accepts RFC 3339 format or duration strings like "365d", "1y", "6m".
|
||||||
|
func parseRetainUntilDate(s string) (time.Time, error) {
|
||||||
|
// First try RFC 3339 format
|
||||||
|
if t, err := time.Parse(time.RFC3339, s); err == nil {
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
// Try as a duration from now
|
||||||
|
d, err := fs.ParseDuration(s)
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, fmt.Errorf("can't parse %q as RFC 3339 date or duration: %w", s, err)
|
||||||
|
}
|
||||||
|
return time.Now().Add(d), nil
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
// Name of the remote (as passed into NewFs)
|
// Name of the remote (as passed into NewFs)
|
||||||
@@ -1664,6 +1806,10 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e
|
|||||||
// MD5 digest of their object data.
|
// MD5 digest of their object data.
|
||||||
f.etagIsNotMD5 = true
|
f.etagIsNotMD5 = true
|
||||||
}
|
}
|
||||||
|
if provider.Quirks.EtagIsNotMD5 != nil && *provider.Quirks.EtagIsNotMD5 {
|
||||||
|
// Provider always returns ETags that are not MD5 (e.g., mandatory encryption)
|
||||||
|
f.etagIsNotMD5 = true
|
||||||
|
}
|
||||||
if opt.DirectoryBucket {
|
if opt.DirectoryBucket {
|
||||||
// Objects uploaded to directory buckets appear to have random ETags
|
// Objects uploaded to directory buckets appear to have random ETags
|
||||||
//
|
//
|
||||||
@@ -1688,6 +1834,9 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e
|
|||||||
if opt.Provider == "AWS" {
|
if opt.Provider == "AWS" {
|
||||||
f.features.DoubleSlash = true
|
f.features.DoubleSlash = true
|
||||||
}
|
}
|
||||||
|
if opt.Provider == "Fastly" {
|
||||||
|
f.features.Copy = nil
|
||||||
|
}
|
||||||
if opt.Provider == "Rabata" {
|
if opt.Provider == "Rabata" {
|
||||||
f.features.Copy = nil
|
f.features.Copy = nil
|
||||||
}
|
}
|
||||||
@@ -2642,8 +2791,9 @@ func (f *Fs) makeBucket(ctx context.Context, bucket string) error {
|
|||||||
}
|
}
|
||||||
return f.cache.Create(bucket, func() error {
|
return f.cache.Create(bucket, func() error {
|
||||||
req := s3.CreateBucketInput{
|
req := s3.CreateBucketInput{
|
||||||
Bucket: &bucket,
|
Bucket: &bucket,
|
||||||
ACL: types.BucketCannedACL(f.opt.BucketACL),
|
ACL: types.BucketCannedACL(f.opt.BucketACL),
|
||||||
|
ObjectLockEnabledForBucket: &f.opt.BucketObjectLockEnabled,
|
||||||
}
|
}
|
||||||
if f.opt.LocationConstraint != "" {
|
if f.opt.LocationConstraint != "" {
|
||||||
req.CreateBucketConfiguration = &types.CreateBucketConfiguration{
|
req.CreateBucketConfiguration = &types.CreateBucketConfiguration{
|
||||||
@@ -2762,6 +2912,24 @@ func (f *Fs) copy(ctx context.Context, req *s3.CopyObjectInput, dstBucket, dstPa
|
|||||||
req.StorageClass = types.StorageClass(f.opt.StorageClass)
|
req.StorageClass = types.StorageClass(f.opt.StorageClass)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply Object Lock options via headers (unless ObjectLockSetAfterUpload is set)
|
||||||
|
// "copy" means: keep the value from source (passed via req from prepareUpload/setFrom functions)
|
||||||
|
if !f.opt.ObjectLockSetAfterUpload {
|
||||||
|
if f.opt.ObjectLockMode != "" && !strings.EqualFold(f.opt.ObjectLockMode, "copy") {
|
||||||
|
req.ObjectLockMode = types.ObjectLockMode(strings.ToUpper(f.opt.ObjectLockMode))
|
||||||
|
}
|
||||||
|
if f.opt.ObjectLockRetainUntilDate != "" && !strings.EqualFold(f.opt.ObjectLockRetainUntilDate, "copy") {
|
||||||
|
retainDate, err := parseRetainUntilDate(f.opt.ObjectLockRetainUntilDate)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid object_lock_retain_until_date: %w", err)
|
||||||
|
}
|
||||||
|
req.ObjectLockRetainUntilDate = &retainDate
|
||||||
|
}
|
||||||
|
if f.opt.ObjectLockLegalHoldStatus != "" && !strings.EqualFold(f.opt.ObjectLockLegalHoldStatus, "copy") {
|
||||||
|
req.ObjectLockLegalHoldStatus = types.ObjectLockLegalHoldStatus(strings.ToUpper(f.opt.ObjectLockLegalHoldStatus))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if src.bytes >= int64(f.opt.CopyCutoff) {
|
if src.bytes >= int64(f.opt.CopyCutoff) {
|
||||||
return f.copyMultipart(ctx, req, dstBucket, dstPath, srcBucket, srcPath, src)
|
return f.copyMultipart(ctx, req, dstBucket, dstPath, srcBucket, srcPath, src)
|
||||||
}
|
}
|
||||||
@@ -2944,7 +3112,15 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object,
|
|||||||
}
|
}
|
||||||
|
|
||||||
setFrom_s3CopyObjectInput_s3PutObjectInput(&req, ui.req)
|
setFrom_s3CopyObjectInput_s3PutObjectInput(&req, ui.req)
|
||||||
if ci.Metadata {
|
// Use REPLACE directive if metadata is being modified, otherwise S3 ignores our values
|
||||||
|
// This is needed when:
|
||||||
|
// 1. --metadata flag is set
|
||||||
|
// 2. Any Object Lock option is set (to override or explicitly copy)
|
||||||
|
needsReplace := ci.Metadata ||
|
||||||
|
f.opt.ObjectLockMode != "" ||
|
||||||
|
f.opt.ObjectLockRetainUntilDate != "" ||
|
||||||
|
f.opt.ObjectLockLegalHoldStatus != ""
|
||||||
|
if needsReplace {
|
||||||
req.MetadataDirective = types.MetadataDirectiveReplace
|
req.MetadataDirective = types.MetadataDirectiveReplace
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2952,7 +3128,21 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return f.NewObject(ctx, remote)
|
dstObj, err := f.NewObject(ctx, remote)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Object Lock via separate API calls if requested
|
||||||
|
if f.opt.ObjectLockSetAfterUpload {
|
||||||
|
if dstObject, ok := dstObj.(*Object); ok {
|
||||||
|
if err := dstObject.setObjectLockAfterUpload(ctx, srcObj); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dstObj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hashes returns the supported hash sets.
|
// Hashes returns the supported hash sets.
|
||||||
@@ -3831,6 +4021,19 @@ func (o *Object) setMetaData(resp *s3.HeadObjectOutput) {
|
|||||||
o.contentEncoding = stringClonePointer(removeAWSChunked(resp.ContentEncoding))
|
o.contentEncoding = stringClonePointer(removeAWSChunked(resp.ContentEncoding))
|
||||||
o.contentLanguage = stringClonePointer(resp.ContentLanguage)
|
o.contentLanguage = stringClonePointer(resp.ContentLanguage)
|
||||||
|
|
||||||
|
// Set Object Lock metadata
|
||||||
|
if resp.ObjectLockMode != "" {
|
||||||
|
mode := string(resp.ObjectLockMode)
|
||||||
|
o.objectLockMode = &mode
|
||||||
|
}
|
||||||
|
if resp.ObjectLockRetainUntilDate != nil {
|
||||||
|
o.objectLockRetainUntilDate = resp.ObjectLockRetainUntilDate
|
||||||
|
}
|
||||||
|
if resp.ObjectLockLegalHoldStatus != "" {
|
||||||
|
status := string(resp.ObjectLockLegalHoldStatus)
|
||||||
|
o.objectLockLegalHoldStatus = &status
|
||||||
|
}
|
||||||
|
|
||||||
// If decompressing then size and md5sum are unknown
|
// If decompressing then size and md5sum are unknown
|
||||||
if o.fs.opt.Decompress && deref(o.contentEncoding) == "gzip" {
|
if o.fs.opt.Decompress && deref(o.contentEncoding) == "gzip" {
|
||||||
o.bytes = -1
|
o.bytes = -1
|
||||||
@@ -4549,6 +4752,26 @@ func (o *Object) prepareUpload(ctx context.Context, src fs.ObjectInfo, options [
|
|||||||
case "btime":
|
case "btime":
|
||||||
// write as metadata since we can't set it
|
// write as metadata since we can't set it
|
||||||
ui.req.Metadata[k] = v
|
ui.req.Metadata[k] = v
|
||||||
|
case "object-lock-mode":
|
||||||
|
// Only apply if option is set to "copy" and not using after-upload API
|
||||||
|
if strings.EqualFold(o.fs.opt.ObjectLockMode, "copy") && !o.fs.opt.ObjectLockSetAfterUpload {
|
||||||
|
ui.req.ObjectLockMode = types.ObjectLockMode(v)
|
||||||
|
}
|
||||||
|
case "object-lock-retain-until-date":
|
||||||
|
// Only apply if option is set to "copy" and not using after-upload API
|
||||||
|
if strings.EqualFold(o.fs.opt.ObjectLockRetainUntilDate, "copy") && !o.fs.opt.ObjectLockSetAfterUpload {
|
||||||
|
retainDate, err := time.Parse(time.RFC3339, v)
|
||||||
|
if err != nil {
|
||||||
|
fs.Debugf(o, "failed to parse object-lock-retain-until-date %q: %v", v, err)
|
||||||
|
} else {
|
||||||
|
ui.req.ObjectLockRetainUntilDate = &retainDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "object-lock-legal-hold-status":
|
||||||
|
// Only apply if option is set to "copy" and not using after-upload API
|
||||||
|
if strings.EqualFold(o.fs.opt.ObjectLockLegalHoldStatus, "copy") && !o.fs.opt.ObjectLockSetAfterUpload {
|
||||||
|
ui.req.ObjectLockLegalHoldStatus = types.ObjectLockLegalHoldStatus(v)
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
ui.req.Metadata[k] = v
|
ui.req.Metadata[k] = v
|
||||||
}
|
}
|
||||||
@@ -4614,6 +4837,25 @@ func (o *Object) prepareUpload(ctx context.Context, src fs.ObjectInfo, options [
|
|||||||
if o.fs.opt.StorageClass != "" {
|
if o.fs.opt.StorageClass != "" {
|
||||||
ui.req.StorageClass = types.StorageClass(o.fs.opt.StorageClass)
|
ui.req.StorageClass = types.StorageClass(o.fs.opt.StorageClass)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply Object Lock options via headers (unless ObjectLockSetAfterUpload is set)
|
||||||
|
// "copy" means: keep the value from metadata (already applied above in the switch)
|
||||||
|
if !o.fs.opt.ObjectLockSetAfterUpload {
|
||||||
|
if o.fs.opt.ObjectLockMode != "" && !strings.EqualFold(o.fs.opt.ObjectLockMode, "copy") {
|
||||||
|
ui.req.ObjectLockMode = types.ObjectLockMode(strings.ToUpper(o.fs.opt.ObjectLockMode))
|
||||||
|
}
|
||||||
|
if o.fs.opt.ObjectLockRetainUntilDate != "" && !strings.EqualFold(o.fs.opt.ObjectLockRetainUntilDate, "copy") {
|
||||||
|
retainDate, err := parseRetainUntilDate(o.fs.opt.ObjectLockRetainUntilDate)
|
||||||
|
if err != nil {
|
||||||
|
return ui, fmt.Errorf("invalid object_lock_retain_until_date %q: %w", o.fs.opt.ObjectLockRetainUntilDate, err)
|
||||||
|
}
|
||||||
|
ui.req.ObjectLockRetainUntilDate = &retainDate
|
||||||
|
}
|
||||||
|
if o.fs.opt.ObjectLockLegalHoldStatus != "" && !strings.EqualFold(o.fs.opt.ObjectLockLegalHoldStatus, "copy") {
|
||||||
|
ui.req.ObjectLockLegalHoldStatus = types.ObjectLockLegalHoldStatus(strings.ToUpper(o.fs.opt.ObjectLockLegalHoldStatus))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Apply upload options
|
// Apply upload options
|
||||||
for _, option := range options {
|
for _, option := range options {
|
||||||
key, value := option.Header()
|
key, value := option.Header()
|
||||||
@@ -4737,6 +4979,14 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
|
|||||||
}
|
}
|
||||||
fs.Debugf(o, "Multipart upload Etag: %s OK", wantETag)
|
fs.Debugf(o, "Multipart upload Etag: %s OK", wantETag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set Object Lock via separate API calls if requested
|
||||||
|
if o.fs.opt.ObjectLockSetAfterUpload {
|
||||||
|
if err := o.setObjectLockAfterUpload(ctx, src); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4754,6 +5004,9 @@ func (o *Object) Remove(ctx context.Context) error {
|
|||||||
if o.fs.opt.RequesterPays {
|
if o.fs.opt.RequesterPays {
|
||||||
req.RequestPayer = types.RequestPayerRequester
|
req.RequestPayer = types.RequestPayerRequester
|
||||||
}
|
}
|
||||||
|
if o.fs.opt.BypassGovernanceRetention {
|
||||||
|
req.BypassGovernanceRetention = &o.fs.opt.BypassGovernanceRetention
|
||||||
|
}
|
||||||
err := o.fs.pacer.Call(func() (bool, error) {
|
err := o.fs.pacer.Call(func() (bool, error) {
|
||||||
_, err := o.fs.c.DeleteObject(ctx, &req)
|
_, err := o.fs.c.DeleteObject(ctx, &req)
|
||||||
return o.fs.shouldRetry(ctx, err)
|
return o.fs.shouldRetry(ctx, err)
|
||||||
@@ -4761,6 +5014,120 @@ func (o *Object) Remove(ctx context.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setObjectRetention sets Object Lock retention on an object via PutObjectRetention API
|
||||||
|
//
|
||||||
|
// Note: We use smithyhttp.AddContentChecksumMiddleware to ensure Content-MD5 is
|
||||||
|
// calculated for the request body. The AWS SDK v2 switched from MD5 to CRC32 as
|
||||||
|
// the default checksum algorithm, but some S3-compatible providers (e.g. MinIO)
|
||||||
|
// still require Content-MD5 for PutObjectRetention requests.
|
||||||
|
// See: https://github.com/aws/aws-sdk-go-v2/discussions/2960
|
||||||
|
func (o *Object) setObjectRetention(ctx context.Context, mode types.ObjectLockRetentionMode, retainUntilDate time.Time) error {
|
||||||
|
bucket, bucketPath := o.split()
|
||||||
|
req := s3.PutObjectRetentionInput{
|
||||||
|
Bucket: &bucket,
|
||||||
|
Key: &bucketPath,
|
||||||
|
VersionId: o.versionID,
|
||||||
|
Retention: &types.ObjectLockRetention{
|
||||||
|
Mode: mode,
|
||||||
|
RetainUntilDate: &retainUntilDate,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if o.fs.opt.RequesterPays {
|
||||||
|
req.RequestPayer = types.RequestPayerRequester
|
||||||
|
}
|
||||||
|
if o.fs.opt.BypassGovernanceRetention {
|
||||||
|
req.BypassGovernanceRetention = &o.fs.opt.BypassGovernanceRetention
|
||||||
|
}
|
||||||
|
return o.fs.pacer.Call(func() (bool, error) {
|
||||||
|
_, err := o.fs.c.PutObjectRetention(ctx, &req,
|
||||||
|
s3.WithAPIOptions(smithyhttp.AddContentChecksumMiddleware))
|
||||||
|
return o.fs.shouldRetry(ctx, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// setObjectLegalHold sets Object Lock legal hold on an object via PutObjectLegalHold API
|
||||||
|
//
|
||||||
|
// Note: We use smithyhttp.AddContentChecksumMiddleware to ensure Content-MD5 is
|
||||||
|
// calculated for the request body. The AWS SDK v2 switched from MD5 to CRC32 as
|
||||||
|
// the default checksum algorithm, but some S3-compatible providers (e.g. MinIO)
|
||||||
|
// still require Content-MD5 for PutObjectLegalHold requests.
|
||||||
|
// See: https://github.com/aws/aws-sdk-go-v2/discussions/2960
|
||||||
|
func (o *Object) setObjectLegalHold(ctx context.Context, status types.ObjectLockLegalHoldStatus) error {
|
||||||
|
bucket, bucketPath := o.split()
|
||||||
|
req := s3.PutObjectLegalHoldInput{
|
||||||
|
Bucket: &bucket,
|
||||||
|
Key: &bucketPath,
|
||||||
|
VersionId: o.versionID,
|
||||||
|
LegalHold: &types.ObjectLockLegalHold{
|
||||||
|
Status: status,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if o.fs.opt.RequesterPays {
|
||||||
|
req.RequestPayer = types.RequestPayerRequester
|
||||||
|
}
|
||||||
|
return o.fs.pacer.Call(func() (bool, error) {
|
||||||
|
_, err := o.fs.c.PutObjectLegalHold(ctx, &req,
|
||||||
|
s3.WithAPIOptions(smithyhttp.AddContentChecksumMiddleware))
|
||||||
|
return o.fs.shouldRetry(ctx, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// setObjectLockAfterUpload sets Object Lock via separate API calls after upload
|
||||||
|
// This is for S3 providers that don't support Object Lock headers during PUT
|
||||||
|
func (o *Object) setObjectLockAfterUpload(ctx context.Context, src fs.ObjectInfo) error {
|
||||||
|
// Determine the mode
|
||||||
|
var mode types.ObjectLockRetentionMode
|
||||||
|
modeOpt := o.fs.opt.ObjectLockMode
|
||||||
|
if strings.EqualFold(modeOpt, "copy") {
|
||||||
|
if srcObj, ok := src.(*Object); ok && srcObj.objectLockMode != nil {
|
||||||
|
mode = types.ObjectLockRetentionMode(*srcObj.objectLockMode)
|
||||||
|
}
|
||||||
|
} else if modeOpt != "" {
|
||||||
|
mode = types.ObjectLockRetentionMode(strings.ToUpper(modeOpt))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the retain until date
|
||||||
|
var retainUntilDate time.Time
|
||||||
|
dateOpt := o.fs.opt.ObjectLockRetainUntilDate
|
||||||
|
if strings.EqualFold(dateOpt, "copy") {
|
||||||
|
if srcObj, ok := src.(*Object); ok && srcObj.objectLockRetainUntilDate != nil {
|
||||||
|
retainUntilDate = *srcObj.objectLockRetainUntilDate
|
||||||
|
}
|
||||||
|
} else if dateOpt != "" {
|
||||||
|
var err error
|
||||||
|
retainUntilDate, err = parseRetainUntilDate(dateOpt)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid object_lock_retain_until_date %q: %w", dateOpt, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set retention if both mode and date are set
|
||||||
|
if mode != "" && !retainUntilDate.IsZero() {
|
||||||
|
if err := o.setObjectRetention(ctx, mode, retainUntilDate); err != nil {
|
||||||
|
return fmt.Errorf("failed to set object retention: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine and set legal hold
|
||||||
|
var legalHold types.ObjectLockLegalHoldStatus
|
||||||
|
legalHoldOpt := o.fs.opt.ObjectLockLegalHoldStatus
|
||||||
|
if strings.EqualFold(legalHoldOpt, "copy") {
|
||||||
|
if srcObj, ok := src.(*Object); ok && srcObj.objectLockLegalHoldStatus != nil {
|
||||||
|
legalHold = types.ObjectLockLegalHoldStatus(*srcObj.objectLockLegalHoldStatus)
|
||||||
|
}
|
||||||
|
} else if legalHoldOpt != "" {
|
||||||
|
legalHold = types.ObjectLockLegalHoldStatus(strings.ToUpper(legalHoldOpt))
|
||||||
|
}
|
||||||
|
|
||||||
|
if legalHold != "" {
|
||||||
|
if err := o.setObjectLegalHold(ctx, legalHold); err != nil {
|
||||||
|
return fmt.Errorf("failed to set legal hold: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// MimeType of an Object if known, "" otherwise
|
// MimeType of an Object if known, "" otherwise
|
||||||
func (o *Object) MimeType(ctx context.Context) string {
|
func (o *Object) MimeType(ctx context.Context) string {
|
||||||
err := o.readMetaData(ctx)
|
err := o.readMetaData(ctx)
|
||||||
@@ -4804,7 +5171,7 @@ func (o *Object) Metadata(ctx context.Context) (metadata fs.Metadata, err error)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
metadata = make(fs.Metadata, len(o.meta)+7)
|
metadata = make(fs.Metadata, len(o.meta)+10)
|
||||||
for k, v := range o.meta {
|
for k, v := range o.meta {
|
||||||
switch k {
|
switch k {
|
||||||
case metaMtime:
|
case metaMtime:
|
||||||
@@ -4839,6 +5206,15 @@ func (o *Object) Metadata(ctx context.Context) (metadata fs.Metadata, err error)
|
|||||||
setMetadata("content-disposition", o.contentDisposition)
|
setMetadata("content-disposition", o.contentDisposition)
|
||||||
setMetadata("content-encoding", o.contentEncoding)
|
setMetadata("content-encoding", o.contentEncoding)
|
||||||
setMetadata("content-language", o.contentLanguage)
|
setMetadata("content-language", o.contentLanguage)
|
||||||
|
|
||||||
|
// Set Object Lock metadata
|
||||||
|
setMetadata("object-lock-mode", o.objectLockMode)
|
||||||
|
if o.objectLockRetainUntilDate != nil {
|
||||||
|
formatted := o.objectLockRetainUntilDate.Format(time.RFC3339)
|
||||||
|
setMetadata("object-lock-retain-until-date", &formatted)
|
||||||
|
}
|
||||||
|
setMetadata("object-lock-legal-hold-status", o.objectLockLegalHoldStatus)
|
||||||
|
|
||||||
metadata["tier"] = o.GetTier()
|
metadata["tier"] = o.GetTier()
|
||||||
|
|
||||||
return metadata, nil
|
return metadata, nil
|
||||||
|
|||||||
@@ -498,10 +498,236 @@ func (f *Fs) InternalTestVersions(t *testing.T) {
|
|||||||
// Purge gets tested later
|
// Purge gets tested later
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *Fs) InternalTestObjectLock(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// Create a temporary bucket with Object Lock enabled to test on.
|
||||||
|
// This exercises our BucketObjectLockEnabled option and isolates
|
||||||
|
// the test from the main test bucket.
|
||||||
|
lockBucket := f.rootBucket + "-object-lock-" + random.String(8)
|
||||||
|
lockBucket = strings.ToLower(lockBucket)
|
||||||
|
|
||||||
|
// Try to create bucket with Object Lock enabled
|
||||||
|
objectLockEnabled := true
|
||||||
|
req := s3.CreateBucketInput{
|
||||||
|
Bucket: &lockBucket,
|
||||||
|
ACL: types.BucketCannedACL(f.opt.BucketACL),
|
||||||
|
ObjectLockEnabledForBucket: &objectLockEnabled,
|
||||||
|
}
|
||||||
|
if f.opt.LocationConstraint != "" {
|
||||||
|
req.CreateBucketConfiguration = &types.CreateBucketConfiguration{
|
||||||
|
LocationConstraint: types.BucketLocationConstraint(f.opt.LocationConstraint),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err := f.pacer.Call(func() (bool, error) {
|
||||||
|
_, err := f.c.CreateBucket(ctx, &req)
|
||||||
|
return f.shouldRetry(ctx, err)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Skipf("Object Lock not supported by this provider: CreateBucket with Object Lock failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify Object Lock is actually enabled on the new bucket.
|
||||||
|
// Some S3-compatible servers (e.g. rclone serve s3) accept the
|
||||||
|
// ObjectLockEnabledForBucket flag but don't actually implement Object Lock.
|
||||||
|
var lockCfg *s3.GetObjectLockConfigurationOutput
|
||||||
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
|
var err error
|
||||||
|
lockCfg, err = f.c.GetObjectLockConfiguration(ctx, &s3.GetObjectLockConfigurationInput{
|
||||||
|
Bucket: &lockBucket,
|
||||||
|
})
|
||||||
|
return f.shouldRetry(ctx, err)
|
||||||
|
})
|
||||||
|
if err != nil || lockCfg.ObjectLockConfiguration == nil ||
|
||||||
|
lockCfg.ObjectLockConfiguration.ObjectLockEnabled != types.ObjectLockEnabledEnabled {
|
||||||
|
_ = f.pacer.Call(func() (bool, error) {
|
||||||
|
_, err := f.c.DeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: &lockBucket})
|
||||||
|
return f.shouldRetry(ctx, err)
|
||||||
|
})
|
||||||
|
t.Skipf("Object Lock not functional on this provider (GetObjectLockConfiguration: %v)", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch f to use the Object Lock bucket for this test
|
||||||
|
oldBucket := f.rootBucket
|
||||||
|
oldRoot := f.root
|
||||||
|
oldRootDir := f.rootDirectory
|
||||||
|
f.rootBucket = lockBucket
|
||||||
|
f.root = lockBucket
|
||||||
|
f.rootDirectory = ""
|
||||||
|
defer func() {
|
||||||
|
f.rootBucket = oldBucket
|
||||||
|
f.root = oldRoot
|
||||||
|
f.rootDirectory = oldRootDir
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Helper to remove an object with Object Lock protection
|
||||||
|
removeLocked := func(t *testing.T, obj fs.Object) {
|
||||||
|
t.Helper()
|
||||||
|
o := obj.(*Object)
|
||||||
|
// Remove legal hold if present
|
||||||
|
_ = o.setObjectLegalHold(ctx, types.ObjectLockLegalHoldStatusOff)
|
||||||
|
// Enable bypass governance retention for deletion
|
||||||
|
o.fs.opt.BypassGovernanceRetention = true
|
||||||
|
err := obj.Remove(ctx)
|
||||||
|
o.fs.opt.BypassGovernanceRetention = false
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up the temporary bucket after all sub-tests
|
||||||
|
defer func() {
|
||||||
|
// List and remove all object versions
|
||||||
|
var objectVersions []types.ObjectIdentifier
|
||||||
|
listReq := &s3.ListObjectVersionsInput{Bucket: &lockBucket}
|
||||||
|
for {
|
||||||
|
var resp *s3.ListObjectVersionsOutput
|
||||||
|
err := f.pacer.Call(func() (bool, error) {
|
||||||
|
var err error
|
||||||
|
resp, err = f.c.ListObjectVersions(ctx, listReq)
|
||||||
|
return f.shouldRetry(ctx, err)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("Failed to list object versions for cleanup: %v", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for _, v := range resp.Versions {
|
||||||
|
objectVersions = append(objectVersions, types.ObjectIdentifier{
|
||||||
|
Key: v.Key,
|
||||||
|
VersionId: v.VersionId,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, m := range resp.DeleteMarkers {
|
||||||
|
objectVersions = append(objectVersions, types.ObjectIdentifier{
|
||||||
|
Key: m.Key,
|
||||||
|
VersionId: m.VersionId,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if !aws.ToBool(resp.IsTruncated) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
listReq.KeyMarker = resp.NextKeyMarker
|
||||||
|
listReq.VersionIdMarker = resp.NextVersionIdMarker
|
||||||
|
}
|
||||||
|
if len(objectVersions) > 0 {
|
||||||
|
bypass := true
|
||||||
|
_ = f.pacer.Call(func() (bool, error) {
|
||||||
|
_, err := f.c.DeleteObjects(ctx, &s3.DeleteObjectsInput{
|
||||||
|
Bucket: &lockBucket,
|
||||||
|
BypassGovernanceRetention: &bypass,
|
||||||
|
Delete: &types.Delete{
|
||||||
|
Objects: objectVersions,
|
||||||
|
Quiet: aws.Bool(true),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return f.shouldRetry(ctx, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ = f.pacer.Call(func() (bool, error) {
|
||||||
|
_, err := f.c.DeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: &lockBucket})
|
||||||
|
return f.shouldRetry(ctx, err)
|
||||||
|
})
|
||||||
|
}()
|
||||||
|
|
||||||
|
retainUntilDate := time.Now().UTC().Add(24 * time.Hour).Truncate(time.Second)
|
||||||
|
|
||||||
|
t.Run("Retention", func(t *testing.T) {
|
||||||
|
// Set Object Lock options for this test
|
||||||
|
f.opt.ObjectLockMode = "GOVERNANCE"
|
||||||
|
f.opt.ObjectLockRetainUntilDate = retainUntilDate.Format(time.RFC3339)
|
||||||
|
defer func() {
|
||||||
|
f.opt.ObjectLockMode = ""
|
||||||
|
f.opt.ObjectLockRetainUntilDate = ""
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Upload an object with Object Lock retention
|
||||||
|
contents := random.String(100)
|
||||||
|
item := fstest.NewItem("test-object-lock-retention", contents, fstest.Time("2001-05-06T04:05:06.499999999Z"))
|
||||||
|
obj := fstests.PutTestContents(ctx, t, f, &item, contents, true)
|
||||||
|
defer func() {
|
||||||
|
removeLocked(t, obj)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Read back metadata and verify Object Lock settings
|
||||||
|
o := obj.(*Object)
|
||||||
|
gotMetadata, err := o.Metadata(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, "GOVERNANCE", gotMetadata["object-lock-mode"])
|
||||||
|
gotRetainDate, err := time.Parse(time.RFC3339, gotMetadata["object-lock-retain-until-date"])
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.WithinDuration(t, retainUntilDate, gotRetainDate, time.Second)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("LegalHold", func(t *testing.T) {
|
||||||
|
// Set Object Lock legal hold option
|
||||||
|
f.opt.ObjectLockLegalHoldStatus = "ON"
|
||||||
|
defer func() {
|
||||||
|
f.opt.ObjectLockLegalHoldStatus = ""
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Upload an object with legal hold
|
||||||
|
contents := random.String(100)
|
||||||
|
item := fstest.NewItem("test-object-lock-legal-hold", contents, fstest.Time("2001-05-06T04:05:06.499999999Z"))
|
||||||
|
obj := fstests.PutTestContents(ctx, t, f, &item, contents, true)
|
||||||
|
defer func() {
|
||||||
|
removeLocked(t, obj)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Verify legal hold is ON
|
||||||
|
o := obj.(*Object)
|
||||||
|
gotMetadata, err := o.Metadata(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "ON", gotMetadata["object-lock-legal-hold-status"])
|
||||||
|
|
||||||
|
// Set legal hold to OFF
|
||||||
|
err = o.setObjectLegalHold(ctx, types.ObjectLockLegalHoldStatusOff)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Clear cached metadata and re-read
|
||||||
|
o.meta = nil
|
||||||
|
gotMetadata, err = o.Metadata(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "OFF", gotMetadata["object-lock-legal-hold-status"])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("SetAfterUpload", func(t *testing.T) {
|
||||||
|
// Test the post-upload API path (PutObjectRetention + PutObjectLegalHold)
|
||||||
|
f.opt.ObjectLockSetAfterUpload = true
|
||||||
|
f.opt.ObjectLockMode = "GOVERNANCE"
|
||||||
|
f.opt.ObjectLockRetainUntilDate = retainUntilDate.Format(time.RFC3339)
|
||||||
|
f.opt.ObjectLockLegalHoldStatus = "ON"
|
||||||
|
defer func() {
|
||||||
|
f.opt.ObjectLockSetAfterUpload = false
|
||||||
|
f.opt.ObjectLockMode = ""
|
||||||
|
f.opt.ObjectLockRetainUntilDate = ""
|
||||||
|
f.opt.ObjectLockLegalHoldStatus = ""
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Upload an object - lock applied AFTER upload via separate API calls
|
||||||
|
contents := random.String(100)
|
||||||
|
item := fstest.NewItem("test-object-lock-after-upload", contents, fstest.Time("2001-05-06T04:05:06.499999999Z"))
|
||||||
|
obj := fstests.PutTestContents(ctx, t, f, &item, contents, true)
|
||||||
|
defer func() {
|
||||||
|
removeLocked(t, obj)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Verify all Object Lock settings were applied
|
||||||
|
o := obj.(*Object)
|
||||||
|
gotMetadata, err := o.Metadata(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, "GOVERNANCE", gotMetadata["object-lock-mode"])
|
||||||
|
gotRetainDate, err := time.Parse(time.RFC3339, gotMetadata["object-lock-retain-until-date"])
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.WithinDuration(t, retainUntilDate, gotRetainDate, time.Second)
|
||||||
|
assert.Equal(t, "ON", gotMetadata["object-lock-legal-hold-status"])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (f *Fs) InternalTest(t *testing.T) {
|
func (f *Fs) InternalTest(t *testing.T) {
|
||||||
t.Run("Metadata", f.InternalTestMetadata)
|
t.Run("Metadata", f.InternalTestMetadata)
|
||||||
t.Run("NoHead", f.InternalTestNoHead)
|
t.Run("NoHead", f.InternalTestNoHead)
|
||||||
t.Run("Versions", f.InternalTestVersions)
|
t.Run("Versions", f.InternalTestVersions)
|
||||||
|
t.Run("ObjectLock", f.InternalTestObjectLock)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ fstests.InternalTester = (*Fs)(nil)
|
var _ fstests.InternalTester = (*Fs)(nil)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go-v2/aws"
|
"github.com/aws/aws-sdk-go-v2/aws"
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
@@ -92,3 +93,87 @@ var (
|
|||||||
_ fstests.SetUploadCutoffer = (*Fs)(nil)
|
_ fstests.SetUploadCutoffer = (*Fs)(nil)
|
||||||
_ fstests.SetCopyCutoffer = (*Fs)(nil)
|
_ fstests.SetCopyCutoffer = (*Fs)(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestParseRetainUntilDate(t *testing.T) {
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
wantErr bool
|
||||||
|
checkFunc func(t *testing.T, result time.Time)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "RFC3339 date",
|
||||||
|
input: "2030-01-15T10:30:00Z",
|
||||||
|
wantErr: false,
|
||||||
|
checkFunc: func(t *testing.T, result time.Time) {
|
||||||
|
expected, _ := time.Parse(time.RFC3339, "2030-01-15T10:30:00Z")
|
||||||
|
assert.Equal(t, expected, result)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "RFC3339 date with timezone",
|
||||||
|
input: "2030-06-15T10:30:00+02:00",
|
||||||
|
wantErr: false,
|
||||||
|
checkFunc: func(t *testing.T, result time.Time) {
|
||||||
|
expected, _ := time.Parse(time.RFC3339, "2030-06-15T10:30:00+02:00")
|
||||||
|
assert.Equal(t, expected, result)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "duration days",
|
||||||
|
input: "365d",
|
||||||
|
wantErr: false,
|
||||||
|
checkFunc: func(t *testing.T, result time.Time) {
|
||||||
|
expected := now.Add(365 * 24 * time.Hour)
|
||||||
|
diff := result.Sub(expected)
|
||||||
|
assert.Less(t, diff.Abs(), 2*time.Second, "result should be ~365 days from now")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "duration hours",
|
||||||
|
input: "24h",
|
||||||
|
wantErr: false,
|
||||||
|
checkFunc: func(t *testing.T, result time.Time) {
|
||||||
|
expected := now.Add(24 * time.Hour)
|
||||||
|
diff := result.Sub(expected)
|
||||||
|
assert.Less(t, diff.Abs(), 2*time.Second, "result should be ~24 hours from now")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "duration minutes",
|
||||||
|
input: "30m",
|
||||||
|
wantErr: false,
|
||||||
|
checkFunc: func(t *testing.T, result time.Time) {
|
||||||
|
expected := now.Add(30 * time.Minute)
|
||||||
|
diff := result.Sub(expected)
|
||||||
|
assert.Less(t, diff.Abs(), 2*time.Second, "result should be ~30 minutes from now")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid input",
|
||||||
|
input: "not-a-date",
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty input",
|
||||||
|
input: "",
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result, err := parseRetainUntilDate(tt.input)
|
||||||
|
if tt.wantErr {
|
||||||
|
require.Error(t, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(t, err)
|
||||||
|
if tt.checkFunc != nil {
|
||||||
|
tt.checkFunc(t, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ func (f *Fs) refreshJWTToken(ctx context.Context) (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("invalid token received from server")
|
return "", fmt.Errorf("invalid token received from server")
|
||||||
}
|
}
|
||||||
var claims map[string]interface{}
|
var claims map[string]any
|
||||||
if err := json.Unmarshal(payload, &claims); err != nil {
|
if err := json.Unmarshal(payload, &claims); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -182,7 +182,7 @@ func (f *Fs) refreshJWTToken(ctx context.Context) (string, error) {
|
|||||||
return f.token, nil
|
return f.token, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Fs) callAPI(ctx context.Context, method, path string, response interface{}) (*http.Response, error) {
|
func (f *Fs) callAPI(ctx context.Context, method, path string, response any) (*http.Response, error) {
|
||||||
token, err := f.refreshJWTToken(ctx)
|
token, err := f.refreshJWTToken(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"maps"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
@@ -203,9 +204,7 @@ func (s *shadeChunkWriter) WriteChunk(ctx context.Context, chunkNumber int, read
|
|||||||
var uploadRes *http.Response
|
var uploadRes *http.Response
|
||||||
if len(partURL.Headers) > 0 {
|
if len(partURL.Headers) > 0 {
|
||||||
opts.ExtraHeaders = make(map[string]string)
|
opts.ExtraHeaders = make(map[string]string)
|
||||||
for k, v := range partURL.Headers {
|
maps.Copy(opts.ExtraHeaders, partURL.Headers)
|
||||||
opts.ExtraHeaders[k] = v
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.f.pacer.Call(func() (bool, error) {
|
err = s.f.pacer.Call(func() (bool, error) {
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ type Item struct {
|
|||||||
FileCount int32 `json:"FileCount,omitempty"`
|
FileCount int32 `json:"FileCount,omitempty"`
|
||||||
Name string `json:"Name,omitempty"`
|
Name string `json:"Name,omitempty"`
|
||||||
FileName string `json:"FileName,omitempty"`
|
FileName string `json:"FileName,omitempty"`
|
||||||
CreatedAt time.Time `json:"CreationDate,omitempty"`
|
CreatedAt time.Time `json:"CreationDate"`
|
||||||
ModifiedAt time.Time `json:"ClientModifiedDate,omitempty"`
|
ModifiedAt time.Time `json:"ClientModifiedDate"`
|
||||||
IsHidden bool `json:"IsHidden,omitempty"`
|
IsHidden bool `json:"IsHidden,omitempty"`
|
||||||
Size int64 `json:"FileSizeBytes,omitempty"`
|
Size int64 `json:"FileSizeBytes,omitempty"`
|
||||||
Type string `json:"odata.type,omitempty"`
|
Type string `json:"odata.type,omitempty"`
|
||||||
|
|||||||
@@ -423,7 +423,7 @@ func (f *Fs) filePath(file string) string {
|
|||||||
if f.opt.Enc != encoder.EncodeZero {
|
if f.opt.Enc != encoder.EncodeZero {
|
||||||
subPath = f.opt.Enc.FromStandardPath(subPath)
|
subPath = f.opt.Enc.FromStandardPath(subPath)
|
||||||
}
|
}
|
||||||
return rest.URLPathEscape(subPath)
|
return rest.URLPathEscapeAll(subPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// dirPath returns a directory path (f.root, dir)
|
// dirPath returns a directory path (f.root, dir)
|
||||||
@@ -610,7 +610,7 @@ func (f *Fs) findHeader(headers fs.CommaSepList, find string) bool {
|
|||||||
|
|
||||||
// fetch the bearer token and set it if successful
|
// fetch the bearer token and set it if successful
|
||||||
func (f *Fs) fetchAndSetBearerToken() error {
|
func (f *Fs) fetchAndSetBearerToken() error {
|
||||||
_, err, _ := f.authSingleflight.Do("bearerToken", func() (interface{}, error) {
|
_, err, _ := f.authSingleflight.Do("bearerToken", func() (any, error) {
|
||||||
if len(f.opt.BearerTokenCommand) == 0 {
|
if len(f.opt.BearerTokenCommand) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,3 +80,35 @@ func TestHeaders(t *testing.T) {
|
|||||||
_, err := f.Features().About(context.Background())
|
_, err := f.Features().About(context.Background())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestReservedCharactersInPathAreEscaped verifies that reserved characters
|
||||||
|
// like semicolons and equals signs in file paths are percent-encoded in
|
||||||
|
// HTTP requests to the WebDAV server (RFC 3986 compliance).
|
||||||
|
func TestReservedCharactersInPathAreEscaped(t *testing.T) {
|
||||||
|
var capturedPath string
|
||||||
|
|
||||||
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
capturedPath = r.RequestURI
|
||||||
|
// Return a 404 so the NewObject call fails cleanly
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
})
|
||||||
|
ts := httptest.NewServer(handler)
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
configfile.Install()
|
||||||
|
m := configmap.Simple{
|
||||||
|
"type": "webdav",
|
||||||
|
"url": ts.URL,
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := webdav.NewFs(context.Background(), remoteName, "", m)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Try to access a file with a semicolon in the name.
|
||||||
|
// We expect the request to fail (404), but the path should be escaped.
|
||||||
|
_, _ = f.NewObject(context.Background(), "my;test")
|
||||||
|
|
||||||
|
// The semicolon must be percent-encoded as %3B
|
||||||
|
assert.Contains(t, capturedPath, "my%3Btest", "semicolons in path should be percent-encoded")
|
||||||
|
assert.NotContains(t, capturedPath, "my;test", "raw semicolons should not appear in path")
|
||||||
|
}
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ type WriteMultiMetadataRequest struct {
|
|||||||
|
|
||||||
// WriteMetadata is used to write item metadata
|
// WriteMetadata is used to write item metadata
|
||||||
type WriteMetadata struct {
|
type WriteMetadata struct {
|
||||||
Attributes WriteAttributes `json:"attributes,omitempty"`
|
Attributes WriteAttributes `json:"attributes"`
|
||||||
ID string `json:"id,omitempty"`
|
ID string `json:"id,omitempty"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -147,6 +147,16 @@ func ArchiveExtract(ctx context.Context, dst fs.Fs, dstDir string, src fs.Fs, sr
|
|||||||
// extract files
|
// extract files
|
||||||
err = ex.Extract(ctx, in, func(ctx context.Context, f archives.FileInfo) error {
|
err = ex.Extract(ctx, in, func(ctx context.Context, f archives.FileInfo) error {
|
||||||
remote := f.NameInArchive
|
remote := f.NameInArchive
|
||||||
|
// Strip leading "./" from archive paths. Tar files created with
|
||||||
|
// relative paths (e.g. "tar -czf archive.tar.gz .") use "./" prefixed
|
||||||
|
// entries. Without stripping, rclone encodes the "." as a full-width
|
||||||
|
// dot character creating a spurious directory. We only strip "./"
|
||||||
|
// specifically to avoid enabling path traversal attacks via "../".
|
||||||
|
remote = strings.TrimPrefix(remote, "./")
|
||||||
|
// If the entry was exactly "./" (the root dir), skip it
|
||||||
|
if remote == "" && f.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if dstDir != "" {
|
if dstDir != "" {
|
||||||
remote = path.Join(dstDir, remote)
|
remote = path.Join(dstDir, remote)
|
||||||
}
|
}
|
||||||
|
|||||||
62
cmd/archive/extract/extract_test.go
Normal file
62
cmd/archive/extract/extract_test.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
//go:build !plan9
|
||||||
|
|
||||||
|
package extract
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStripDotSlashPrefix(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "strip leading dot-slash from file",
|
||||||
|
input: "./file.txt",
|
||||||
|
expected: "file.txt",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "strip leading dot-slash from nested path",
|
||||||
|
input: "./subdir/file.txt",
|
||||||
|
expected: "subdir/file.txt",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no prefix unchanged",
|
||||||
|
input: "file.txt",
|
||||||
|
expected: "file.txt",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nested path unchanged",
|
||||||
|
input: "dir/file.txt",
|
||||||
|
expected: "dir/file.txt",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dot-dot-slash NOT stripped (path traversal safety)",
|
||||||
|
input: "../etc/passwd",
|
||||||
|
expected: "../etc/passwd",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dot-slash directory entry becomes empty",
|
||||||
|
input: "./",
|
||||||
|
expected: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only single leading dot-slash stripped",
|
||||||
|
input: "././file.txt",
|
||||||
|
expected: "./file.txt",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
// This mirrors the stripping logic in ArchiveExtract
|
||||||
|
got := strings.TrimPrefix(tc.input, "./")
|
||||||
|
assert.Equal(t, tc.expected, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -92,10 +92,10 @@ func TestCountWriterConcurrent(t *testing.T) {
|
|||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(goroutines)
|
wg.Add(goroutines)
|
||||||
for g := 0; g < goroutines; g++ {
|
for range goroutines {
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
for i := 0; i < loops; i++ {
|
for range loops {
|
||||||
n, err := cw.Write(data)
|
n, err := cw.Write(data)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, chunkSize, n)
|
assert.Equal(t, chunkSize, n)
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ var commandDefinition = &cobra.Command{
|
|||||||
Long: longHelp,
|
Long: longHelp,
|
||||||
Annotations: map[string]string{
|
Annotations: map[string]string{
|
||||||
"versionIntroduced": "v1.58",
|
"versionIntroduced": "v1.58",
|
||||||
"groups": "Filter,Copy,Important",
|
"groups": "Filter,Copy,Important,Sync",
|
||||||
},
|
},
|
||||||
RunE: func(command *cobra.Command, args []string) error {
|
RunE: func(command *cobra.Command, args []string) error {
|
||||||
// NOTE: avoid putting too much handling here, as it won't apply to the rc.
|
// NOTE: avoid putting too much handling here, as it won't apply to the rc.
|
||||||
|
|||||||
@@ -128,9 +128,7 @@ func (b *bisyncRun) startLockRenewal() func() {
|
|||||||
}
|
}
|
||||||
stopLockRenewal := make(chan struct{})
|
stopLockRenewal := make(chan struct{})
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(1)
|
wg.Go(func() {
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
ticker := time.NewTicker(time.Duration(b.opt.MaxLock) - time.Minute)
|
ticker := time.NewTicker(time.Duration(b.opt.MaxLock) - time.Minute)
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@@ -141,7 +139,7 @@ func (b *bisyncRun) startLockRenewal() func() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
return func() {
|
return func() {
|
||||||
close(stopLockRenewal)
|
close(stopLockRenewal)
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|||||||
@@ -361,9 +361,7 @@ func StartStats() func() {
|
|||||||
}
|
}
|
||||||
stopStats := make(chan struct{})
|
stopStats := make(chan struct{})
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(1)
|
wg.Go(func() {
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
ticker := time.NewTicker(*statsInterval)
|
ticker := time.NewTicker(*statsInterval)
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@@ -374,7 +372,7 @@ func StartStats() func() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
return func() {
|
return func() {
|
||||||
close(stopStats)
|
close(stopStats)
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ func mountOptions(VFS *vfs.VFS, device string, mountpoint string, opt *mountlib.
|
|||||||
func waitFor(fn func() bool) (ok bool) {
|
func waitFor(fn func() bool) (ok bool) {
|
||||||
const totalWait = 10 * time.Second
|
const totalWait = 10 * time.Second
|
||||||
const individualWait = 10 * time.Millisecond
|
const individualWait = 10 * time.Millisecond
|
||||||
for i := 0; i < int(totalWait/individualWait); i++ {
|
for range int(totalWait / individualWait) {
|
||||||
ok = fn()
|
ok = fn()
|
||||||
if ok {
|
if ok {
|
||||||
return ok
|
return ok
|
||||||
|
|||||||
@@ -70,6 +70,15 @@ Note that |--stdout| and |--print-filename| are incompatible with |--urls|.
|
|||||||
This will do |--transfers| copies in parallel. Note that if |--auto-filename|
|
This will do |--transfers| copies in parallel. Note that if |--auto-filename|
|
||||||
is desired for all URLs then a file with only URLs and no filename can be used.
|
is desired for all URLs then a file with only URLs and no filename can be used.
|
||||||
|
|
||||||
|
Each FILENAME in the CSV file can start with a relative path which will be appended
|
||||||
|
to the destination path provided at the command line. For example, running the command
|
||||||
|
shown above with the following CSV file will write two files to the destination:
|
||||||
|
|remote:dir/local/path/bar.json| and |remote:dir/another/local/directory/qux.json|
|
||||||
|
|||csv
|
||||||
|
https://example.org/foo/bar.json,local/path/bar.json
|
||||||
|
https://example.org/qux/baz.json,another/local/directory/qux.json
|
||||||
|
|||
|
||||||
|
|
||||||
### Troubleshooting
|
### Troubleshooting
|
||||||
|
|
||||||
If you can't get |rclone copyurl| to work then here are some things you can try:
|
If you can't get |rclone copyurl| to work then here are some things you can try:
|
||||||
|
|||||||
@@ -41,9 +41,7 @@ func startProgress() func() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(1)
|
wg.Go(func() {
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
progressInterval := defaultProgressInterval
|
progressInterval := defaultProgressInterval
|
||||||
if ShowStats() && *statsInterval > 0 {
|
if ShowStats() && *statsInterval > 0 {
|
||||||
progressInterval = *statsInterval
|
progressInterval = *statsInterval
|
||||||
@@ -65,7 +63,7 @@ func startProgress() func() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
return func() {
|
return func() {
|
||||||
close(stopStats)
|
close(stopStats)
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|||||||
@@ -66,11 +66,9 @@ func testCacheCRUD(t *testing.T, h *Handler, c Cache, fileName string) {
|
|||||||
func testCacheThrashDifferent(t *testing.T, h *Handler, c Cache) {
|
func testCacheThrashDifferent(t *testing.T, h *Handler, c Cache) {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
for i := range 100 {
|
for i := range 100 {
|
||||||
wg.Add(1)
|
wg.Go(func() {
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
testCacheCRUD(t, h, c, fmt.Sprintf("file-%d", i))
|
testCacheCRUD(t, h, c, fmt.Sprintf("file-%d", i))
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
@@ -79,9 +77,7 @@ func testCacheThrashDifferent(t *testing.T, h *Handler, c Cache) {
|
|||||||
func testCacheThrashSame(t *testing.T, h *Handler, c Cache) {
|
func testCacheThrashSame(t *testing.T, h *Handler, c Cache) {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
for range 100 {
|
for range 100 {
|
||||||
wg.Add(1)
|
wg.Go(func() {
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
// Write a handle
|
// Write a handle
|
||||||
splitPath := []string{"file"}
|
splitPath := []string{"file"}
|
||||||
@@ -108,7 +104,7 @@ func testCacheThrashSame(t *testing.T, h *Handler, c Cache) {
|
|||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
assert.Equal(t, errStaleHandle, err)
|
assert.Equal(t, errStaleHandle, err)
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ WebDAV or S3, that work out of the box.)
|
|||||||
{{< provider name="Dropbox" home="https://www.dropbox.com/" config="/dropbox/" >}}
|
{{< provider name="Dropbox" home="https://www.dropbox.com/" config="/dropbox/" >}}
|
||||||
{{< provider name="Enterprise File Fabric" home="https://storagemadeeasy.com/about/" config="/filefabric/" >}}
|
{{< provider name="Enterprise File Fabric" home="https://storagemadeeasy.com/about/" config="/filefabric/" >}}
|
||||||
{{< provider name="Exaba" home="https://exaba.com/" config="/s3/#exaba" >}}
|
{{< provider name="Exaba" home="https://exaba.com/" config="/s3/#exaba" >}}
|
||||||
|
{{< provider name="Fastly Object Storage" home="https://www.fastly.com/products/storage" config="/s3/#fastly" >}}
|
||||||
{{< provider name="Fastmail Files" home="https://www.fastmail.com/" config="/webdav/#fastmail-files" >}}
|
{{< 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 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="FileLu S5 (S3-Compatible Object Storage)" home="https://s5lu.com/" config="/s3/#filelu-s5" >}}
|
||||||
@@ -210,7 +211,6 @@ WebDAV or S3, that work out of the box.)
|
|||||||
{{< provider name="Sia" home="https://sia.tech/" config="/sia/" >}}
|
{{< provider name="Sia" home="https://sia.tech/" config="/sia/" >}}
|
||||||
{{< provider name="SMB / CIFS" home="https://en.wikipedia.org/wiki/Server_Message_Block" config="/smb/" >}}
|
{{< provider name="SMB / CIFS" home="https://en.wikipedia.org/wiki/Server_Message_Block" config="/smb/" >}}
|
||||||
{{< provider name="Spectra Logic" home="https://spectralogic.com/blackpearl-nearline-object-gateway/" config="/s3/#spectralogic" >}}
|
{{< provider name="Spectra Logic" home="https://spectralogic.com/blackpearl-nearline-object-gateway/" config="/s3/#spectralogic" >}}
|
||||||
{{< provider name="StackPath" home="https://www.stackpath.com/products/object-storage/" config="/s3/#stackpath" >}}
|
|
||||||
{{< provider name="Storj" home="https://storj.io/" config="/storj/" >}}
|
{{< 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="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="SugarSync" home="https://sugarsync.com/" config="/sugarsync/" >}}
|
||||||
@@ -219,6 +219,7 @@ WebDAV or S3, that work out of the box.)
|
|||||||
{{< provider name="Wasabi" home="https://wasabi.com/" config="/s3/#wasabi" >}}
|
{{< provider name="Wasabi" home="https://wasabi.com/" config="/s3/#wasabi" >}}
|
||||||
{{< provider name="WebDAV" home="https://en.wikipedia.org/wiki/WebDAV" config="/webdav/" >}}
|
{{< provider name="WebDAV" home="https://en.wikipedia.org/wiki/WebDAV" config="/webdav/" >}}
|
||||||
{{< provider name="Yandex Disk" home="https://disk.yandex.com/" config="/yandex/" >}}
|
{{< provider name="Yandex Disk" home="https://disk.yandex.com/" config="/yandex/" >}}
|
||||||
|
{{< provider name="Zadara Object Storage" home="https://www.zadara.com" config="/s3/#zadara" >}}
|
||||||
{{< provider name="Zoho WorkDrive" home="https://www.zoho.com/workdrive/" config="/zoho/" >}}
|
{{< provider name="Zoho WorkDrive" home="https://www.zoho.com/workdrive/" config="/zoho/" >}}
|
||||||
{{< provider name="Zata" home="https://zata.ai/" config="/s3/#Zata" end="true" >}}
|
{{< provider name="Zata" home="https://zata.ai/" config="/s3/#Zata" end="true" >}}
|
||||||
{{< provider name="The local filesystem" home="/local/" config="/local/" end="true">}}
|
{{< provider name="The local filesystem" home="/local/" config="/local/" end="true">}}
|
||||||
|
|||||||
@@ -978,7 +978,7 @@ put them back in again. -->
|
|||||||
- Nathanael Demacon <7271496+quantumsheep@users.noreply.github.com>
|
- Nathanael Demacon <7271496+quantumsheep@users.noreply.github.com>
|
||||||
- ahxxm <ahxxm@users.noreply.github.com>
|
- ahxxm <ahxxm@users.noreply.github.com>
|
||||||
- Flora Thiebaut <johann.thiebaut@gmail.com>
|
- Flora Thiebaut <johann.thiebaut@gmail.com>
|
||||||
- kingston125 <support@filelu.com>
|
- kingston125 <support@filelu.com> <kingston125@github.com>
|
||||||
- Ser-Bul <30335009+Ser-Bul@users.noreply.github.com>
|
- Ser-Bul <30335009+Ser-Bul@users.noreply.github.com>
|
||||||
- jinjingroad <jinjingroad@sina.com>
|
- jinjingroad <jinjingroad@sina.com>
|
||||||
- necaran <55765083+necaran@users.noreply.github.com>
|
- necaran <55765083+necaran@users.noreply.github.com>
|
||||||
@@ -1073,3 +1073,12 @@ put them back in again. -->
|
|||||||
- jzunigax2 <125698953+jzunigax2@users.noreply.github.com>
|
- jzunigax2 <125698953+jzunigax2@users.noreply.github.com>
|
||||||
- lullius <lullius@users.noreply.github.com>
|
- lullius <lullius@users.noreply.github.com>
|
||||||
- StarHack <StarHack@users.noreply.github.com>
|
- StarHack <StarHack@users.noreply.github.com>
|
||||||
|
- Leon Brocard <acme@astray.com>
|
||||||
|
- Cohinem <143964778+Cohinem@users.noreply.github.com>
|
||||||
|
- Jack Kelly <jack@OpenClimateFix.org>
|
||||||
|
- Prakhar Chhalotre <chhalotreprakhar00@gmail.com>
|
||||||
|
- Varun Chawla <34209028+veeceey@users.noreply.github.com>
|
||||||
|
- Jan-Philipp Reßler <75355263+TabError@users.noreply.github.com>
|
||||||
|
- Shlomi Avihou <shlomi@zadarastorage.com>
|
||||||
|
- Chris <238498929+chris081519-crypto@users.noreply.github.com>
|
||||||
|
- Jan-Philipp Reßler <xodarap@xodarap.de>
|
||||||
|
|||||||
@@ -6,6 +6,33 @@ description: "Rclone Changelog"
|
|||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v1.73.1 - 2026-02-17
|
||||||
|
|
||||||
|
[See commits](https://github.com/rclone/rclone/compare/v1.73.0...v1.73.1)
|
||||||
|
|
||||||
|
- Bug Fixes
|
||||||
|
- accounting: Fix missing server side stats from core/stats rc (Nick Craig-Wood)
|
||||||
|
- build
|
||||||
|
- Fix CVE-2025-68121 by updating go to 1.25.7 or later (Nick Craig-Wood)
|
||||||
|
- Bump github.com/go-chi/chi/v5 from 5.2.3 to 5.2.5 to fix GO-2026-4316 (albertony)
|
||||||
|
- docs: Extend copyurl docs with an example of CSV FILENAMEs starting with a path. (Jack Kelly)
|
||||||
|
- march: Fix runtime: program exceeds 10000-thread limit (Nick Craig-Wood)
|
||||||
|
- pacer
|
||||||
|
- Fix deadlock between pacer token and --max-connections (Nick Craig-Wood)
|
||||||
|
- Re-read the sleep time as it may be stale (Nick Craig-Wood)
|
||||||
|
- Drime
|
||||||
|
- Fix files and directories being created in the default workspace (Nick Craig-Wood)
|
||||||
|
- Filelu
|
||||||
|
- Avoid buffering entire file in memory (kingston125)
|
||||||
|
- Add multipart upload support with configurable cutoff (kingston125)
|
||||||
|
- Filen
|
||||||
|
- Fix 32 bit targets not being able to list directories (Enduriel)
|
||||||
|
- Fix potential panic in case of error during upload (Enduriel)
|
||||||
|
- Internxt
|
||||||
|
- Implement re-login under refresh logic, improve retry logic (José Zúniga)
|
||||||
|
-S3
|
||||||
|
- Set list_version to 2 for FileLu S3 configuration (kingston125)
|
||||||
|
|
||||||
## v1.73.0 - 2026-01-30
|
## v1.73.0 - 2026-01-30
|
||||||
|
|
||||||
[See commits](https://github.com/rclone/rclone/compare/v1.72.0...v1.73.0)
|
[See commits](https://github.com/rclone/rclone/compare/v1.72.0...v1.73.0)
|
||||||
|
|||||||
@@ -380,7 +380,7 @@ not the rclone developers so it may be out of date. Its current version is as be
|
|||||||
## Source installation {#source}
|
## Source installation {#source}
|
||||||
|
|
||||||
Make sure you have git and [Go](https://golang.org/) installed.
|
Make sure you have git and [Go](https://golang.org/) installed.
|
||||||
Go version 1.24 or newer is required, the latest release is recommended.
|
Go version 1.25 or newer is required, the latest release is recommended.
|
||||||
You can get it from your package manager, or download it from
|
You can get it from your package manager, or download it from
|
||||||
[golang.org/dl](https://golang.org/dl/). Then you can run the following:
|
[golang.org/dl](https://golang.org/dl/). Then you can run the following:
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ 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="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="Dreamhost" home="https://www.dreamhost.com/cloud/storage/" config="/s3/#dreamhost" >}}
|
||||||
{{< provider name="Exaba" home="https://exaba.com/" config="/s3/#exaba" >}}
|
{{< provider name="Exaba" home="https://exaba.com/" config="/s3/#exaba" >}}
|
||||||
|
{{< provider name="Fastly Object Storage" home="https://www.fastly.com/products/storage" config="/s3/#fastly" >}}
|
||||||
{{< provider name="FileLu S5 (S3-Compatible Object Storage)" home="https://s5lu.com/" config="/s3/#filelu-s5" >}}
|
{{< 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="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="Hetzner" home="https://www.hetzner.com/storage/object-storage/" config="/s3/#hetzner" >}}
|
||||||
@@ -51,11 +52,11 @@ The S3 backend can be used with a number of different providers:
|
|||||||
{{< provider name="Selectel" home="https://selectel.ru/services/cloud/storage/" config="/s3/#selectel" >}}
|
{{< provider name="Selectel" home="https://selectel.ru/services/cloud/storage/" config="/s3/#selectel" >}}
|
||||||
{{< provider name="Servercore Object Storage" home="https://servercore.com/services/object-storage/" config="/s3/#servercore" >}}
|
{{< provider name="Servercore Object Storage" home="https://servercore.com/services/object-storage/" config="/s3/#servercore" >}}
|
||||||
{{< provider name="Spectra Logic" home="https://spectralogic.com/blackpearl-nearline-object-gateway" config="/s3/#spectralogic" >}}
|
{{< provider name="Spectra Logic" home="https://spectralogic.com/blackpearl-nearline-object-gateway" config="/s3/#spectralogic" >}}
|
||||||
{{< provider name="StackPath" home="https://www.stackpath.com/products/object-storage/" config="/s3/#stackpath" >}}
|
|
||||||
{{< provider name="Storj" home="https://storj.io/" config="/s3/#storj" >}}
|
{{< provider name="Storj" home="https://storj.io/" config="/s3/#storj" >}}
|
||||||
{{< provider name="Synology C2 Object Storage" home="https://c2.synology.com/en-global/object-storage/overview" config="/s3/#synology-c2" >}}
|
{{< provider name="Synology C2 Object Storage" home="https://c2.synology.com/en-global/object-storage/overview" config="/s3/#synology-c2" >}}
|
||||||
{{< provider name="Tencent Cloud Object Storage (COS)" home="https://intl.cloud.tencent.com/product/cos" config="/s3/#tencent-cos" >}}
|
{{< provider name="Tencent Cloud Object Storage (COS)" home="https://intl.cloud.tencent.com/product/cos" config="/s3/#tencent-cos" >}}
|
||||||
{{< provider name="Wasabi" home="https://wasabi.com/" config="/s3/#wasabi" end="true" >}}
|
{{< provider name="Wasabi" home="https://wasabi.com/" config="/s3/#wasabi" >}}
|
||||||
|
{{< provider name="Zadara Object Storage" home="https://www.zadara.com" config="/s3/#zadara" >}}
|
||||||
{{< provider name="Zata" home="https://zata.ai/" config="/s3/#Zata" end="true" >}}
|
{{< provider name="Zata" home="https://zata.ai/" config="/s3/#Zata" end="true" >}}
|
||||||
{{< /provider_list >}}
|
{{< /provider_list >}}
|
||||||
|
|
||||||
@@ -903,10 +904,52 @@ section, small files that are not uploaded as multipart, use a different tag, ca
|
|||||||
the upload to fail. A simple solution is to set the `--s3-upload-cutoff 0` and force
|
the upload to fail. A simple solution is to set the `--s3-upload-cutoff 0` and force
|
||||||
all the files to be uploaded as multipart.
|
all the files to be uploaded as multipart.
|
||||||
|
|
||||||
|
#### Setting Object Lock retention
|
||||||
|
|
||||||
|
Rclone supports setting Object Lock retention on uploaded objects with
|
||||||
|
the following options:
|
||||||
|
|
||||||
|
- `--s3-object-lock-mode` - Set the Object Lock mode (GOVERNANCE or COMPLIANCE)
|
||||||
|
- `--s3-object-lock-retain-until-date` - Set the retention date (RFC3339 format or duration like `365d`, `24h`)
|
||||||
|
- `--s3-object-lock-legal-hold-status` - Set legal hold (ON or OFF)
|
||||||
|
|
||||||
|
Example - upload files with 1 year GOVERNANCE retention:
|
||||||
|
|
||||||
|
rclone copy local:/data remote:bucket \
|
||||||
|
--s3-object-lock-mode GOVERNANCE \
|
||||||
|
--s3-object-lock-retain-until-date 365d
|
||||||
|
|
||||||
|
#### Copying Object Lock settings from source
|
||||||
|
|
||||||
|
When using `--metadata`, you can use the special value `copy` to preserve
|
||||||
|
the source object's Object Lock settings:
|
||||||
|
|
||||||
|
rclone copy source:bucket dest:bucket \
|
||||||
|
--metadata \
|
||||||
|
--s3-object-lock-mode copy \
|
||||||
|
--s3-object-lock-retain-until-date copy
|
||||||
|
|
||||||
|
You can also mix copied and explicit values. For example, to change the
|
||||||
|
mode from COMPLIANCE to GOVERNANCE while preserving the original retention date:
|
||||||
|
|
||||||
|
rclone copy source:bucket dest:bucket \
|
||||||
|
--metadata \
|
||||||
|
--s3-object-lock-mode GOVERNANCE \
|
||||||
|
--s3-object-lock-retain-until-date copy
|
||||||
|
|
||||||
|
#### Additional Object Lock options
|
||||||
|
|
||||||
|
- `--s3-bypass-governance-retention` - Required to delete or overwrite objects
|
||||||
|
with GOVERNANCE mode retention before the retention date expires
|
||||||
|
- `--s3-bucket-object-lock-enabled` - Enable Object Lock when creating a new
|
||||||
|
bucket with `rclone mkdir`
|
||||||
|
- `--s3-object-lock-set-after-upload` - Set Object Lock via separate API calls
|
||||||
|
after upload (for providers that don't support Object Lock headers during PUT)
|
||||||
|
|
||||||
<!-- autogenerated options start - DO NOT EDIT - instead edit fs.RegInfo in backend/s3/s3.go and run make backenddocs to verify --> <!-- markdownlint-disable-line line-length -->
|
<!-- autogenerated options start - DO NOT EDIT - instead edit fs.RegInfo in backend/s3/s3.go and run make backenddocs to verify --> <!-- markdownlint-disable-line line-length -->
|
||||||
### Standard options
|
### Standard options
|
||||||
|
|
||||||
Here are the Standard options specific to s3 (Amazon S3 Compliant Storage Providers including AWS, Alibaba, ArvanCloud, BizflyCloud, Ceph, ChinaMobile, Cloudflare, Cubbit, DigitalOcean, Dreamhost, Exaba, FileLu, FlashBlade, GCS, Hetzner, HuaweiOBS, IBMCOS, IDrive, Intercolo, IONOS, Leviia, Liara, Linode, LyveCloud, Magalu, Mega, Minio, Netease, Outscale, OVHcloud, Petabox, Qiniu, Rabata, RackCorp, Rclone, Scaleway, SeaweedFS, Selectel, Servercore, SpectraLogic, StackPath, Storj, Synology, TencentCOS, Wasabi, Zata, Other).
|
Here are the Standard options specific to s3 (Amazon S3 Compliant Storage Providers including AWS, Alibaba, ArvanCloud, BizflyCloud, Ceph, ChinaMobile, Cloudflare, Cubbit, DigitalOcean, Dreamhost, Exaba, FileLu, FlashBlade, GCS, Hetzner, HuaweiOBS, IBMCOS, IDrive, Intercolo, IONOS, Leviia, Liara, Linode, LyveCloud, Magalu, Mega, Minio, Netease, Outscale, OVHcloud, Petabox, Qiniu, Rabata, RackCorp, Rclone, Scaleway, SeaweedFS, Selectel, Servercore, SpectraLogic, Storj, Synology, TencentCOS, Wasabi, Zata, Other).
|
||||||
|
|
||||||
#### --s3-provider
|
#### --s3-provider
|
||||||
|
|
||||||
@@ -999,8 +1042,6 @@ Properties:
|
|||||||
- Servercore Object Storage
|
- Servercore Object Storage
|
||||||
- "SpectraLogic"
|
- "SpectraLogic"
|
||||||
- Spectra Logic Black Pearl
|
- Spectra Logic Black Pearl
|
||||||
- "StackPath"
|
|
||||||
- StackPath Object Storage
|
|
||||||
- "Storj"
|
- "Storj"
|
||||||
- Storj (S3 Compatible Gateway)
|
- Storj (S3 Compatible Gateway)
|
||||||
- "Synology"
|
- "Synology"
|
||||||
@@ -1068,7 +1109,7 @@ Properties:
|
|||||||
|
|
||||||
- Config: region
|
- Config: region
|
||||||
- Env Var: RCLONE_S3_REGION
|
- Env Var: RCLONE_S3_REGION
|
||||||
- Provider: AWS,BizflyCloud,Ceph,Cloudflare,Cubbit,DigitalOcean,Dreamhost,Exaba,FileLu,GCS,Hetzner,HuaweiOBS,IBMCOS,Intercolo,IONOS,Leviia,LyveCloud,Minio,Netease,Outscale,OVHcloud,Petabox,Qiniu,Rabata,RackCorp,Scaleway,SeaweedFS,Selectel,Servercore,StackPath,Synology,Wasabi,Zata,Other
|
- Provider: AWS,BizflyCloud,Ceph,Cloudflare,Cubbit,DigitalOcean,Dreamhost,Exaba,FileLu,GCS,Hetzner,HuaweiOBS,IBMCOS,Intercolo,IONOS,Leviia,LyveCloud,Minio,Netease,Outscale,OVHcloud,Petabox,Qiniu,Rabata,RackCorp,Scaleway,SeaweedFS,Selectel,Servercore,Synology,Wasabi,Zata,Other
|
||||||
- Type: string
|
- Type: string
|
||||||
- Required: false
|
- Required: false
|
||||||
- Examples:
|
- Examples:
|
||||||
@@ -1186,11 +1227,11 @@ Properties:
|
|||||||
- ""
|
- ""
|
||||||
- Use this if unsure.
|
- Use this if unsure.
|
||||||
- Will use v4 signatures and an empty region.
|
- Will use v4 signatures and an empty region.
|
||||||
- Provider: Ceph,DigitalOcean,Dreamhost,Exaba,GCS,IBMCOS,Leviia,LyveCloud,Minio,Netease,SeaweedFS,StackPath,Wasabi,Other
|
- Provider: Ceph,DigitalOcean,Dreamhost,Exaba,GCS,IBMCOS,Leviia,LyveCloud,Minio,Netease,SeaweedFS,Wasabi,Other
|
||||||
- "other-v2-signature"
|
- "other-v2-signature"
|
||||||
- Use this only if v4 signatures don't work.
|
- Use this only if v4 signatures don't work.
|
||||||
- E.g. pre Jewel/v10 CEPH.
|
- E.g. pre Jewel/v10 CEPH.
|
||||||
- Provider: Ceph,DigitalOcean,Dreamhost,Exaba,GCS,IBMCOS,Leviia,LyveCloud,Minio,Netease,SeaweedFS,StackPath,Wasabi,Other
|
- Provider: Ceph,DigitalOcean,Dreamhost,Exaba,GCS,IBMCOS,Leviia,LyveCloud,Minio,Netease,SeaweedFS,Wasabi,Other
|
||||||
- "auto"
|
- "auto"
|
||||||
- R2 buckets are automatically distributed across Cloudflare's data centers for low latency.
|
- R2 buckets are automatically distributed across Cloudflare's data centers for low latency.
|
||||||
- Provider: Cloudflare
|
- Provider: Cloudflare
|
||||||
@@ -1504,7 +1545,7 @@ Properties:
|
|||||||
|
|
||||||
- Config: endpoint
|
- Config: endpoint
|
||||||
- Env Var: RCLONE_S3_ENDPOINT
|
- Env Var: RCLONE_S3_ENDPOINT
|
||||||
- Provider: AWS,Alibaba,ArvanCloud,BizflyCloud,Ceph,ChinaMobile,Cloudflare,Cubbit,DigitalOcean,Dreamhost,Exaba,FileLu,FlashBlade,GCS,Hetzner,HuaweiOBS,IBMCOS,Intercolo,IONOS,Leviia,Liara,Linode,LyveCloud,Magalu,Mega,Minio,Netease,Outscale,OVHcloud,Petabox,Qiniu,Rabata,RackCorp,Rclone,Scaleway,SeaweedFS,Selectel,Servercore,SpectraLogic,StackPath,Storj,Synology,TencentCOS,Wasabi,Zata,Other
|
- Provider: AWS,Alibaba,ArvanCloud,BizflyCloud,Ceph,ChinaMobile,Cloudflare,Cubbit,DigitalOcean,Dreamhost,Exaba,FileLu,FlashBlade,GCS,Hetzner,HuaweiOBS,IBMCOS,Intercolo,IONOS,Leviia,Liara,Linode,LyveCloud,Magalu,Mega,Minio,Netease,Outscale,OVHcloud,Petabox,Qiniu,Rabata,RackCorp,Rclone,Scaleway,SeaweedFS,Selectel,Servercore,SpectraLogic,Storj,Synology,TencentCOS,Wasabi,Zata,Other
|
||||||
- Type: string
|
- Type: string
|
||||||
- Required: false
|
- Required: false
|
||||||
- Examples:
|
- Examples:
|
||||||
@@ -2292,15 +2333,6 @@ Properties:
|
|||||||
- "s3.kz-1.srvstorage.kz"
|
- "s3.kz-1.srvstorage.kz"
|
||||||
- Almaty, Kazakhstan
|
- Almaty, Kazakhstan
|
||||||
- Provider: Servercore
|
- Provider: Servercore
|
||||||
- "s3.us-east-2.stackpathstorage.com"
|
|
||||||
- US East Endpoint
|
|
||||||
- Provider: StackPath
|
|
||||||
- "s3.us-west-1.stackpathstorage.com"
|
|
||||||
- US West Endpoint
|
|
||||||
- Provider: StackPath
|
|
||||||
- "s3.eu-central-1.stackpathstorage.com"
|
|
||||||
- EU Endpoint
|
|
||||||
- Provider: StackPath
|
|
||||||
- "gateway.storjshare.io"
|
- "gateway.storjshare.io"
|
||||||
- Global Hosted Gateway
|
- Global Hosted Gateway
|
||||||
- Provider: Storj
|
- Provider: Storj
|
||||||
@@ -2810,36 +2842,36 @@ Properties:
|
|||||||
|
|
||||||
- Config: acl
|
- Config: acl
|
||||||
- Env Var: RCLONE_S3_ACL
|
- Env Var: RCLONE_S3_ACL
|
||||||
- Provider: AWS,Alibaba,ArvanCloud,BizflyCloud,Ceph,ChinaMobile,Cubbit,DigitalOcean,Dreamhost,Exaba,FileLu,GCS,Hetzner,HuaweiOBS,IBMCOS,IDrive,Intercolo,IONOS,Leviia,Liara,Linode,LyveCloud,Magalu,Minio,Netease,Outscale,OVHcloud,Petabox,Qiniu,RackCorp,Scaleway,SeaweedFS,StackPath,TencentCOS,Wasabi,Zata,Other
|
- Provider: AWS,Alibaba,ArvanCloud,BizflyCloud,Ceph,ChinaMobile,Cubbit,DigitalOcean,Dreamhost,Exaba,FileLu,GCS,Hetzner,HuaweiOBS,IBMCOS,IDrive,Intercolo,IONOS,Leviia,Liara,Linode,LyveCloud,Magalu,Minio,Netease,Outscale,OVHcloud,Petabox,Qiniu,RackCorp,Scaleway,SeaweedFS,TencentCOS,Wasabi,Zata,Other
|
||||||
- Type: string
|
- Type: string
|
||||||
- Required: false
|
- Required: false
|
||||||
- Examples:
|
- Examples:
|
||||||
- "private"
|
- "private"
|
||||||
- Owner gets FULL_CONTROL.
|
- Owner gets FULL_CONTROL.
|
||||||
- No one else has access rights (default).
|
- No one else has access rights (default).
|
||||||
- Provider: AWS,Alibaba,ArvanCloud,BizflyCloud,Ceph,ChinaMobile,Cubbit,DigitalOcean,Dreamhost,Exaba,FileLu,GCS,Hetzner,HuaweiOBS,IDrive,Intercolo,IONOS,Leviia,Liara,Linode,LyveCloud,Magalu,Minio,Netease,Outscale,OVHcloud,Petabox,Qiniu,RackCorp,Scaleway,SeaweedFS,StackPath,Wasabi,Zata,Other
|
- Provider: AWS,Alibaba,ArvanCloud,BizflyCloud,Ceph,ChinaMobile,Cubbit,DigitalOcean,Dreamhost,Exaba,FileLu,GCS,Hetzner,HuaweiOBS,IDrive,Intercolo,IONOS,Leviia,Liara,Linode,LyveCloud,Magalu,Minio,Netease,Outscale,OVHcloud,Petabox,Qiniu,RackCorp,Scaleway,SeaweedFS,Wasabi,Zata,Other
|
||||||
- "public-read"
|
- "public-read"
|
||||||
- Owner gets FULL_CONTROL.
|
- Owner gets FULL_CONTROL.
|
||||||
- The AllUsers group gets READ access.
|
- The AllUsers group gets READ access.
|
||||||
- Provider: AWS,Alibaba,ArvanCloud,BizflyCloud,Ceph,ChinaMobile,Cubbit,DigitalOcean,Dreamhost,Exaba,FileLu,GCS,Hetzner,HuaweiOBS,IDrive,Intercolo,IONOS,Leviia,Liara,Linode,LyveCloud,Magalu,Minio,Netease,Outscale,OVHcloud,Petabox,Qiniu,RackCorp,Scaleway,SeaweedFS,StackPath,TencentCOS,Wasabi,Zata,Other
|
- Provider: AWS,Alibaba,ArvanCloud,BizflyCloud,Ceph,ChinaMobile,Cubbit,DigitalOcean,Dreamhost,Exaba,FileLu,GCS,Hetzner,HuaweiOBS,IDrive,Intercolo,IONOS,Leviia,Liara,Linode,LyveCloud,Magalu,Minio,Netease,Outscale,OVHcloud,Petabox,Qiniu,RackCorp,Scaleway,SeaweedFS,TencentCOS,Wasabi,Zata,Other
|
||||||
- "public-read-write"
|
- "public-read-write"
|
||||||
- Owner gets FULL_CONTROL.
|
- Owner gets FULL_CONTROL.
|
||||||
- The AllUsers group gets READ and WRITE access.
|
- The AllUsers group gets READ and WRITE access.
|
||||||
- Granting this on a bucket is generally not recommended.
|
- Granting this on a bucket is generally not recommended.
|
||||||
- Provider: AWS,Alibaba,ArvanCloud,BizflyCloud,Ceph,ChinaMobile,Cubbit,DigitalOcean,Dreamhost,Exaba,FileLu,GCS,Hetzner,HuaweiOBS,IDrive,Intercolo,IONOS,Leviia,Liara,Linode,LyveCloud,Magalu,Minio,Netease,Outscale,OVHcloud,Petabox,Qiniu,RackCorp,Scaleway,SeaweedFS,StackPath,TencentCOS,Wasabi,Zata,Other
|
- Provider: AWS,Alibaba,ArvanCloud,BizflyCloud,Ceph,ChinaMobile,Cubbit,DigitalOcean,Dreamhost,Exaba,FileLu,GCS,Hetzner,HuaweiOBS,IDrive,Intercolo,IONOS,Leviia,Liara,Linode,LyveCloud,Magalu,Minio,Netease,Outscale,OVHcloud,Petabox,Qiniu,RackCorp,Scaleway,SeaweedFS,TencentCOS,Wasabi,Zata,Other
|
||||||
- "authenticated-read"
|
- "authenticated-read"
|
||||||
- Owner gets FULL_CONTROL.
|
- Owner gets FULL_CONTROL.
|
||||||
- The AuthenticatedUsers group gets READ access.
|
- The AuthenticatedUsers group gets READ access.
|
||||||
- Provider: AWS,Alibaba,ArvanCloud,BizflyCloud,Ceph,ChinaMobile,Cubbit,DigitalOcean,Dreamhost,Exaba,FileLu,GCS,Hetzner,HuaweiOBS,IDrive,Intercolo,IONOS,Leviia,Liara,Linode,LyveCloud,Magalu,Minio,Netease,Outscale,OVHcloud,Petabox,Qiniu,RackCorp,Scaleway,SeaweedFS,StackPath,TencentCOS,Wasabi,Zata,Other
|
- Provider: AWS,Alibaba,ArvanCloud,BizflyCloud,Ceph,ChinaMobile,Cubbit,DigitalOcean,Dreamhost,Exaba,FileLu,GCS,Hetzner,HuaweiOBS,IDrive,Intercolo,IONOS,Leviia,Liara,Linode,LyveCloud,Magalu,Minio,Netease,Outscale,OVHcloud,Petabox,Qiniu,RackCorp,Scaleway,SeaweedFS,TencentCOS,Wasabi,Zata,Other
|
||||||
- "bucket-owner-read"
|
- "bucket-owner-read"
|
||||||
- Object owner gets FULL_CONTROL.
|
- Object owner gets FULL_CONTROL.
|
||||||
- Bucket owner gets READ access.
|
- Bucket owner gets READ access.
|
||||||
- If you specify this canned ACL when creating a bucket, Amazon S3 ignores it.
|
- If you specify this canned ACL when creating a bucket, Amazon S3 ignores it.
|
||||||
- Provider: AWS,Alibaba,ArvanCloud,BizflyCloud,Ceph,Cubbit,DigitalOcean,Dreamhost,Exaba,FileLu,GCS,Hetzner,HuaweiOBS,IDrive,Intercolo,IONOS,Leviia,Liara,Linode,LyveCloud,Magalu,Minio,Netease,Outscale,OVHcloud,Petabox,Qiniu,RackCorp,Scaleway,SeaweedFS,StackPath,TencentCOS,Wasabi,Zata,Other
|
- Provider: AWS,Alibaba,ArvanCloud,BizflyCloud,Ceph,Cubbit,DigitalOcean,Dreamhost,Exaba,FileLu,GCS,Hetzner,HuaweiOBS,IDrive,Intercolo,IONOS,Leviia,Liara,Linode,LyveCloud,Magalu,Minio,Netease,Outscale,OVHcloud,Petabox,Qiniu,RackCorp,Scaleway,SeaweedFS,TencentCOS,Wasabi,Zata,Other
|
||||||
- "bucket-owner-full-control"
|
- "bucket-owner-full-control"
|
||||||
- Both the object owner and the bucket owner get FULL_CONTROL over the object.
|
- Both the object owner and the bucket owner get FULL_CONTROL over the object.
|
||||||
- If you specify this canned ACL when creating a bucket, Amazon S3 ignores it.
|
- If you specify this canned ACL when creating a bucket, Amazon S3 ignores it.
|
||||||
- Provider: AWS,Alibaba,ArvanCloud,BizflyCloud,Ceph,Cubbit,DigitalOcean,Dreamhost,Exaba,FileLu,GCS,Hetzner,HuaweiOBS,IDrive,Intercolo,IONOS,Leviia,Liara,Linode,LyveCloud,Magalu,Minio,Netease,Outscale,OVHcloud,Petabox,Qiniu,RackCorp,Scaleway,SeaweedFS,StackPath,TencentCOS,Wasabi,Zata,Other
|
- Provider: AWS,Alibaba,ArvanCloud,BizflyCloud,Ceph,Cubbit,DigitalOcean,Dreamhost,Exaba,FileLu,GCS,Hetzner,HuaweiOBS,IDrive,Intercolo,IONOS,Leviia,Liara,Linode,LyveCloud,Magalu,Minio,Netease,Outscale,OVHcloud,Petabox,Qiniu,RackCorp,Scaleway,SeaweedFS,TencentCOS,Wasabi,Zata,Other
|
||||||
- "private"
|
- "private"
|
||||||
- Owner gets FULL_CONTROL.
|
- Owner gets FULL_CONTROL.
|
||||||
- No one else has access rights (default).
|
- No one else has access rights (default).
|
||||||
@@ -3004,7 +3036,7 @@ Properties:
|
|||||||
|
|
||||||
### Advanced options
|
### Advanced options
|
||||||
|
|
||||||
Here are the Advanced options specific to s3 (Amazon S3 Compliant Storage Providers including AWS, Alibaba, ArvanCloud, BizflyCloud, Ceph, ChinaMobile, Cloudflare, Cubbit, DigitalOcean, Dreamhost, Exaba, FileLu, FlashBlade, GCS, Hetzner, HuaweiOBS, IBMCOS, IDrive, Intercolo, IONOS, Leviia, Liara, Linode, LyveCloud, Magalu, Mega, Minio, Netease, Outscale, OVHcloud, Petabox, Qiniu, Rabata, RackCorp, Rclone, Scaleway, SeaweedFS, Selectel, Servercore, SpectraLogic, StackPath, Storj, Synology, TencentCOS, Wasabi, Zata, Other).
|
Here are the Advanced options specific to s3 (Amazon S3 Compliant Storage Providers including AWS, Alibaba, ArvanCloud, BizflyCloud, Ceph, ChinaMobile, Cloudflare, Cubbit, DigitalOcean, Dreamhost, Exaba, FileLu, FlashBlade, GCS, Hetzner, HuaweiOBS, IBMCOS, IDrive, Intercolo, IONOS, Leviia, Liara, Linode, LyveCloud, Magalu, Mega, Minio, Netease, Outscale, OVHcloud, Petabox, Qiniu, Rabata, RackCorp, Rclone, Scaleway, SeaweedFS, Selectel, Servercore, SpectraLogic, Storj, Synology, TencentCOS, Wasabi, Zata, Other).
|
||||||
|
|
||||||
#### --s3-bucket-acl
|
#### --s3-bucket-acl
|
||||||
|
|
||||||
@@ -3023,7 +3055,7 @@ Properties:
|
|||||||
|
|
||||||
- Config: bucket_acl
|
- Config: bucket_acl
|
||||||
- Env Var: RCLONE_S3_BUCKET_ACL
|
- Env Var: RCLONE_S3_BUCKET_ACL
|
||||||
- Provider: AWS,Alibaba,ArvanCloud,BizflyCloud,Ceph,ChinaMobile,Cubbit,DigitalOcean,Dreamhost,Exaba,FileLu,GCS,Hetzner,HuaweiOBS,IBMCOS,IDrive,Intercolo,IONOS,Leviia,Liara,Linode,LyveCloud,Magalu,Mega,Minio,Netease,Outscale,OVHcloud,Petabox,Qiniu,RackCorp,Scaleway,SeaweedFS,Servercore,StackPath,TencentCOS,Wasabi,Zata,Other
|
- Provider: AWS,Alibaba,ArvanCloud,BizflyCloud,Ceph,ChinaMobile,Cubbit,DigitalOcean,Dreamhost,Exaba,FileLu,GCS,Hetzner,HuaweiOBS,IBMCOS,IDrive,Intercolo,IONOS,Leviia,Liara,Linode,LyveCloud,Magalu,Mega,Minio,Netease,Outscale,OVHcloud,Petabox,Qiniu,RackCorp,Scaleway,SeaweedFS,Servercore,TencentCOS,Wasabi,Zata,Other
|
||||||
- Type: string
|
- Type: string
|
||||||
- Required: false
|
- Required: false
|
||||||
- Examples:
|
- Examples:
|
||||||
@@ -4992,7 +5024,7 @@ Option Storage.
|
|||||||
Type of storage to configure.
|
Type of storage to configure.
|
||||||
Choose a number from below, or type in your own value.
|
Choose a number from below, or type in your own value.
|
||||||
...
|
...
|
||||||
XX / Amazon S3 Compliant Storage Providers including AWS, Alibaba, Ceph, China Mobile, Cloudflare, ArvanCloud, DigitalOcean, Dreamhost, Huawei OBS, IBM COS, Lyve Cloud, Minio, Magalu, Netease, RackCorp, Scaleway, SeaweedFS, StackPath, Storj, Synology, Tencent COS and Wasabi
|
XX / Amazon S3 Compliant Storage Providers including AWS, Alibaba, Ceph, China Mobile, Cloudflare, ArvanCloud, DigitalOcean, Dreamhost, Huawei OBS, IBM COS, Lyve Cloud, Minio, Magalu, Netease, RackCorp, Scaleway, SeaweedFS, Storj, Synology, Tencent COS and Wasabi
|
||||||
\ (s3)
|
\ (s3)
|
||||||
...
|
...
|
||||||
Storage> s3
|
Storage> s3
|
||||||
@@ -5279,6 +5311,92 @@ secret_access_key = XXX
|
|||||||
endpoint = http://127.0.0.1:9000/
|
endpoint = http://127.0.0.1:9000/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Fastly Object Storage {#fastly}
|
||||||
|
|
||||||
|
[Fastly Object Storage](https://www.fastly.com/products/storage) is an
|
||||||
|
S3-compatible object storage service from Fastly. It provides three
|
||||||
|
regions (US East, US West, and EU Central) with mandatory server-side
|
||||||
|
encryption.
|
||||||
|
|
||||||
|
Here is an example of making a configuration. First run:
|
||||||
|
|
||||||
|
```console
|
||||||
|
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> fastly
|
||||||
|
|
||||||
|
Option Storage.
|
||||||
|
Type of storage to configure.
|
||||||
|
Storage> s3
|
||||||
|
|
||||||
|
Option provider.
|
||||||
|
Choose your S3 provider.
|
||||||
|
provider> Fastly
|
||||||
|
|
||||||
|
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.
|
||||||
|
env_auth> false
|
||||||
|
|
||||||
|
Option access_key_id.
|
||||||
|
AWS Access Key ID.
|
||||||
|
access_key_id> YOUR_ACCESS_KEY
|
||||||
|
|
||||||
|
Option secret_access_key.
|
||||||
|
AWS Secret Access Key (password).
|
||||||
|
secret_access_key> YOUR_SECRET_KEY
|
||||||
|
|
||||||
|
Option region.
|
||||||
|
Region where your bucket will be created and your data stored.
|
||||||
|
region> us-east
|
||||||
|
|
||||||
|
Option endpoint.
|
||||||
|
Endpoint for S3 API.
|
||||||
|
endpoint> us-east.object.fastlystorage.app
|
||||||
|
|
||||||
|
Edit advanced config?
|
||||||
|
y) Yes
|
||||||
|
n) No (default)
|
||||||
|
y/n> n
|
||||||
|
|
||||||
|
Configuration complete.
|
||||||
|
Options:
|
||||||
|
- type: s3
|
||||||
|
- provider: Fastly
|
||||||
|
- access_key_id: YOUR_ACCESS_KEY
|
||||||
|
- secret_access_key: YOUR_SECRET_KEY
|
||||||
|
- region: us-east
|
||||||
|
- endpoint: us-east.object.fastlystorage.app
|
||||||
|
Keep this "fastly" remote?
|
||||||
|
y) Yes this is OK (default)
|
||||||
|
e) Edit this remote
|
||||||
|
d) Delete this remote
|
||||||
|
y/e/d> y
|
||||||
|
```
|
||||||
|
|
||||||
|
The resulting configuration file should look like:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[fastly]
|
||||||
|
type = s3
|
||||||
|
provider = Fastly
|
||||||
|
access_key_id = YOUR_ACCESS_KEY
|
||||||
|
secret_access_key = YOUR_SECRET_KEY
|
||||||
|
region = us-east
|
||||||
|
endpoint = us-east.object.fastlystorage.app
|
||||||
|
```
|
||||||
|
|
||||||
### Google Cloud Storage
|
### Google Cloud Storage
|
||||||
|
|
||||||
[GoogleCloudStorage](https://cloud.google.com/storage/docs) is an
|
[GoogleCloudStorage](https://cloud.google.com/storage/docs) is an
|
||||||
@@ -5330,7 +5448,7 @@ Option Storage.
|
|||||||
Type of storage to configure.
|
Type of storage to configure.
|
||||||
Choose a number from below, or type in your own value.
|
Choose a number from below, or type in your own value.
|
||||||
[snip]
|
[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
|
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, Storj, Synology, TencentCOS, Wasabi, Qiniu and others
|
||||||
\ (s3)
|
\ (s3)
|
||||||
[snip]
|
[snip]
|
||||||
Storage> s3
|
Storage> s3
|
||||||
@@ -7112,7 +7230,7 @@ Option Storage.
|
|||||||
Type of storage to configure.
|
Type of storage to configure.
|
||||||
Choose a number from below, or type in your own value.
|
Choose a number from below, or type in your own value.
|
||||||
[...]
|
[...]
|
||||||
XX / Amazon S3 Compliant Storage Providers including AWS, Alibaba, ArvanCloud, Ceph, ChinaMobile, Cloudflare, DigitalOcean, Dreamhost, GCS, HuaweiOBS, IBMCOS, IDrive, IONOS, LyveCloud, Leviia, Liara, Linode, Magalu, Minio, Netease, Outscale, OVHcloud, Petabox, RackCorp, Rclone, Scaleway, SeaweedFS, Selectel, StackPath, Storj, Synology, TencentCOS, Wasabi, Qiniu and others
|
XX / Amazon S3 Compliant Storage Providers including AWS, Alibaba, ArvanCloud, Ceph, ChinaMobile, Cloudflare, DigitalOcean, Dreamhost, GCS, HuaweiOBS, IBMCOS, IDrive, IONOS, LyveCloud, Leviia, Liara, Linode, Magalu, Minio, Netease, Outscale, OVHcloud, Petabox, RackCorp, Rclone, Scaleway, SeaweedFS, Selectel, Storj, Synology, TencentCOS, Wasabi, Qiniu and others
|
||||||
\ (s3)
|
\ (s3)
|
||||||
[...]
|
[...]
|
||||||
Storage> s3
|
Storage> s3
|
||||||
@@ -7486,7 +7604,7 @@ Option Storage.
|
|||||||
Type of storage to configure.
|
Type of storage to configure.
|
||||||
Choose a number from below, or type in your own value.
|
Choose a number from below, or type in your own value.
|
||||||
[snip]
|
[snip]
|
||||||
4 / Amazon S3 Compliant Storage Providers including AWS, Alibaba, ArvanCloud, Ceph, ChinaMobile, Cloudflare, DigitalOcean, Dreamhost, FlashBlade, GCS, 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
|
4 / Amazon S3 Compliant Storage Providers including AWS, Alibaba, ArvanCloud, Ceph, ChinaMobile, Cloudflare, DigitalOcean, Dreamhost, FlashBlade, GCS, HuaweiOBS, IBMCOS, IDrive, IONOS, LyveCloud, Leviia, Liara, Linode, Magalu, Minio, Netease, Outscale, Petabox, RackCorp, Rclone, Scaleway, SeaweedFS, Selectel, Storj, Synology, TencentCOS, Wasabi, Qiniu and others
|
||||||
\ (s3)
|
\ (s3)
|
||||||
[snip]
|
[snip]
|
||||||
Storage> s3
|
Storage> s3
|
||||||
@@ -9099,6 +9217,125 @@ server_side_encryption =
|
|||||||
storage_class =
|
storage_class =
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Zadara Object Storage {#zadara}
|
||||||
|
|
||||||
|
[Zadara Object Storage](https://www.zadara.com) is a fully-managed,
|
||||||
|
enterprise-grade, S3-compatible storage solution that provides scalable,
|
||||||
|
multi-tenant object storage with flexible deployment options (on-prem,
|
||||||
|
hybrid, or cloud).
|
||||||
|
|
||||||
|
Zadara Object Storage can be configured using `rclone config` with `s3` as
|
||||||
|
the type and `Zadara` as the provider name. Here is an example
|
||||||
|
run of the configurator.
|
||||||
|
|
||||||
|
Authentication and endpoint information, including the region field,
|
||||||
|
should be fetched from Zadara’s Object Storage management interface
|
||||||
|
|
||||||
|
To configure access to Zadara Object Storage, first run:
|
||||||
|
|
||||||
|
```console
|
||||||
|
rclone config
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
This will guide you through an interactive setup process:
|
||||||
|
|
||||||
|
No remotes found, make a new one\?
|
||||||
|
n) New remote
|
||||||
|
s) Set configuration password
|
||||||
|
n/s> n
|
||||||
|
|
||||||
|
Enter name for new remote.
|
||||||
|
name> Zadara-Object-Storage
|
||||||
|
|
||||||
|
Option Storage.
|
||||||
|
Type of storage to configure.
|
||||||
|
Choose a number from below, or type in your own value.
|
||||||
|
|
||||||
|
XX / Amazon S3 Compliant Storage Providers including AWS ...
|
||||||
|
\ (s3)
|
||||||
|
|
||||||
|
Storage> s3
|
||||||
|
|
||||||
|
Option provider.
|
||||||
|
Choose your S3 provider.
|
||||||
|
Choose a number from below, or type in your own value.
|
||||||
|
Press Enter to leave empty.
|
||||||
|
|
||||||
|
XX / Zadara Object Storage
|
||||||
|
\ (Zadara)
|
||||||
|
|
||||||
|
provider> Zadara
|
||||||
|
|
||||||
|
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> S3_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> S3_SECRET_KEY
|
||||||
|
|
||||||
|
Option region.
|
||||||
|
Region to connect to.
|
||||||
|
Leave blank if you are using an S3 clone and you don't have a region.
|
||||||
|
Choose a number from below, or type in your own value.
|
||||||
|
Press Enter to leave empty.
|
||||||
|
/ The default region.
|
||||||
|
1 | Leave location constraint empty.
|
||||||
|
\ (us-east-1)
|
||||||
|
region>
|
||||||
|
|
||||||
|
Option endpoint.
|
||||||
|
Endpoint for S3 API.
|
||||||
|
Required when using an S3 clone.
|
||||||
|
Enter a value. Press Enter to leave empty.
|
||||||
|
endpoint> https://vsa-00000001-public-zadara-cloud-01.zadarazios.com
|
||||||
|
|
||||||
|
Edit advanced config?
|
||||||
|
y) Yes
|
||||||
|
n) No (default)
|
||||||
|
y/n>
|
||||||
|
|
||||||
|
Configuration complete.
|
||||||
|
Options:
|
||||||
|
- type: s3
|
||||||
|
- provider: Zadara
|
||||||
|
- access_key_id: S3_ACCESS_KEY
|
||||||
|
- secret_access_key: S3_SECRET_KEY
|
||||||
|
- endpoint: https://vsa-00000001-public-zadara-cloud-01.zadarazios.com
|
||||||
|
Keep this "Zadara-Object-Storage" 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.
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Zadara-Object-Storage]
|
||||||
|
type = s3
|
||||||
|
provider = Zadara
|
||||||
|
access_key_id = S3_ACCESS_KEY
|
||||||
|
secret_access_key = S3_SECRET_KEY
|
||||||
|
endpoint = https://vsa-00000001-public-zadara-cloud-01.zadarazios.com
|
||||||
|
```
|
||||||
|
|
||||||
### Zata Object Storage {#Zata}
|
### Zata Object Storage {#Zata}
|
||||||
|
|
||||||
[Zata Object Storage](https://zata.ai/) provides a secure, S3-compatible cloud
|
[Zata Object Storage](https://zata.ai/) provides a secure, S3-compatible cloud
|
||||||
|
|||||||
@@ -16,16 +16,18 @@ Thank you to our sponsors:
|
|||||||
{{< sponsor src="/img/logos/rabata.svg" width="300" height="200" title="Visit our sponsor Rabata.io" link="https://rabata.io/?utm_source=banner&utm_medium=rclone&utm_content=general">}}
|
{{< sponsor src="/img/logos/rabata.svg" width="300" height="200" title="Visit our sponsor Rabata.io" link="https://rabata.io/?utm_source=banner&utm_medium=rclone&utm_content=general">}}
|
||||||
{{< sponsor src="/img/logos/idrive_e2.svg" width="300" height="200" title="Visit our sponsor IDrive e2" link="https://www.idrive.com/e2/?refer=rclone">}}
|
{{< sponsor src="/img/logos/idrive_e2.svg" width="300" height="200" title="Visit our sponsor IDrive e2" link="https://www.idrive.com/e2/?refer=rclone">}}
|
||||||
{{< sponsor src="/img/logos/filescom-enterprise-grade-workflows.png" width="300" height="200" title="Start Your Free Trial Today" link="https://files.com/?utm_source=rclone&utm_medium=referral&utm_campaign=banner&utm_term=rclone">}}
|
{{< sponsor src="/img/logos/filescom-enterprise-grade-workflows.png" width="300" height="200" title="Start Your Free Trial Today" link="https://files.com/?utm_source=rclone&utm_medium=referral&utm_campaign=banner&utm_term=rclone">}}
|
||||||
|
{{< sponsor src="/img/logos/internxt.jpg" width="300" height="200" title="Visit rclone's sponsor Internxt" link="https://internxt.com/specialoffer/rclone">}}
|
||||||
{{< sponsor src="/img/logos/mega-s4.svg" width="300" height="200" title="MEGA S4: New S3 compatible object storage. High scale. Low cost. Free egress." link="https://mega.io/objectstorage?utm_source=rclone&utm_medium=referral&utm_campaign=rclone-mega-s4&mct=rclonepromo">}}
|
{{< sponsor src="/img/logos/mega-s4.svg" width="300" height="200" title="MEGA S4: New S3 compatible object storage. High scale. Low cost. Free egress." link="https://mega.io/objectstorage?utm_source=rclone&utm_medium=referral&utm_campaign=rclone-mega-s4&mct=rclonepromo">}}
|
||||||
{{< sponsor src="/img/logos/sia.svg" width="200" height="200" title="Visit our sponsor sia" link="https://sia.tech">}}
|
{{< sponsor src="/img/logos/sia.svg" width="200" height="200" title="Visit our sponsor sia" link="https://sia.tech">}}
|
||||||
{{< sponsor src="/img/logos/route4me.svg" width="400" height="200" title="Visit our sponsor Route4Me" link="https://route4me.com/">}}
|
{{< sponsor src="/img/logos/route4me.svg" width="400" height="200" title="Visit our sponsor Route4Me" link="https://route4me.com/">}}
|
||||||
{{< sponsor src="/img/logos/rcloneview-banner.svg" width="300" height="200" title="Visit our sponsor RcloneView" link="https://rcloneview.com/">}}
|
{{< sponsor src="/img/logos/rcloneview.svg" width="300" height="200" title="Visit our sponsor RcloneView" link="https://rcloneview.com/">}}
|
||||||
{{< sponsor src="/img/logos/rcloneui.svg" width="300" height="200" title="Visit our sponsor RcloneUI" link="https://github.com/rclone-ui/rclone-ui">}}
|
{{< sponsor src="/img/logos/rcloneui.svg" width="300" height="200" title="Visit our sponsor RcloneUI" link="https://github.com/rclone-ui/rclone-ui">}}
|
||||||
{{< sponsor src="/img/logos/shade.svg" width="300" height="200" title="Visit our sponsor Shade" link="https://shade.inc">}}
|
{{< sponsor src="/img/logos/shade.svg" width="300" height="200" title="Visit our sponsor Shade" link="https://shade.inc">}}
|
||||||
{{< sponsor src="/img/logos/filelu-rclone.svg" width="300" height="200" title="Visit our sponsor FileLu" link="https://filelu.com/">}}
|
{{< sponsor src="/img/logos/filelu-rclone.svg" width="300" height="200" title="Visit our sponsor FileLu" link="https://filelu.com/">}}
|
||||||
{{< sponsor src="/img/logos/torbox.png" width="200" height="200" title="Visit our sponsor TORBOX" link="https://www.torbox.app/">}}
|
{{< sponsor src="/img/logos/torbox.png" width="200" height="200" title="Visit our sponsor TORBOX" link="https://www.torbox.app/">}}
|
||||||
{{< sponsor src="/img/logos/spectra-logic.svg" width="300" height="200" title="Visit our sponsor Spectra Logic" link="https://spectralogic.com/">}}
|
{{< sponsor src="/img/logos/spectra-logic.svg" width="300" height="200" title="Visit our sponsor Spectra Logic" link="https://spectralogic.com/">}}
|
||||||
{{< sponsor src="/img/logos/servercore.svg" width="300" height="200" title="Visit our sponsor servercore" link="https://servercore.com/services/object-storage/?utm_source=rclone.org&utm_medium=referral&utm_campaign=cloud-s3_rclone_231025_paid">}}
|
{{< sponsor src="/img/logos/servercore.svg" width="300" height="200" title="Visit our sponsor servercore" link="https://servercore.com/services/object-storage/?utm_source=rclone.org&utm_medium=referral&utm_campaign=cloud-s3_rclone_231025_paid">}}
|
||||||
|
{{< sponsor src="/img/logos/exchangerate-api.png" width="300" height="200" title="Visit our sponsor ExchangeRate-API" link="https://www.exchangerate-api.com/">}}
|
||||||
|
|
||||||
<!-- markdownlint-restore -->
|
<!-- markdownlint-restore -->
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ security: High
|
|||||||
virtual: false
|
virtual: false
|
||||||
remote: 'TestDrime:'
|
remote: 'TestDrime:'
|
||||||
features:
|
features:
|
||||||
|
- About
|
||||||
- CanHaveEmptyDirectories
|
- CanHaveEmptyDirectories
|
||||||
- Copy
|
- Copy
|
||||||
- DirCacheFlush
|
- DirCacheFlush
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">Platinum Sponsor</div>
|
<div class="card-header">Platinum Sponsor</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<a href="https://rabata.io/?utm_source=banner&utm_medium=rclone&utm_content=general" target="_blank" rel="noopener" title="Visit rclone's sponsor Rabata.io"><img src="/img/logos/rabata.svg"></a><br />
|
<a href="https://internxt.com/specialoffer/rclone" target="_blank" rel="noopener" title="Visit rclone's sponsor Internxt"><img style="max-width: 100%; height: auto;" src="/img/logos/internxt.jpg"></a><br />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -30,13 +30,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">Gold Sponsor</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<a href="https://internxt.com/?utm_source=rclone" target="_blank" rel="noopener" title="Visit rclone's sponsor Internxt"><img style="max-width: 100%; height: auto;" src="/img/logos/internxt.jpg"></a><br />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{if .IsHome}}
|
{{if .IsHome}}
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">Silver Sponsor</div>
|
<div class="card-header">Silver Sponsor</div>
|
||||||
@@ -44,12 +37,6 @@
|
|||||||
<a href="https://rcloneview.com/?utm_source=rclone" target="_blank" rel="noopener" title="Visit rclone's sponsor RcloneView"><img src="/img/logos/rcloneview.svg"></a><br />
|
<a href="https://rcloneview.com/?utm_source=rclone" target="_blank" rel="noopener" title="Visit rclone's sponsor RcloneView"><img src="/img/logos/rcloneview.svg"></a><br />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">Silver Sponsor</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<a href="https://rcloneui.com" target="_blank" rel="noopener" title="Visit rclone's sponsor rclone UI"><img src="/img/logos/rcloneui.svg"></a><br />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">Silver Sponsor</div>
|
<div class="card-header">Silver Sponsor</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
v1.73.0
|
v1.74.0
|
||||||
@@ -575,8 +575,11 @@ func (acc *Account) String() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var displaySpeedString string
|
||||||
if acc.ci.DataRateUnit == "bits" {
|
if acc.ci.DataRateUnit == "bits" {
|
||||||
cur *= 8
|
displaySpeedString = fs.SizeSuffix(cur * 8).BitRateUnit()
|
||||||
|
} else {
|
||||||
|
displaySpeedString = fs.SizeSuffix(cur).ByteRateUnit()
|
||||||
}
|
}
|
||||||
|
|
||||||
percentageDone := 0
|
percentageDone := 0
|
||||||
@@ -584,12 +587,12 @@ func (acc *Account) String() string {
|
|||||||
percentageDone = int(100 * float64(a) / float64(b))
|
percentageDone = int(100 * float64(a) / float64(b))
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("%*s:%3d%% /%s, %s/s, %s",
|
return fmt.Sprintf("%*s:%3d%% / %s, %s, %s",
|
||||||
acc.ci.StatsFileNameLength,
|
acc.ci.StatsFileNameLength,
|
||||||
shortenName(acc.name, acc.ci.StatsFileNameLength),
|
shortenName(acc.name, acc.ci.StatsFileNameLength),
|
||||||
percentageDone,
|
percentageDone,
|
||||||
fs.SizeSuffix(b),
|
fs.SizeSuffix(b).ByteUnit(),
|
||||||
fs.SizeSuffix(cur),
|
displaySpeedString,
|
||||||
etas,
|
etas,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,14 +183,14 @@ func TestAccountString(t *testing.T) {
|
|||||||
|
|
||||||
// FIXME not an exhaustive test!
|
// FIXME not an exhaustive test!
|
||||||
|
|
||||||
assert.Equal(t, "test: 0% /3, 0/s, -", strings.TrimSpace(acc.String()))
|
assert.Equal(t, "test: 0% / 3 B, 0 B/s, -", strings.TrimSpace(acc.String()))
|
||||||
|
|
||||||
var buf = make([]byte, 2)
|
var buf = make([]byte, 2)
|
||||||
n, err := acc.Read(buf)
|
n, err := acc.Read(buf)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, 2, n)
|
assert.Equal(t, 2, n)
|
||||||
|
|
||||||
assert.Equal(t, "test: 66% /3, 0/s, -", strings.TrimSpace(acc.String()))
|
assert.Equal(t, "test: 66% / 3 B, 0 B/s, -", strings.TrimSpace(acc.String()))
|
||||||
|
|
||||||
assert.NoError(t, acc.Close())
|
assert.NoError(t, acc.Close())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -385,12 +385,14 @@ func (sg *statsGroups) sum(ctx context.Context) *StatsInfo {
|
|||||||
sum.checkQueueSize += stats.checkQueueSize
|
sum.checkQueueSize += stats.checkQueueSize
|
||||||
sum.transfers += stats.transfers
|
sum.transfers += stats.transfers
|
||||||
sum.transferring.merge(stats.transferring)
|
sum.transferring.merge(stats.transferring)
|
||||||
|
sum.transferQueue += stats.transferQueue
|
||||||
sum.transferQueueSize += stats.transferQueueSize
|
sum.transferQueueSize += stats.transferQueueSize
|
||||||
sum.listed += stats.listed
|
sum.listed += stats.listed
|
||||||
sum.renames += stats.renames
|
sum.renames += stats.renames
|
||||||
sum.renameQueue += stats.renameQueue
|
sum.renameQueue += stats.renameQueue
|
||||||
sum.renameQueueSize += stats.renameQueueSize
|
sum.renameQueueSize += stats.renameQueueSize
|
||||||
sum.deletes += stats.deletes
|
sum.deletes += stats.deletes
|
||||||
|
sum.deletesSize += stats.deletesSize
|
||||||
sum.deletedDirs += stats.deletedDirs
|
sum.deletedDirs += stats.deletedDirs
|
||||||
sum.inProgress.merge(stats.inProgress)
|
sum.inProgress.merge(stats.inProgress)
|
||||||
sum.startedTransfers = append(sum.startedTransfers, stats.startedTransfers...)
|
sum.startedTransfers = append(sum.startedTransfers, stats.startedTransfers...)
|
||||||
@@ -399,6 +401,10 @@ func (sg *statsGroups) sum(ctx context.Context) *StatsInfo {
|
|||||||
stats.average.mu.Lock()
|
stats.average.mu.Lock()
|
||||||
sum.average.speed += stats.average.speed
|
sum.average.speed += stats.average.speed
|
||||||
stats.average.mu.Unlock()
|
stats.average.mu.Unlock()
|
||||||
|
sum.serverSideCopies += stats.serverSideCopies
|
||||||
|
sum.serverSideCopyBytes += stats.serverSideCopyBytes
|
||||||
|
sum.serverSideMoves += stats.serverSideMoves
|
||||||
|
sum.serverSideMoveBytes += stats.serverSideMoveBytes
|
||||||
}
|
}
|
||||||
stats.mu.RUnlock()
|
stats.mu.RUnlock()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ type TransferSnapshot struct {
|
|||||||
Checked bool `json:"checked"`
|
Checked bool `json:"checked"`
|
||||||
What string `json:"what"`
|
What string `json:"what"`
|
||||||
StartedAt time.Time `json:"started_at"`
|
StartedAt time.Time `json:"started_at"`
|
||||||
CompletedAt time.Time `json:"completed_at,omitempty"`
|
CompletedAt time.Time `json:"completed_at"`
|
||||||
Error error `json:"-"`
|
Error error `json:"-"`
|
||||||
Group string `json:"group"`
|
Group string `json:"group"`
|
||||||
SrcFs string `json:"srcFs,omitempty"`
|
SrcFs string `json:"srcFs,omitempty"`
|
||||||
|
|||||||
@@ -265,9 +265,7 @@ func testAsyncReaderClose(t *testing.T, writeto bool) {
|
|||||||
var copyErr error
|
var copyErr error
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
started := make(chan struct{})
|
started := make(chan struct{})
|
||||||
wg.Add(1)
|
wg.Go(func() {
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
close(started)
|
close(started)
|
||||||
if writeto {
|
if writeto {
|
||||||
// exercise the WriteTo path
|
// exercise the WriteTo path
|
||||||
@@ -284,7 +282,7 @@ func testAsyncReaderClose(t *testing.T, writeto bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
// Do some copying
|
// Do some copying
|
||||||
<-started
|
<-started
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ type Item struct {
|
|||||||
// struct, otherwise they will be embedded as they are.
|
// struct, otherwise they will be embedded as they are.
|
||||||
func Items(opt any) (items []Item, err error) {
|
func Items(opt any) (items []Item, err error) {
|
||||||
def := reflect.ValueOf(opt)
|
def := reflect.ValueOf(opt)
|
||||||
if def.Kind() != reflect.Ptr {
|
if def.Kind() != reflect.Pointer {
|
||||||
return nil, errors.New("argument must be a pointer")
|
return nil, errors.New("argument must be a pointer")
|
||||||
}
|
}
|
||||||
def = def.Elem() // indirect the pointer
|
def = def.Elem() // indirect the pointer
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ func Trace(o any, format string, a ...any) func(string, ...any) {
|
|||||||
for i := range a {
|
for i := range a {
|
||||||
// read the values of the pointed to items
|
// read the values of the pointed to items
|
||||||
typ := reflect.TypeOf(a[i])
|
typ := reflect.TypeOf(a[i])
|
||||||
if typ.Kind() == reflect.Ptr {
|
if typ.Kind() == reflect.Pointer {
|
||||||
value := reflect.ValueOf(a[i])
|
value := reflect.ValueOf(a[i])
|
||||||
if value.IsNil() {
|
if value.IsNil() {
|
||||||
a[i] = nil
|
a[i] = nil
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/rclone/rclone/fs/list"
|
"github.com/rclone/rclone/fs/list"
|
||||||
"github.com/rclone/rclone/fs/walk"
|
"github.com/rclone/rclone/fs/walk"
|
||||||
"github.com/rclone/rclone/lib/transform"
|
"github.com/rclone/rclone/lib/transform"
|
||||||
|
"golang.org/x/sync/semaphore"
|
||||||
"golang.org/x/text/unicode/norm"
|
"golang.org/x/text/unicode/norm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -41,9 +42,10 @@ type March struct {
|
|||||||
NoCheckDest bool // transfer all objects regardless without checking dst
|
NoCheckDest bool // transfer all objects regardless without checking dst
|
||||||
NoUnicodeNormalization bool // don't normalize unicode characters in filenames
|
NoUnicodeNormalization bool // don't normalize unicode characters in filenames
|
||||||
// internal state
|
// internal state
|
||||||
srcListDir listDirFn // function to call to list a directory in the src
|
srcListDir listDirFn // function to call to list a directory in the src
|
||||||
dstListDir listDirFn // function to call to list a directory in the dst
|
dstListDir listDirFn // function to call to list a directory in the dst
|
||||||
transforms []matchTransformFn
|
transforms []matchTransformFn
|
||||||
|
newObjectSem *semaphore.Weighted // make sure we don't call too many NewObjects simultaneously
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marcher is called on each match
|
// Marcher is called on each match
|
||||||
@@ -78,6 +80,8 @@ func (m *March) init(ctx context.Context) {
|
|||||||
if m.Fdst.Features().CaseInsensitive || ci.IgnoreCaseSync {
|
if m.Fdst.Features().CaseInsensitive || ci.IgnoreCaseSync {
|
||||||
m.transforms = append(m.transforms, strings.ToLower)
|
m.transforms = append(m.transforms, strings.ToLower)
|
||||||
}
|
}
|
||||||
|
// Only allow ci.Checkers simultaneous calls to NewObject
|
||||||
|
m.newObjectSem = semaphore.NewWeighted(int64(ci.Checkers))
|
||||||
}
|
}
|
||||||
|
|
||||||
// srcOrDstKey turns a directory entry into a sort key using the defined transforms.
|
// srcOrDstKey turns a directory entry into a sort key using the defined transforms.
|
||||||
@@ -201,9 +205,7 @@ func (m *March) Run(ctx context.Context) error {
|
|||||||
checkers := ci.Checkers
|
checkers := ci.Checkers
|
||||||
in := make(chan listDirJob, checkers)
|
in := make(chan listDirJob, checkers)
|
||||||
for range checkers {
|
for range checkers {
|
||||||
wg.Add(1)
|
wg.Go(func() {
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-m.Ctx.Done():
|
case <-m.Ctx.Done():
|
||||||
@@ -240,7 +242,7 @@ func (m *March) Run(ctx context.Context) error {
|
|||||||
traversing.Done()
|
traversing.Done()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the process
|
// Start the process
|
||||||
@@ -389,9 +391,7 @@ func (m *March) processJob(job listDirJob) ([]listDirJob, error) {
|
|||||||
// List the src and dst directories
|
// List the src and dst directories
|
||||||
if !job.noSrc {
|
if !job.noSrc {
|
||||||
srcChan := srcChan // duplicate this as we may override it later
|
srcChan := srcChan // duplicate this as we may override it later
|
||||||
wg.Add(1)
|
wg.Go(func() {
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
srcListErr = m.srcListDir(job.srcRemote, func(entries fs.DirEntries) error {
|
srcListErr = m.srcListDir(job.srcRemote, func(entries fs.DirEntries) error {
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
srcChan <- entry
|
srcChan <- entry
|
||||||
@@ -399,16 +399,14 @@ func (m *March) processJob(job listDirJob) ([]listDirJob, error) {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
close(srcChan)
|
close(srcChan)
|
||||||
}()
|
})
|
||||||
} else {
|
} else {
|
||||||
close(srcChan)
|
close(srcChan)
|
||||||
}
|
}
|
||||||
startedDst := false
|
startedDst := false
|
||||||
if !m.NoTraverse && !job.noDst {
|
if !m.NoTraverse && !job.noDst {
|
||||||
startedDst = true
|
startedDst = true
|
||||||
wg.Add(1)
|
wg.Go(func() {
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
dstListErr = m.dstListDir(job.dstRemote, func(entries fs.DirEntries) error {
|
dstListErr = m.dstListDir(job.dstRemote, func(entries fs.DirEntries) error {
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
dstChan <- entry
|
dstChan <- entry
|
||||||
@@ -416,7 +414,7 @@ func (m *March) processJob(job listDirJob) ([]listDirJob, error) {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
close(dstChan)
|
close(dstChan)
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
// If NoTraverse is set, then try to find a matching object
|
// If NoTraverse is set, then try to find a matching object
|
||||||
// for each item in the srcList to head dst object
|
// for each item in the srcList to head dst object
|
||||||
@@ -451,9 +449,7 @@ func (m *March) processJob(job listDirJob) ([]listDirJob, error) {
|
|||||||
// Get the tasks from the queue and find a matching object.
|
// Get the tasks from the queue and find a matching object.
|
||||||
var workerWg sync.WaitGroup
|
var workerWg sync.WaitGroup
|
||||||
for range workers {
|
for range workers {
|
||||||
workerWg.Add(1)
|
workerWg.Go(func() {
|
||||||
go func() {
|
|
||||||
defer workerWg.Done()
|
|
||||||
for t := range matchTasks {
|
for t := range matchTasks {
|
||||||
// Can't match directories with NewObject
|
// Can't match directories with NewObject
|
||||||
if _, ok := t.src.(fs.Object); !ok {
|
if _, ok := t.src.(fs.Object); !ok {
|
||||||
@@ -461,13 +457,18 @@ func (m *March) processJob(job listDirJob) ([]listDirJob, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
leaf := path.Base(t.src.Remote())
|
leaf := path.Base(t.src.Remote())
|
||||||
|
if err := m.newObjectSem.Acquire(m.Ctx, 1); err != nil {
|
||||||
|
t.dstMatch <- nil
|
||||||
|
continue
|
||||||
|
}
|
||||||
dst, err := m.Fdst.NewObject(m.Ctx, path.Join(job.dstRemote, leaf))
|
dst, err := m.Fdst.NewObject(m.Ctx, path.Join(job.dstRemote, leaf))
|
||||||
|
m.newObjectSem.Release(1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dst = nil
|
dst = nil
|
||||||
}
|
}
|
||||||
t.dstMatch <- dst
|
t.dstMatch <- dst
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close dstResults when all the workers have finished
|
// Close dstResults when all the workers have finished
|
||||||
@@ -477,9 +478,7 @@ func (m *March) processJob(job listDirJob) ([]listDirJob, error) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
// Read the matches in order and send them to dstChan if found.
|
// Read the matches in order and send them to dstChan if found.
|
||||||
wg.Add(1)
|
wg.Go(func() {
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
for dstMatch := range dstMatches {
|
for dstMatch := range dstMatches {
|
||||||
dst := <-dstMatch
|
dst := <-dstMatch
|
||||||
// Note that dst may be nil here
|
// Note that dst may be nil here
|
||||||
@@ -488,7 +487,7 @@ func (m *March) processJob(job listDirJob) ([]listDirJob, error) {
|
|||||||
}
|
}
|
||||||
close(srcChan)
|
close(srcChan)
|
||||||
close(dstChan)
|
close(dstChan)
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
if !startedDst {
|
if !startedDst {
|
||||||
close(dstChan)
|
close(dstChan)
|
||||||
|
|||||||
@@ -508,9 +508,7 @@ func TestMatchListings(t *testing.T) {
|
|||||||
}
|
}
|
||||||
ls, err := list.NewSorter(ctx, nil, list.SortToChan(out), key)
|
ls, err := list.NewSorter(ctx, nil, list.SortToChan(out), key)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
wg.Add(1)
|
wg.Go(func() {
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
for i := 0; i < len(test.input); i += 2 {
|
for i := 0; i < len(test.input); i += 2 {
|
||||||
entry := test.input[i+offset]
|
entry := test.input[i+offset]
|
||||||
if entry != nil {
|
if entry != nil {
|
||||||
@@ -520,7 +518,7 @@ func TestMatchListings(t *testing.T) {
|
|||||||
require.NoError(t, ls.Send())
|
require.NoError(t, ls.Send())
|
||||||
ls.CleanUp()
|
ls.CleanUp()
|
||||||
close(out)
|
close(out)
|
||||||
}()
|
})
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -570,13 +570,11 @@ func (s *syncCopyMove) startTrackRenames() {
|
|||||||
if !s.trackRenames {
|
if !s.trackRenames {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.trackRenamesWg.Add(1)
|
s.trackRenamesWg.Go(func() {
|
||||||
go func() {
|
|
||||||
defer s.trackRenamesWg.Done()
|
|
||||||
for o := range s.trackRenamesCh {
|
for o := range s.trackRenamesCh {
|
||||||
s.renameCheck = append(s.renameCheck, o)
|
s.renameCheck = append(s.renameCheck, o)
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// This stops the background rename collection
|
// This stops the background rename collection
|
||||||
@@ -593,12 +591,10 @@ func (s *syncCopyMove) startDeleters() {
|
|||||||
if s.deleteMode != fs.DeleteModeDuring && s.deleteMode != fs.DeleteModeOnly {
|
if s.deleteMode != fs.DeleteModeDuring && s.deleteMode != fs.DeleteModeOnly {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.deletersWg.Add(1)
|
s.deletersWg.Go(func() {
|
||||||
go func() {
|
|
||||||
defer s.deletersWg.Done()
|
|
||||||
err := operations.DeleteFilesWithBackupDir(s.ctx, s.deleteFilesCh, s.backupDir)
|
err := operations.DeleteFilesWithBackupDir(s.ctx, s.deleteFilesCh, s.backupDir)
|
||||||
s.processError(err)
|
s.processError(err)
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// This stops the background deleters
|
// This stops the background deleters
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//go:build !go1.24
|
//go:build !go1.25
|
||||||
|
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
// Upgrade to Go version 1.24 to compile rclone - latest stable go
|
// Upgrade to Go version 1.25 to compile rclone - latest stable go
|
||||||
// compiler recommended.
|
// compiler recommended.
|
||||||
func init() { Go_version_1_24_required_for_compilation() }
|
func init() { Go_version_1_25_required_for_compilation() }
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package fs
|
package fs
|
||||||
|
|
||||||
// VersionTag of rclone
|
// VersionTag of rclone
|
||||||
var VersionTag = "v1.73.0"
|
var VersionTag = "v1.74.0"
|
||||||
|
|||||||
@@ -391,9 +391,7 @@ func walk(ctx context.Context, f fs.Fs, path string, includeAll bool, maxLevel i
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
for range ci.Checkers {
|
for range ci.Checkers {
|
||||||
wg.Add(1)
|
wg.Go(func() {
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case job, ok := <-in:
|
case job, ok := <-in:
|
||||||
@@ -442,7 +440,7 @@ func walk(ctx context.Context, f fs.Fs, path string, includeAll bool, maxLevel i
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
// Start the process
|
// Start the process
|
||||||
traversing.Add(1)
|
traversing.Add(1)
|
||||||
|
|||||||
@@ -678,6 +678,7 @@ backends:
|
|||||||
- backend: "internxt"
|
- backend: "internxt"
|
||||||
remote: "TestInternxt:"
|
remote: "TestInternxt:"
|
||||||
fastlist: false
|
fastlist: false
|
||||||
|
listretries: 5
|
||||||
ignore:
|
ignore:
|
||||||
- TestRWFileHandleWriteNoWrite
|
- TestRWFileHandleWriteNoWrite
|
||||||
- backend: "drime"
|
- backend: "drime"
|
||||||
|
|||||||
179
go.mod
179
go.mod
@@ -1,35 +1,35 @@
|
|||||||
module github.com/rclone/rclone
|
module github.com/rclone/rclone
|
||||||
|
|
||||||
go 1.24.4
|
go 1.25.0
|
||||||
|
|
||||||
godebug x509negativeserial=1
|
godebug x509negativeserial=1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
bazil.org/fuse v0.0.0-20230120002735-62a210ff1fd5
|
bazil.org/fuse v0.0.0-20230120002735-62a210ff1fd5
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.0
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.3
|
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azfile v1.5.3
|
github.com/Azure/azure-sdk-for-go/sdk/storage/azfile v1.5.4
|
||||||
github.com/Azure/go-ntlmssp v0.0.2-0.20251110135918-10b7b7e7cd26
|
github.com/Azure/go-ntlmssp v0.1.0
|
||||||
github.com/FilenCloudDienste/filen-sdk-go v0.0.35
|
github.com/FilenCloudDienste/filen-sdk-go v0.0.37
|
||||||
github.com/Files-com/files-sdk-go/v3 v3.2.264
|
github.com/Files-com/files-sdk-go/v3 v3.3.40
|
||||||
github.com/Max-Sum/base32768 v0.0.0-20230304063302-18e6ce5945fd
|
github.com/Max-Sum/base32768 v0.0.0-20230304063302-18e6ce5945fd
|
||||||
github.com/a1ex3/zstd-seekable-format-go/pkg v0.10.0
|
github.com/a1ex3/zstd-seekable-format-go/pkg v0.10.0
|
||||||
github.com/a8m/tree v0.0.0-20240104212747-2c8764a5f17e
|
github.com/a8m/tree v0.0.0-20240104212747-2c8764a5f17e
|
||||||
github.com/aalpar/deheap v0.0.0-20210914013432-0cc84d79dec3
|
github.com/aalpar/deheap v1.0.0
|
||||||
github.com/abbot/go-http-auth v0.4.0
|
github.com/abbot/go-http-auth v0.4.0
|
||||||
github.com/anacrolix/dms v1.7.2
|
github.com/anacrolix/dms v1.7.2
|
||||||
github.com/anacrolix/log v0.17.0
|
github.com/anacrolix/log v0.17.0
|
||||||
github.com/atotto/clipboard v0.1.4
|
github.com/atotto/clipboard v0.1.4
|
||||||
github.com/aws/aws-sdk-go-v2 v1.39.6
|
github.com/aws/aws-sdk-go-v2 v1.41.1
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.31.17
|
github.com/aws/aws-sdk-go-v2/config v1.32.8
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.21
|
github.com/aws/aws-sdk-go-v2/credentials v1.19.8
|
||||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.20.4
|
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.22.1
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.90.0
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.0
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.39.1
|
github.com/aws/aws-sdk-go-v2/service/sts v1.41.6
|
||||||
github.com/aws/smithy-go v1.23.2
|
github.com/aws/smithy-go v1.24.0
|
||||||
github.com/buengese/sgzip v0.1.1
|
github.com/buengese/sgzip v0.1.1
|
||||||
github.com/cloudinary/cloudinary-go/v2 v2.13.0
|
github.com/cloudinary/cloudinary-go/v2 v2.14.1
|
||||||
github.com/cloudsoda/go-smb2 v0.0.0-20250228001242-d4c70e6251cc
|
github.com/cloudsoda/go-smb2 v0.0.0-20250228001242-d4c70e6251cc
|
||||||
github.com/colinmarc/hdfs/v2 v2.4.0
|
github.com/colinmarc/hdfs/v2 v2.4.0
|
||||||
github.com/coreos/go-semver v0.3.1
|
github.com/coreos/go-semver v0.3.1
|
||||||
@@ -37,31 +37,31 @@ require (
|
|||||||
github.com/diskfs/go-diskfs v1.7.0
|
github.com/diskfs/go-diskfs v1.7.0
|
||||||
github.com/dop251/scsu v0.0.0-20220106150536-84ac88021d00
|
github.com/dop251/scsu v0.0.0-20220106150536-84ac88021d00
|
||||||
github.com/dropbox/dropbox-sdk-go-unofficial/v6 v6.0.5
|
github.com/dropbox/dropbox-sdk-go-unofficial/v6 v6.0.5
|
||||||
github.com/gabriel-vasile/mimetype v1.4.11
|
github.com/gabriel-vasile/mimetype v1.4.13
|
||||||
github.com/gdamore/tcell/v2 v2.9.0
|
github.com/gdamore/tcell/v2 v2.13.8
|
||||||
github.com/go-chi/chi/v5 v5.2.3
|
github.com/go-chi/chi/v5 v5.2.5
|
||||||
github.com/go-darwin/apfs v0.0.0-20211011131704-f84b94dbf348
|
github.com/go-darwin/apfs v0.0.0-20211011131704-f84b94dbf348
|
||||||
github.com/go-git/go-billy/v5 v5.6.2
|
github.com/go-git/go-billy/v5 v5.7.0
|
||||||
github.com/golang-jwt/jwt/v5 v5.3.0
|
github.com/golang-jwt/jwt/v5 v5.3.1
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/hanwen/go-fuse/v2 v2.9.0
|
github.com/hanwen/go-fuse/v2 v2.9.0
|
||||||
github.com/internxt/rclone-adapter v0.0.0-20260130171252-c3c6ebb49276
|
github.com/internxt/rclone-adapter v0.0.0-20260213125353-6f59c89fcb7c
|
||||||
github.com/jcmturner/gokrb5/v8 v8.4.4
|
github.com/jcmturner/gokrb5/v8 v8.4.4
|
||||||
github.com/jlaffaye/ftp v0.2.1-0.20240918233326-1b970516f5d3
|
github.com/jlaffaye/ftp v0.2.1-0.20240918233326-1b970516f5d3
|
||||||
github.com/josephspurrier/goversioninfo v1.5.0
|
github.com/josephspurrier/goversioninfo v1.5.0
|
||||||
github.com/jzelinskie/whirlpool v0.0.0-20201016144138-0675e54bb004
|
github.com/jzelinskie/whirlpool v0.0.0-20201016144138-0675e54bb004
|
||||||
github.com/klauspost/compress v1.18.1
|
github.com/klauspost/compress v1.18.4
|
||||||
github.com/koofr/go-httpclient v0.0.0-20240520111329-e20f8f203988
|
github.com/koofr/go-httpclient v0.0.0-20240520111329-e20f8f203988
|
||||||
github.com/koofr/go-koofrclient v0.0.0-20221207135200-cbd7fc9ad6a6
|
github.com/koofr/go-koofrclient v0.0.0-20221207135200-cbd7fc9ad6a6
|
||||||
github.com/lanrat/extsort v1.4.2
|
github.com/lanrat/extsort v1.4.2
|
||||||
github.com/mattn/go-colorable v0.1.14
|
github.com/mattn/go-colorable v0.1.14
|
||||||
github.com/mattn/go-runewidth v0.0.19
|
github.com/mattn/go-runewidth v0.0.20
|
||||||
github.com/mholt/archives v0.1.5
|
github.com/mholt/archives v0.1.5
|
||||||
github.com/minio/minio-go/v7 v7.0.97
|
github.com/minio/minio-go/v7 v7.0.98
|
||||||
github.com/mitchellh/go-homedir v1.1.0
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
github.com/moby/sys/mountinfo v0.7.2
|
github.com/moby/sys/mountinfo v0.7.2
|
||||||
github.com/ncw/swift/v2 v2.0.5
|
github.com/ncw/swift/v2 v2.0.5
|
||||||
github.com/oracle/oci-go-sdk/v65 v65.104.0
|
github.com/oracle/oci-go-sdk/v65 v65.108.2
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/peterh/liner v1.2.2
|
github.com/peterh/liner v1.2.2
|
||||||
github.com/pkg/sftp v1.13.10
|
github.com/pkg/sftp v1.13.10
|
||||||
@@ -72,15 +72,15 @@ require (
|
|||||||
github.com/rclone/Proton-API-Bridge v1.0.1-0.20260127174007-77f974840d11
|
github.com/rclone/Proton-API-Bridge v1.0.1-0.20260127174007-77f974840d11
|
||||||
github.com/rclone/go-proton-api v1.0.1-0.20260127173028-eb465cac3b18
|
github.com/rclone/go-proton-api v1.0.1-0.20260127173028-eb465cac3b18
|
||||||
github.com/rclone/gofakes3 v0.0.4
|
github.com/rclone/gofakes3 v0.0.4
|
||||||
github.com/rfjakob/eme v1.1.2
|
github.com/rfjakob/eme v1.2.0
|
||||||
github.com/rivo/uniseg v0.4.7
|
github.com/rivo/uniseg v0.4.7
|
||||||
github.com/rogpeppe/go-internal v1.14.1
|
github.com/rogpeppe/go-internal v1.14.1
|
||||||
github.com/shirou/gopsutil/v4 v4.25.10
|
github.com/shirou/gopsutil/v4 v4.26.1
|
||||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
|
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
|
||||||
github.com/spf13/cobra v1.10.1
|
github.com/spf13/cobra v1.10.2
|
||||||
github.com/spf13/pflag v1.0.10
|
github.com/spf13/pflag v1.0.10
|
||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.11.1
|
||||||
github.com/t3rm1n4l/go-mega v0.0.0-20251031123324-a804aaa87491
|
github.com/t3rm1n4l/go-mega v0.0.0-20251120131202-6845944c051c
|
||||||
github.com/unknwon/goconfig v1.0.0
|
github.com/unknwon/goconfig v1.0.0
|
||||||
github.com/willscott/go-nfs v0.0.3
|
github.com/willscott/go-nfs v0.0.3
|
||||||
github.com/winfsp/cgofuse v1.6.1-0.20260126094232-f2c4fccdb286
|
github.com/winfsp/cgofuse v1.6.1-0.20260126094232-f2c4fccdb286
|
||||||
@@ -89,17 +89,17 @@ require (
|
|||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78
|
||||||
github.com/yunify/qingstor-sdk-go/v3 v3.2.0
|
github.com/yunify/qingstor-sdk-go/v3 v3.2.0
|
||||||
github.com/zeebo/blake3 v0.2.4
|
github.com/zeebo/blake3 v0.2.4
|
||||||
github.com/zeebo/xxh3 v1.0.2
|
github.com/zeebo/xxh3 v1.1.0
|
||||||
go.etcd.io/bbolt v1.4.3
|
go.etcd.io/bbolt v1.4.3
|
||||||
goftp.io/server/v2 v2.0.2
|
goftp.io/server/v2 v2.0.2
|
||||||
golang.org/x/crypto v0.45.0
|
golang.org/x/crypto v0.48.0
|
||||||
golang.org/x/net v0.47.0
|
golang.org/x/net v0.50.0
|
||||||
golang.org/x/oauth2 v0.33.0
|
golang.org/x/oauth2 v0.35.0
|
||||||
golang.org/x/sync v0.18.0
|
golang.org/x/sync v0.19.0
|
||||||
golang.org/x/sys v0.38.0
|
golang.org/x/sys v0.41.0
|
||||||
golang.org/x/text v0.31.0
|
golang.org/x/text v0.34.0
|
||||||
golang.org/x/time v0.14.0
|
golang.org/x/time v0.14.0
|
||||||
google.golang.org/api v0.255.0
|
google.golang.org/api v0.267.0
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||||
gopkg.in/validator.v2 v2.0.1
|
gopkg.in/validator.v2 v2.0.1
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
@@ -107,7 +107,7 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go/auth v0.17.0 // indirect
|
cloud.google.com/go/auth v0.18.2 // indirect
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||||
cloud.google.com/go/compute/metadata v0.9.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/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
|
||||||
@@ -117,26 +117,27 @@ require (
|
|||||||
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect
|
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect
|
||||||
github.com/ProtonMail/go-srp v0.0.7 // indirect
|
github.com/ProtonMail/go-srp v0.0.7 // indirect
|
||||||
github.com/ProtonMail/gopenpgp/v2 v2.9.0 // indirect
|
github.com/ProtonMail/gopenpgp/v2 v2.9.0 // indirect
|
||||||
github.com/PuerkitoBio/goquery v1.10.3 // indirect
|
github.com/PuerkitoBio/goquery v1.11.0 // indirect
|
||||||
github.com/STARRY-S/zip v0.2.3 // indirect
|
github.com/STARRY-S/zip v0.2.3 // indirect
|
||||||
github.com/akavel/rsrc v0.10.2 // indirect
|
github.com/akavel/rsrc v0.10.2 // indirect
|
||||||
github.com/anacrolix/generics v0.1.0 // indirect
|
github.com/anacrolix/generics v0.2.0 // indirect
|
||||||
github.com/anchore/go-lzo v0.1.0 // indirect
|
github.com/anchore/go-lzo v0.1.0 // indirect
|
||||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||||
github.com/appscode/go-querystring v0.0.0-20170504095604-0126cfb3f1dc // indirect
|
github.com/appscode/go-querystring v0.0.0-20170504095604-0126cfb3f1dc // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3 // indirect
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 // indirect
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.13 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.17 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.8 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.17 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.1 // indirect
|
github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sso v1.30.9 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.14 // indirect
|
||||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bodgit/plumbing v1.3.0 // indirect
|
github.com/bodgit/plumbing v1.3.0 // indirect
|
||||||
@@ -149,9 +150,8 @@ require (
|
|||||||
github.com/calebcase/tmpfile v1.0.3 // indirect
|
github.com/calebcase/tmpfile v1.0.3 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/chilts/sid v0.0.0-20190607042430-660e94789ec9 // indirect
|
github.com/chilts/sid v0.0.0-20190607042430-660e94789ec9 // indirect
|
||||||
github.com/clipperhouse/stringish v0.1.1 // indirect
|
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
|
||||||
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
|
github.com/cloudflare/circl v1.6.3 // indirect
|
||||||
github.com/cloudflare/circl v1.6.1 // indirect
|
|
||||||
github.com/cloudsoda/sddl v0.0.0-20250224235906-926454e91efc // indirect
|
github.com/cloudsoda/sddl v0.0.0-20250224235906-926454e91efc // indirect
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||||
github.com/creasty/defaults v1.8.0 // indirect
|
github.com/creasty/defaults v1.8.0 // indirect
|
||||||
@@ -171,19 +171,19 @@ require (
|
|||||||
github.com/go-logr/logr v1.4.3 // indirect
|
github.com/go-logr/logr v1.4.3 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||||
github.com/go-openapi/errors v0.22.4 // indirect
|
github.com/go-openapi/errors v0.22.6 // indirect
|
||||||
github.com/go-openapi/strfmt v0.25.0 // indirect
|
github.com/go-openapi/strfmt v0.25.0 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.28.0 // indirect
|
github.com/go-playground/validator/v10 v10.30.1 // indirect
|
||||||
github.com/go-resty/resty/v2 v2.16.5 // indirect
|
github.com/go-resty/resty/v2 v2.17.2 // indirect
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
|
||||||
github.com/gofrs/flock v0.13.0 // indirect
|
github.com/gofrs/flock v0.13.0 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/google/btree v1.1.3 // indirect
|
github.com/google/btree v1.1.3 // indirect
|
||||||
github.com/google/s2a-go v0.1.9 // indirect
|
github.com/google/s2a-go v0.1.9 // indirect
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.7 // indirect
|
github.com/googleapis/enterprise-certificate-proxy v0.3.12 // indirect
|
||||||
github.com/googleapis/gax-go/v2 v2.15.0 // indirect
|
github.com/googleapis/gax-go/v2 v2.17.0 // indirect
|
||||||
github.com/gorilla/schema v1.4.1 // indirect
|
github.com/gorilla/schema v1.4.1 // indirect
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
@@ -206,7 +206,7 @@ require (
|
|||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/lpar/date v1.0.0 // indirect
|
github.com/lpar/date v1.0.0 // indirect
|
||||||
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
||||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect
|
github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88 // indirect
|
||||||
github.com/mailru/easyjson v0.9.1 // indirect
|
github.com/mailru/easyjson v0.9.1 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mikelolasagasti/xz v1.0.1 // indirect
|
github.com/mikelolasagasti/xz v1.0.1 // indirect
|
||||||
@@ -215,17 +215,17 @@ require (
|
|||||||
github.com/minio/minlz v1.0.1 // indirect
|
github.com/minio/minlz v1.0.1 // indirect
|
||||||
github.com/minio/xxml v0.0.3 // indirect
|
github.com/minio/xxml v0.0.3 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/nwaples/rardecode/v2 v2.2.1 // indirect
|
github.com/nwaples/rardecode/v2 v2.2.2 // indirect
|
||||||
github.com/oklog/ulid v1.3.1 // indirect
|
github.com/oklog/ulid v1.3.1 // indirect
|
||||||
github.com/panjf2000/ants/v2 v2.11.3 // indirect
|
github.com/panjf2000/ants/v2 v2.11.5 // indirect
|
||||||
github.com/pengsrc/go-shared v0.2.1-0.20190131101655-1999055a4a14 // indirect
|
github.com/pengsrc/go-shared v0.2.1-0.20190131101655-1999055a4a14 // indirect
|
||||||
github.com/philhofer/fwd v1.2.0 // indirect
|
github.com/philhofer/fwd v1.2.0 // indirect
|
||||||
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
github.com/pierrec/lz4/v4 v4.1.25 // indirect
|
||||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.67.2 // indirect
|
github.com/prometheus/common v0.67.5 // indirect
|
||||||
github.com/prometheus/procfs v0.19.2 // indirect
|
github.com/prometheus/procfs v0.19.2 // indirect
|
||||||
github.com/rasky/go-xdr v0.0.0-20170124162913-1a41d1a06c93 // indirect
|
github.com/rasky/go-xdr v0.0.0-20170124162913-1a41d1a06c93 // indirect
|
||||||
github.com/relvacode/iso8601 v1.7.0 // indirect
|
github.com/relvacode/iso8601 v1.7.0 // indirect
|
||||||
@@ -235,38 +235,39 @@ require (
|
|||||||
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 // indirect
|
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 // indirect
|
||||||
github.com/samber/lo v1.52.0 // indirect
|
github.com/samber/lo v1.52.0 // indirect
|
||||||
github.com/shabbyrobe/gocovmerge v0.0.0-20230507112040-c3350d9342df // indirect
|
github.com/shabbyrobe/gocovmerge v0.0.0-20230507112040-c3350d9342df // indirect
|
||||||
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af // indirect
|
github.com/sirupsen/logrus v1.9.4 // indirect
|
||||||
github.com/smartystreets/goconvey v1.8.1 // indirect
|
github.com/smartystreets/goconvey v1.8.1 // indirect
|
||||||
github.com/sony/gobreaker v1.0.0 // indirect
|
github.com/sony/gobreaker v1.0.0 // indirect
|
||||||
github.com/sorairolake/lzip-go v0.3.8 // indirect
|
github.com/sorairolake/lzip-go v0.3.8 // indirect
|
||||||
github.com/spacemonkeygo/monkit/v3 v3.0.25-0.20251022131615-eb24eb109368 // indirect
|
github.com/spacemonkeygo/monkit/v3 v3.0.25-0.20251022131615-eb24eb109368 // indirect
|
||||||
github.com/spf13/afero v1.15.0 // indirect
|
github.com/spf13/afero v1.15.0 // indirect
|
||||||
github.com/tinylib/msgp v1.5.0 // indirect
|
github.com/tinylib/msgp v1.6.3 // indirect
|
||||||
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
||||||
github.com/tklauser/numcpus v0.10.0 // indirect
|
github.com/tklauser/numcpus v0.11.0 // indirect
|
||||||
github.com/tyler-smith/go-bip39 v1.1.0 // indirect
|
github.com/tyler-smith/go-bip39 v1.1.0 // indirect
|
||||||
github.com/ulikunitz/xz v0.5.15 // indirect
|
github.com/ulikunitz/xz v0.5.15 // indirect
|
||||||
github.com/willscott/go-nfs-client v0.0.0-20251022144359-801f10d98886 // indirect
|
github.com/willscott/go-nfs-client v0.0.0-20251022144359-801f10d98886 // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||||
github.com/zeebo/errs v1.4.0 // indirect
|
github.com/zeebo/errs v1.4.0 // indirect
|
||||||
go.mongodb.org/mongo-driver v1.17.6 // indirect
|
go.mongodb.org/mongo-driver v1.17.9 // indirect
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1 // 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/contrib/instrumentation/net/http/otelhttp v0.65.0 // indirect
|
||||||
go.opentelemetry.io/otel v1.38.0 // indirect
|
go.opentelemetry.io/otel v1.40.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
go.opentelemetry.io/otel/metric v1.40.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
go.opentelemetry.io/otel/trace v1.40.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||||
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
|
go4.org v0.0.0-20260112195520-a5071408f32f // indirect
|
||||||
golang.org/x/image v0.32.0 // indirect
|
golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a // indirect
|
||||||
golang.org/x/mod v0.29.0 // indirect
|
golang.org/x/image v0.36.0 // indirect
|
||||||
golang.org/x/tools v0.38.0 // indirect
|
golang.org/x/tools v0.42.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d // indirect
|
||||||
google.golang.org/grpc v1.76.0 // indirect
|
google.golang.org/grpc v1.79.1 // indirect
|
||||||
google.golang.org/protobuf v1.36.10 // indirect
|
google.golang.org/protobuf v1.36.11 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
moul.io/http2curl/v2 v2.3.0 // indirect
|
moul.io/http2curl/v2 v2.3.0 // indirect
|
||||||
storj.io/common v0.0.0-20251107171817-6221ae45072c // indirect
|
sigs.k8s.io/yaml v1.6.0 // indirect
|
||||||
|
storj.io/common v0.0.0-20260212175235-9580cc9c5777 // indirect
|
||||||
storj.io/drpc v0.0.35-0.20250513201419-f7819ea69b55 // indirect
|
storj.io/drpc v0.0.35-0.20250513201419-f7819ea69b55 // indirect
|
||||||
storj.io/eventkit v0.0.0-20250410172343-61f26d3de156 // indirect
|
storj.io/eventkit v0.0.0-20250410172343-61f26d3de156 // indirect
|
||||||
storj.io/infectious v0.0.2 // indirect
|
storj.io/infectious v0.0.2 // indirect
|
||||||
@@ -274,12 +275,12 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/IBM/go-sdk-core/v5 v5.18.5
|
github.com/IBM/go-sdk-core/v5 v5.21.2
|
||||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||||
github.com/ProtonMail/go-crypto v1.3.0
|
github.com/ProtonMail/go-crypto v1.3.0
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.2
|
github.com/golang-jwt/jwt/v4 v4.5.2
|
||||||
github.com/pkg/xattr v0.4.12
|
github.com/pkg/xattr v0.4.12
|
||||||
github.com/pquerna/otp v1.5.0
|
github.com/pquerna/otp v1.5.0
|
||||||
golang.org/x/mobile v0.0.0-20251021151156-188f512ec823
|
golang.org/x/mobile v0.0.0-20260217195705-b56b3793a9c4
|
||||||
golang.org/x/term v0.37.0
|
golang.org/x/term v0.40.0
|
||||||
)
|
)
|
||||||
|
|||||||
377
go.sum
377
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.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.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||||
cloud.google.com/go/auth v0.17.0 h1:74yCm7hCj2rUyyAocqnFzsAYXgJhrG26XCFimrc/Kz4=
|
cloud.google.com/go/auth v0.18.2 h1:+Nbt5Ev0xEqxlNjd6c+yYUeosQ5TtEUaNcN/3FozlaM=
|
||||||
cloud.google.com/go/auth v0.17.0/go.mod h1:6wv/t5/6rOPAX4fJiRjKkJCvswLwdet7G8+UGXt7nCQ=
|
cloud.google.com/go/auth v0.18.2/go.mod h1:xD+oY7gcahcu7G2SG2DsBerfFxgPAJz17zz2joOFF3M=
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
|
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/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=
|
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||||
@@ -39,41 +39,41 @@ 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.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
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=
|
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.20.0 h1:JXg2dwJUmPB9JmtVmdEB16APJ7jurfbY5jnfXpJoRMc=
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 h1:fou+2+WFTib47nS+nz/ozhEBnvU96bKHy6LjRsY4E28=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw=
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0/go.mod h1:t76Ruy8AHvUAC8GfMWJMa0ElSbuIcO03NLpynfbgsPA=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.0 h1:KpMC6LFL7mqpExyMC9jVOYRiVhLmamjeZfRsUpB7l4s=
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.0/go.mod h1:J7MUC/wtRpfGVbQ5sIItY5/FuVWmvzlY21WAOfQnq/I=
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0=
|
||||||
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 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/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=
|
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI=
|
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1 h1:/Zt+cDPnpC3OVDm/JKLOs7M2DKmLRIIp3XIx9pHHiig=
|
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1 h1:/Zt+cDPnpC3OVDm/JKLOs7M2DKmLRIIp3XIx9pHHiig=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1/go.mod h1:Ng3urmn6dYe8gnbCMoHHVl5APYz2txho3koEkV2o2HA=
|
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1/go.mod h1:Ng3urmn6dYe8gnbCMoHHVl5APYz2txho3koEkV2o2HA=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.3 h1:ZJJNFaQ86GVKQ9ehwqyAFE6pIfyicpuJ8IkVaPBc6/4=
|
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4 h1:jWQK1GI+LeGGUKBADtcH2rRqPxYB1Ljwms5gFA2LqrM=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.3/go.mod h1:URuDvhmATVKqHBH9/0nOiNKk0+YcwfQ3WkK5PqHKxc8=
|
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4/go.mod h1:8mwH4klAm9DUgR2EEHyEEAQlRDvLPyg5fQry3y+cDew=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azfile v1.5.3 h1:sxgSqOB9CDToiaVFpxuvb5wGgGqWa3lCShcm5o0n3bE=
|
github.com/Azure/azure-sdk-for-go/sdk/storage/azfile v1.5.4 h1:tZh20RjgfMxKBxJiIS75iTVAKIUxrST5X2dVHMTptL4=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azfile v1.5.3/go.mod h1:XdED8i399lEVblYHTZM8eXaP07gv4Z58IL6ueMlVlrg=
|
github.com/Azure/azure-sdk-for-go/sdk/storage/azfile v1.5.4/go.mod h1:vGYAk36rhMVCfTP7v+RVruCR0zmPe6S+36KRpDCLySw=
|
||||||
github.com/Azure/go-ntlmssp v0.0.2-0.20251110135918-10b7b7e7cd26 h1:gy/jrlpp8EfSyA73a51fofoSfhp5rPNQAUvDr4Dm91c=
|
github.com/Azure/go-ntlmssp v0.1.0 h1:DjFo6YtWzNqNvQdrwEyr/e4nhU3vRiwenz5QX7sFz+A=
|
||||||
github.com/Azure/go-ntlmssp v0.0.2-0.20251110135918-10b7b7e7cd26/go.mod h1:NYqdhxd/8aAct/s4qSYZEerdPuH1liG2/X9DiVTbhpk=
|
github.com/Azure/go-ntlmssp v0.1.0/go.mod h1:NYqdhxd/8aAct/s4qSYZEerdPuH1liG2/X9DiVTbhpk=
|
||||||
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 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
|
||||||
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
|
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.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs=
|
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs=
|
||||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk=
|
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
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/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
github.com/FilenCloudDienste/filen-sdk-go v0.0.35 h1:geuYpD/1ZXSp1H3kdW7si+KRUIrHHqM1kk8lqoA8Y9M=
|
github.com/FilenCloudDienste/filen-sdk-go v0.0.37 h1:W8S9TrAyZ4//3PXsU6+Bi+fe/6uIL986GyS7PVzIDL4=
|
||||||
github.com/FilenCloudDienste/filen-sdk-go v0.0.35/go.mod h1:0cBhKXQg49XbKZZfk5TCDa3sVLP+xMxZTWL+7KY0XR0=
|
github.com/FilenCloudDienste/filen-sdk-go v0.0.37/go.mod h1:0cBhKXQg49XbKZZfk5TCDa3sVLP+xMxZTWL+7KY0XR0=
|
||||||
github.com/Files-com/files-sdk-go/v3 v3.2.264 h1:lMHTplAYI9FtmCo/QOcpRxmPA5REVAct1r2riQmDQKw=
|
github.com/Files-com/files-sdk-go/v3 v3.3.40 h1:N4zEhSZWrqUNW4m1ta9FFjvmpNjSwyuUnX6HZAzjHFw=
|
||||||
github.com/Files-com/files-sdk-go/v3 v3.2.264/go.mod h1:wGqkOzRu/ClJibvDgcfuJNAqI2nLhe8g91tPlDKRCdE=
|
github.com/Files-com/files-sdk-go/v3 v3.3.40/go.mod h1:IPk80dOmc7VFC0DJ85xMTPmre+8xoXX6kGHAkf5jRRw=
|
||||||
github.com/IBM/go-sdk-core/v5 v5.18.5 h1:g0JRl3sYXJczB/yuDlrN6x22LJ6jIxhp0Sa4ARNW60c=
|
github.com/IBM/go-sdk-core/v5 v5.21.2 h1:mJ5QbLPOm4g5qhZiVB6wbSllfpeUExftGoyPek2hk4M=
|
||||||
github.com/IBM/go-sdk-core/v5 v5.18.5/go.mod h1:KonTFRR+8ZSgw5cxBSYo6E4WZoY1+7n1kfHM82VcjFU=
|
github.com/IBM/go-sdk-core/v5 v5.21.2/go.mod h1:ngpMgwkjur1VNUjqn11LPk3o5eCyOCRbcfg/0YAY7Hc=
|
||||||
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
|
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
|
||||||
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||||
github.com/Max-Sum/base32768 v0.0.0-20230304063302-18e6ce5945fd h1:nzE1YQBdx1bq9IlZinHa+HVffy+NmVRoKr+wHN8fpLE=
|
github.com/Max-Sum/base32768 v0.0.0-20230304063302-18e6ce5945fd h1:nzE1YQBdx1bq9IlZinHa+HVffy+NmVRoKr+wHN8fpLE=
|
||||||
github.com/Max-Sum/base32768 v0.0.0-20230304063302-18e6ce5945fd/go.mod h1:C8yoIfvESpM3GD07OCHU7fqI7lhwyZ2Td1rbNbTAhnc=
|
github.com/Max-Sum/base32768 v0.0.0-20230304063302-18e6ce5945fd/go.mod h1:C8yoIfvESpM3GD07OCHU7fqI7lhwyZ2Td1rbNbTAhnc=
|
||||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||||
github.com/ProtonMail/bcrypt v0.0.0-20210511135022-227b4adcab57/go.mod h1:HecWFHognK8GfRDGnFQbW/LiV7A3MX3gZVs45vk5h8I=
|
github.com/ProtonMail/bcrypt v0.0.0-20210511135022-227b4adcab57/go.mod h1:HecWFHognK8GfRDGnFQbW/LiV7A3MX3gZVs45vk5h8I=
|
||||||
github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf h1:yc9daCCYUefEs69zUkSzubzjBbL+cmOXgnmt9Fyd9ug=
|
github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf h1:yc9daCCYUefEs69zUkSzubzjBbL+cmOXgnmt9Fyd9ug=
|
||||||
github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf/go.mod h1:o0ESU9p83twszAU8LBeJKFAAMX14tISa0yk4Oo5TOqo=
|
github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf/go.mod h1:o0ESU9p83twszAU8LBeJKFAAMX14tISa0yk4Oo5TOqo=
|
||||||
@@ -88,24 +88,24 @@ github.com/ProtonMail/go-srp v0.0.7 h1:Sos3Qk+th4tQR64vsxGIxYpN3rdnG9Wf9K4ZloC1J
|
|||||||
github.com/ProtonMail/go-srp v0.0.7/go.mod h1:giCp+7qRnMIcCvI6V6U3S1lDDXDQYx2ewJ6F/9wdlJk=
|
github.com/ProtonMail/go-srp v0.0.7/go.mod h1:giCp+7qRnMIcCvI6V6U3S1lDDXDQYx2ewJ6F/9wdlJk=
|
||||||
github.com/ProtonMail/gopenpgp/v2 v2.9.0 h1:ruLzBmwe4dR1hdnrsEJ/S7psSBmV15gFttFUPP/+/kE=
|
github.com/ProtonMail/gopenpgp/v2 v2.9.0 h1:ruLzBmwe4dR1hdnrsEJ/S7psSBmV15gFttFUPP/+/kE=
|
||||||
github.com/ProtonMail/gopenpgp/v2 v2.9.0/go.mod h1:IldDyh9Hv1ZCCYatTuuEt1XZJ0OPjxLpTarDfglih7s=
|
github.com/ProtonMail/gopenpgp/v2 v2.9.0/go.mod h1:IldDyh9Hv1ZCCYatTuuEt1XZJ0OPjxLpTarDfglih7s=
|
||||||
github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo=
|
github.com/PuerkitoBio/goquery v1.11.0 h1:jZ7pwMQXIITcUXNH83LLk+txlaEy6NVOfTuP43xxfqw=
|
||||||
github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y=
|
github.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ=
|
||||||
github.com/STARRY-S/zip v0.2.3 h1:luE4dMvRPDOWQdeDdUxUoZkzUIpTccdKdhHHsQJ1fm4=
|
github.com/STARRY-S/zip v0.2.3 h1:luE4dMvRPDOWQdeDdUxUoZkzUIpTccdKdhHHsQJ1fm4=
|
||||||
github.com/STARRY-S/zip v0.2.3/go.mod h1:lqJ9JdeRipyOQJrYSOtpNAiaesFO6zVDsE8GIGFaoSk=
|
github.com/STARRY-S/zip v0.2.3/go.mod h1:lqJ9JdeRipyOQJrYSOtpNAiaesFO6zVDsE8GIGFaoSk=
|
||||||
github.com/a1ex3/zstd-seekable-format-go/pkg v0.10.0 h1:iLDOF0rdGTrol/q8OfPIIs5kLD8XvA2q75o6Uq/tgak=
|
github.com/a1ex3/zstd-seekable-format-go/pkg v0.10.0 h1:iLDOF0rdGTrol/q8OfPIIs5kLD8XvA2q75o6Uq/tgak=
|
||||||
github.com/a1ex3/zstd-seekable-format-go/pkg v0.10.0/go.mod h1:DrEWcQJjz7t5iF2duaiyhg4jyoF0kxOD6LtECNGkZ/Q=
|
github.com/a1ex3/zstd-seekable-format-go/pkg v0.10.0/go.mod h1:DrEWcQJjz7t5iF2duaiyhg4jyoF0kxOD6LtECNGkZ/Q=
|
||||||
github.com/a8m/tree v0.0.0-20240104212747-2c8764a5f17e h1:KMVieI1/Ub++GYfnhyFPoGE3g5TUiG4srE3TMGr5nM4=
|
github.com/a8m/tree v0.0.0-20240104212747-2c8764a5f17e h1:KMVieI1/Ub++GYfnhyFPoGE3g5TUiG4srE3TMGr5nM4=
|
||||||
github.com/a8m/tree v0.0.0-20240104212747-2c8764a5f17e/go.mod h1:j5astEcUkZQX8lK+KKlQ3NRQ50f4EE8ZjyZpCz3mrH4=
|
github.com/a8m/tree v0.0.0-20240104212747-2c8764a5f17e/go.mod h1:j5astEcUkZQX8lK+KKlQ3NRQ50f4EE8ZjyZpCz3mrH4=
|
||||||
github.com/aalpar/deheap v0.0.0-20210914013432-0cc84d79dec3 h1:hhdWprfSpFbN7lz3W1gM40vOgvSh1WCSMxYD6gGB4Hs=
|
github.com/aalpar/deheap v1.0.0 h1:vVdXUiQ16b858LraxHgCg1UmRmoObS53gFRi3fFY9x0=
|
||||||
github.com/aalpar/deheap v0.0.0-20210914013432-0cc84d79dec3/go.mod h1:XaUnRxSCYgL3kkgX0QHIV0D+znljPIDImxlv2kbGv0Y=
|
github.com/aalpar/deheap v1.0.0/go.mod h1:A+nfkD4JbS05sewV0he/MYgR/90vfqyMoNNROgs+rmA=
|
||||||
github.com/abbot/go-http-auth v0.4.0 h1:QjmvZ5gSC7jm3Zg54DqWE/T5m1t2AfDu6QlXJT0EVT0=
|
github.com/abbot/go-http-auth v0.4.0 h1:QjmvZ5gSC7jm3Zg54DqWE/T5m1t2AfDu6QlXJT0EVT0=
|
||||||
github.com/abbot/go-http-auth v0.4.0/go.mod h1:Cz6ARTIzApMJDzh5bRMSUou6UMSp0IEXg9km/ci7TJM=
|
github.com/abbot/go-http-auth v0.4.0/go.mod h1:Cz6ARTIzApMJDzh5bRMSUou6UMSp0IEXg9km/ci7TJM=
|
||||||
github.com/akavel/rsrc v0.10.2 h1:Zxm8V5eI1hW4gGaYsJQUhxpjkENuG91ki8B4zCrvEsw=
|
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/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 h1:JAAJJIlXp+jT2yEah1EbR1AFpGALHL238uSKFXec2qw=
|
||||||
github.com/anacrolix/dms v1.7.2/go.mod h1:excFJW5MKBhn5yt5ZMyeE9iFVqnO6tEGQl7YG/2tUoQ=
|
github.com/anacrolix/dms v1.7.2/go.mod h1:excFJW5MKBhn5yt5ZMyeE9iFVqnO6tEGQl7YG/2tUoQ=
|
||||||
github.com/anacrolix/generics v0.1.0 h1:r6OgogjCdml3K5A8ixUG0X9DM4jrQiMfIkZiBOGvIfg=
|
github.com/anacrolix/generics v0.2.0 h1:gPwGOs14irokFN9kUP1i1A0Bn0FPT7/hWWD3hHKSKNw=
|
||||||
github.com/anacrolix/generics v0.1.0/go.mod h1:MN3ve08Z3zSV/rTuX/ouI4lNdlfTxgdafQJiLzyNRB8=
|
github.com/anacrolix/generics v0.2.0/go.mod h1:NGehhfeXJPBujPx0s6cstSj8B+TERsTY32Xckfx5ftc=
|
||||||
github.com/anacrolix/log v0.17.0 h1:cZvEGRPCbIg+WK+qAxWj/ap2Gj8cx1haOCSVxNZQpK4=
|
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/anacrolix/log v0.17.0/go.mod h1:m0poRtlr41mriZlXBQ9SOVZ8yZBkLjOkDhd5Li5pITA=
|
||||||
github.com/anchore/go-lzo v0.1.0 h1:NgAacnzqPeGH49Ky19QKLBZEuFRqtTG9cdaucc3Vncs=
|
github.com/anchore/go-lzo v0.1.0 h1:NgAacnzqPeGH49Ky19QKLBZEuFRqtTG9cdaucc3Vncs=
|
||||||
@@ -118,44 +118,46 @@ github.com/appscode/go-querystring v0.0.0-20170504095604-0126cfb3f1dc h1:LoL75er
|
|||||||
github.com/appscode/go-querystring v0.0.0-20170504095604-0126cfb3f1dc/go.mod h1:w648aMHEgFYS6xb0KVMMtZ2uMeemhiKCuD2vj6gY52A=
|
github.com/appscode/go-querystring v0.0.0-20170504095604-0126cfb3f1dc/go.mod h1:w648aMHEgFYS6xb0KVMMtZ2uMeemhiKCuD2vj6gY52A=
|
||||||
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||||
github.com/aws/aws-sdk-go-v2 v1.39.6 h1:2JrPCVgWJm7bm83BDwY5z8ietmeJUbh3O2ACnn+Xsqk=
|
github.com/aws/aws-sdk-go-v2 v1.41.1 h1:ABlyEARCDLN034NhxlRUSZr4l71mh+T5KAeGh6cerhU=
|
||||||
github.com/aws/aws-sdk-go-v2 v1.39.6/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE=
|
github.com/aws/aws-sdk-go-v2 v1.41.1/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0=
|
||||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3 h1:DHctwEM8P8iTXFxC/QK0MRjwEpWQeM9yzidCRjldUz0=
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 h1:489krEF9xIGkOaaX3CE/Be2uWjiXrkCH6gUX+bZA/BU=
|
||||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3/go.mod h1:xdCzcZEtnSTKVDOmUZs4l/j3pSV6rpo1WXl5ugNsL8Y=
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4/go.mod h1:IOAPF6oT9KCsceNTvvYMNHy0+kMF8akOjeDvPENWxp4=
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.31.17 h1:QFl8lL6RgakNK86vusim14P2k8BFSxjvUkcWLDjgz9Y=
|
github.com/aws/aws-sdk-go-v2/config v1.32.8 h1:iu+64gwDKEoKnyTQskSku72dAwggKI5sV6rNvgSMpMs=
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.31.17/go.mod h1:V8P7ILjp/Uef/aX8TjGk6OHZN6IKPM5YW6S78QnRD5c=
|
github.com/aws/aws-sdk-go-v2/config v1.32.8/go.mod h1:MI2XvA+qDi3i9AJxX1E2fu730syEBzp/jnXrjxuHwgI=
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.21 h1:56HGpsgnmD+2/KpG0ikvvR8+3v3COCwaF4r+oWwOeNA=
|
github.com/aws/aws-sdk-go-v2/credentials v1.19.8 h1:Jp2JYH1lRT3KhX4mshHPvVYsR5qqRec3hGvEarNYoR0=
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.21/go.mod h1:3YELwedmQbw7cXNaII2Wywd+YY58AmLPwX4LzARgmmA=
|
github.com/aws/aws-sdk-go-v2/credentials v1.19.8/go.mod h1:fZG9tuvyVfxknv1rKibIz3DobRaFw1Poe8IKtXB3XYY=
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 h1:T1brd5dR3/fzNFAQch/iBKeX07/ffu/cLu+q+RuzEWk=
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17 h1:I0GyV8wiYrP8XpA70g1HBcQO1JlQxCMTW9npl5UbDHY=
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13/go.mod h1:Peg/GBAQ6JDt+RoBf4meB1wylmAipb7Kg2ZFakZTlwk=
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17/go.mod h1:tyw7BOl5bBe/oqvoIeECFJjMdzXoa/dfVz3QQ5lgHGA=
|
||||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.20.4 h1:2fjfz3/G9BRvIKuNZ655GwzpklC2kEH0cowZQGO7uBg=
|
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.22.1 h1:IbWiN670htmBioc+Zj32vSpJgQ2+OYSlvTvfQ1nCORQ=
|
||||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.20.4/go.mod h1:Ymws824lvMypLFPwyyUXM52SXuGgxpu0+DISLfKvB+c=
|
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.22.1/go.mod h1:tw/B596EUhBWDFGdDGuLC21fVU4A3s4/5Efy8S39W18=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 h1:a+8/MLcWlIxo1lF9xaGt3J/u3yOZx+CdSveSNwjhD40=
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 h1:xOLELNKGp2vsiteLsvLPwxC+mYmO6OZ8PYgiuPJzF8U=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13/go.mod h1:oGnKwIYZ4XttyU2JWxFrwvhF6YKiK/9/wmE3v3Iu9K8=
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17/go.mod h1:5M5CI3D12dNOtH3/mk6minaRwI2/37ifCURZISxA/IQ=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13 h1:HBSI2kDkMdWz4ZM7FjwE7e/pWDEZ+nR95x8Ztet1ooY=
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 h1:WWLqlh79iO48yLkj1v3ISRNiv+3KdQoZ6JWyfcsyQik=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13/go.mod h1:YE94ZoDArI7awZqJzBAZ3PDD2zSfuP7w6P2knOzIn8M=
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17/go.mod h1:EhG22vHRrvF8oXSTYStZhJc1aUgKtnJe+aOiFEV90cM=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk=
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc=
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.13 h1:eg/WYAa12vqTphzIdWMzqYRVKKnCboVPRlvaybNCqPA=
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.17 h1:JqcdRG//czea7Ppjb+g/n4o8i/R50aTBHkA7vu0lK+k=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.13/go.mod h1:/FDdxWhz1486obGrKKC1HONd7krpk38LBt+dutLcN9k=
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.17/go.mod h1:CO+WeGmIdj/MlPel2KwID9Gt7CNq4M65HUfBW97liM0=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 h1:x2Ibm/Af8Fi+BH+Hsn9TXGdT+hKbDd5XOTZxTMxDk7o=
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3/go.mod h1:IW1jwyrQgMdhisceG8fQLmQIydcT/jWY21rFhzgaKwo=
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4 h1:NvMjwvv8hpGUILarKw7Z4Q0w1H9anXKsesMxtw++MA4=
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.8 h1:Z5EiPIzXKewUQK0QTMkutjiaPVeVYXX7KIqhXu/0fXs=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4/go.mod h1:455WPHSwaGj2waRSpQp7TsnpOnBfw8iDfPfbwl7KPJE=
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.8/go.mod h1:FsTpJtvC4U1fyDXk7c71XoDv3HlRm8V3NiYLeYLh5YE=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13 h1:kDqdFvMY4AtKoACfzIGD8A0+hbT41KTKF//gq7jITfM=
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 h1:RuNSMoozM8oXlgLG/n6WLaFGoea7/CddrCfIiSA+xdY=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13/go.mod h1:lmKuogqSU3HzQCwZ9ZtcqOc5XGMqtDK7OIc2+DxiUEg=
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17/go.mod h1:F2xxQ9TZz5gDWsclCtPQscGpP0VUOc8RqgFM3vDENmU=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13 h1:zhBJXdhWIFZ1acfDYIhu4+LCzdUS2Vbcum7D01dXlHQ=
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.17 h1:bGeHBsGZx0Dvu/eJC0Lh9adJa3M1xREcndxLNZlve2U=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13/go.mod h1:JaaOeCE368qn2Hzi3sEzY6FgAZVCIYcC2nwbro2QCh8=
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.17/go.mod h1:dcW24lbU0CzHusTE8LLHhRLI42ejmINN8Lcr22bwh/g=
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.90.0 h1:ef6gIJR+xv/JQWwpa5FYirzoQctfSJm7tuDe3SZsUf8=
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.0 h1:oeu8VPlOre74lBA/PMhxa5vewaMIMmILM+RraSyB8KA=
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.90.0/go.mod h1:+wArOOrcHUevqdto9k1tKOF5++YTe9JEcPSc9Tx2ZSw=
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.0/go.mod h1:5jggDlZ2CLQhwJBiZJb4vfk4f0GxWdEDruWKEJ1xOdo=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.1 h1:0JPwLz1J+5lEOfy/g0SURC9cxhbQ1lIMHMa+AHZSzz0=
|
github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 h1:VrhDvQib/i0lxvr3zqlUwLwJP4fpmpyD9wYG1vfSu+Y=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.1/go.mod h1:fKvyjJcz63iL/ftA6RaM8sRCtN4r4zl4tjL3qw5ec7k=
|
github.com/aws/aws-sdk-go-v2/service/signin v1.0.5/go.mod h1:k029+U8SY30/3/ras4G/Fnv/b88N4mAfliNn08Dem4M=
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5 h1:OWs0/j2UYR5LOGi88sD5/lhN6TDLG6SfA7CqsQO9zF0=
|
github.com/aws/aws-sdk-go-v2/service/sso v1.30.9 h1:v6EiMvhEYBoHABfbGB4alOYmCIrcgyPPiBE1wZAEbqk=
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5/go.mod h1:klO+ejMvYsB4QATfEOIXk8WAEwN4N0aBfJpvC+5SZBo=
|
github.com/aws/aws-sdk-go-v2/service/sso v1.30.9/go.mod h1:yifAsgBxgJWn3ggx70A3urX2AN49Y5sJTD1UQFlfqBw=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.39.1 h1:mLlUgHn02ue8whiR4BmxxGJLR2gwU6s6ZzJ5wDamBUs=
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.14 h1:0jbJeuEHlwKJ9PfXtpSFc4MF+WIWORdhN1n30ITZGFM=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.39.1/go.mod h1:E19xDjpzPZC7LS2knI9E6BaRFDK43Eul7vd6rSq2HWk=
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.14/go.mod h1:sTGThjphYE4Ohw8vJiRStAcu3rbjtXRsdNB0TvZ5wwo=
|
||||||
github.com/aws/smithy-go v1.23.2 h1:Crv0eatJUQhaManss33hS5r40CG3ZFH+21XSkqMrIUM=
|
github.com/aws/aws-sdk-go-v2/service/sts v1.41.6 h1:5fFjR/ToSOzB2OQ/XqWpZBmNvmP/pJ1jOWYlFDJTjRQ=
|
||||||
github.com/aws/smithy-go v1.23.2/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
|
github.com/aws/aws-sdk-go-v2/service/sts v1.41.6/go.mod h1:qgFDZQSD/Kys7nJnVqYlWKnh0SSdMjAi0uSwON4wgYQ=
|
||||||
|
github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk=
|
||||||
|
github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
|
||||||
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
|
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
|
||||||
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
|
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
@@ -193,15 +195,13 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
|
|||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
|
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
|
||||||
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
|
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||||
github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
|
|
||||||
github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
|
||||||
github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
|
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.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
|
||||||
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
|
||||||
github.com/cloudinary/cloudinary-go/v2 v2.13.0 h1:ugiQwb7DwpWQnete2AZkTh94MonZKmxD7hDGy1qTzDs=
|
github.com/cloudinary/cloudinary-go/v2 v2.14.1 h1:PK2pjdNl0OMuo5IvbwHF6o8uEzafD66q6LIYFAqt3ic=
|
||||||
github.com/cloudinary/cloudinary-go/v2 v2.13.0/go.mod h1:ireC4gqVetsjVhYlwjUJwKTbZuWjEIynbR9zQTlqsvo=
|
github.com/cloudinary/cloudinary-go/v2 v2.14.1/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 h1:t8YjNUCt1DimB4HCIXBztwWMhgxr5yG5/YaRl9Afdfg=
|
||||||
github.com/cloudsoda/go-smb2 v0.0.0-20250228001242-d4c70e6251cc/go.mod h1:CgWpFCFWzzEA5hVkhAc6DZZzGd3czx+BblvOzjmg6KA=
|
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=
|
github.com/cloudsoda/sddl v0.0.0-20250224235906-926454e91efc h1:0xCWmFKBmarCqqqLeM7jFBSw/Or81UEElFqO8MY+GDs=
|
||||||
@@ -268,24 +268,24 @@ 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/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 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.11 h1:AQvxbp830wPhHTqc1u7nzoLT+ZFxGY7emj5DR5DYFik=
|
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.11/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||||
github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw=
|
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/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo=
|
||||||
github.com/gdamore/tcell/v2 v2.9.0 h1:N6t+eqK7/xwtRPwxzs1PXeRWnm0H9l02CrgJ7DLn1ys=
|
github.com/gdamore/tcell/v2 v2.13.8 h1:Mys/Kl5wfC/GcC5Cx4C2BIQH9dbnhnkPgS9/wF3RlfU=
|
||||||
github.com/gdamore/tcell/v2 v2.9.0/go.mod h1:8/ZoqM9rxzYphT9tH/9LnunhV9oPBqwS8WHGYm5nrmo=
|
github.com/gdamore/tcell/v2 v2.13.8/go.mod h1:+Wfe208WDdB7INEtCsNrAN6O2m+wsTPk1RAovjaILlo=
|
||||||
github.com/geoffgarside/ber v1.2.0 h1:/loowoRcs/MWLYmGX9QtIAbA+V/FrnVLsMMPhwiRm64=
|
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/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 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
|
||||||
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
|
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 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||||
github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE=
|
github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug=
|
||||||
github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
|
github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0=
|
||||||
github.com/go-darwin/apfs v0.0.0-20211011131704-f84b94dbf348 h1:JnrjqG5iR07/8k7NqrLNilRsl3s1EPRQEGvbPyOce68=
|
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-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=
|
github.com/go-git/go-billy/v5 v5.7.0 h1:83lBUJhGWhYp0ngzCMSgllhUSuoHP1iEWYjsPl9nwqM=
|
||||||
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
|
github.com/go-git/go-billy/v5 v5.7.0/go.mod h1:/1IUejTKH8xipsAcdfcSAlUlo2J7lkYV8GTKxAT/L3E=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
@@ -299,8 +299,8 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre
|
|||||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||||
github.com/go-openapi/errors v0.22.4 h1:oi2K9mHTOb5DPW2Zjdzs/NIvwi2N3fARKaTJLdNabaM=
|
github.com/go-openapi/errors v0.22.6 h1:eDxcf89O8odEnohIXwEjY1IB4ph5vmbUsBMsFNwXWPo=
|
||||||
github.com/go-openapi/errors v0.22.4/go.mod h1:z9S8ASTUqx7+CP1Q8dD8ewGH/1JWFFLX/2PmAYNQLgk=
|
github.com/go-openapi/errors v0.22.6/go.mod h1:z9S8ASTUqx7+CP1Q8dD8ewGH/1JWFFLX/2PmAYNQLgk=
|
||||||
github.com/go-openapi/strfmt v0.25.0 h1:7R0RX7mbKLa9EYCTHRcCuIPcaqlyQiWNPTXwClK0saQ=
|
github.com/go-openapi/strfmt v0.25.0 h1:7R0RX7mbKLa9EYCTHRcCuIPcaqlyQiWNPTXwClK0saQ=
|
||||||
github.com/go-openapi/strfmt v0.25.0/go.mod h1:nNXct7OzbwrMY9+5tLX4I21pzcmE6ccMGXl3jFdPfn8=
|
github.com/go-openapi/strfmt v0.25.0/go.mod h1:nNXct7OzbwrMY9+5tLX4I21pzcmE6ccMGXl3jFdPfn8=
|
||||||
github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls=
|
github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls=
|
||||||
@@ -311,14 +311,14 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
|
|||||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688=
|
github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=
|
||||||
github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU=
|
github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
|
||||||
github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
|
github.com/go-resty/resty/v2 v2.17.2 h1:FQW5oHYcIlkCNrMD2lloGScxcHJ0gkjshV3qcQAyHQk=
|
||||||
github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
|
github.com/go-resty/resty/v2 v2.17.2/go.mod h1:kCKZ3wWmwJaNc7S29BRtUhJwy7iqmn+2mLtQrOyQlVA=
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
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/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||||
github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw=
|
github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw=
|
||||||
@@ -327,8 +327,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
|||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
|
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||||
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
|
||||||
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
@@ -386,12 +386,12 @@ github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
|||||||
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
|
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.7 h1:zrn2Ee/nWmHulBx5sAVrGgAa0f2/R35S4DJwfFaUPFQ=
|
github.com/googleapis/enterprise-certificate-proxy v0.3.12 h1:Fg+zsqzYEs1ZnvmcztTYxhgCBsx3eEhEwQ1W/lHq/sQ=
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.7/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
|
github.com/googleapis/enterprise-certificate-proxy v0.3.12/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
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.17.0 h1:RksgfBpxqff0EZkDWYuz9q/uWsTVz+kf43LsZ1J6SMc=
|
||||||
github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc=
|
github.com/googleapis/gax-go/v2 v2.17.0/go.mod h1:mzaqghpQp4JDh3HvADwrat+6M3MOIDp5YKHhb9PAgDY=
|
||||||
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
|
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/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 h1:jUg5hUjCSDZpNGLuXQOgIWGdlgrIdYvgQ0wZtdK1M3E=
|
||||||
@@ -423,8 +423,8 @@ github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyf
|
|||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/internxt/rclone-adapter v0.0.0-20260130171252-c3c6ebb49276 h1:PTJPYovznNqc9t/9MjvtqhrgEVC9OiK75ZPL6hqm6gM=
|
github.com/internxt/rclone-adapter v0.0.0-20260213125353-6f59c89fcb7c h1:r+KtxPyrhsYeNbsfeqTfEM8xRdwgV6LuNhLZxpXecb4=
|
||||||
github.com/internxt/rclone-adapter v0.0.0-20260130171252-c3c6ebb49276/go.mod h1:vdPya4AIcDjvng4ViaAzqjegJf0VHYpYHQguFx5xBp0=
|
github.com/internxt/rclone-adapter v0.0.0-20260213125353-6f59c89fcb7c/go.mod h1:vdPya4AIcDjvng4ViaAzqjegJf0VHYpYHQguFx5xBp0=
|
||||||
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
|
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
|
||||||
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
||||||
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
|
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
|
||||||
@@ -456,8 +456,8 @@ github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXw
|
|||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||||
github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co=
|
github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=
|
||||||
github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0=
|
github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
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 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||||
@@ -490,8 +490,8 @@ 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/lpar/date v1.0.0/go.mod h1:KjYe0dDyMQTgpqcUz4LEIeM5VZwhggjVx/V2dtc8NSo=
|
||||||
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
|
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/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k=
|
github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88 h1:PTw+yKnXcOFCR6+8hHTyWBeQ/P4Nb7dd4/0ohEcWQuM=
|
||||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
|
github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
|
||||||
github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=
|
github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=
|
||||||
github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||||
@@ -499,8 +499,8 @@ github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stg
|
|||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
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-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.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
|
github.com/mattn/go-runewidth v0.0.20 h1:WcT52H91ZUAwy8+HUkdM3THM6gXqXuLJi9O3rjcQQaQ=
|
||||||
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
github.com/mattn/go-runewidth v0.0.20/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||||
github.com/mholt/archives v0.1.5 h1:Fh2hl1j7VEhc6DZs2DLMgiBNChUux154a1G+2esNvzQ=
|
github.com/mholt/archives v0.1.5 h1:Fh2hl1j7VEhc6DZs2DLMgiBNChUux154a1G+2esNvzQ=
|
||||||
github.com/mholt/archives v0.1.5/go.mod h1:3TPMmBLPsgszL+1As5zECTuKwKvIfj6YcwWPpeTAXF4=
|
github.com/mholt/archives v0.1.5/go.mod h1:3TPMmBLPsgszL+1As5zECTuKwKvIfj6YcwWPpeTAXF4=
|
||||||
github.com/mikelolasagasti/xz v1.0.1 h1:Q2F2jX0RYJUG3+WsM+FJknv+6eVjsjXNDV0KJXZzkD0=
|
github.com/mikelolasagasti/xz v1.0.1 h1:Q2F2jX0RYJUG3+WsM+FJknv+6eVjsjXNDV0KJXZzkD0=
|
||||||
@@ -509,8 +509,8 @@ 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/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 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||||
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||||
github.com/minio/minio-go/v7 v7.0.97 h1:lqhREPyfgHTB/ciX8k2r8k0D93WaFqxbJX36UZq5occ=
|
github.com/minio/minio-go/v7 v7.0.98 h1:MeAVKjLVz+XJ28zFcuYyImNSAh8Mq725uNW4beRisi0=
|
||||||
github.com/minio/minio-go/v7 v7.0.97/go.mod h1:re5VXuo0pwEtoNLsNuSr0RrLfT/MBtohwdaSmPPSRSk=
|
github.com/minio/minio-go/v7 v7.0.98/go.mod h1:cY0Y+W7yozf0mdIclrttzo1Iiu7mEf9y7nk2uXqMOvM=
|
||||||
github.com/minio/minlz v1.0.1 h1:OUZUzXcib8diiX+JYxyRLIdomyZYzHct6EShOKtQY2A=
|
github.com/minio/minlz v1.0.1 h1:OUZUzXcib8diiX+JYxyRLIdomyZYzHct6EShOKtQY2A=
|
||||||
github.com/minio/minlz v1.0.1/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
|
github.com/minio/minlz v1.0.1/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
|
||||||
github.com/minio/xxml v0.0.3 h1:ZIpPQpfyG5uZQnqqC0LZuWtPk/WT8G/qkxvO6jb7zMU=
|
github.com/minio/xxml v0.0.3 h1:ZIpPQpfyG5uZQnqqC0LZuWtPk/WT8G/qkxvO6jb7zMU=
|
||||||
@@ -527,8 +527,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
|
|||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/ncw/swift/v2 v2.0.5 h1:9o5Gsd7bInAFEqsGPcaUdsboMbqf8lnNtxqWKFT9iz8=
|
github.com/ncw/swift/v2 v2.0.5 h1:9o5Gsd7bInAFEqsGPcaUdsboMbqf8lnNtxqWKFT9iz8=
|
||||||
github.com/ncw/swift/v2 v2.0.5/go.mod h1:cbAO76/ZwcFrFlHdXPjaqWZ9R7Hdar7HpjRXBfbjigk=
|
github.com/ncw/swift/v2 v2.0.5/go.mod h1:cbAO76/ZwcFrFlHdXPjaqWZ9R7Hdar7HpjRXBfbjigk=
|
||||||
github.com/nwaples/rardecode/v2 v2.2.1 h1:DgHK/O/fkTQEKBJxBMC5d9IU8IgauifbpG78+rZJMnI=
|
github.com/nwaples/rardecode/v2 v2.2.2 h1:/5oL8dzYivRM/tqX9VcTSWfbpwcbwKG1QtSJr3b3KcU=
|
||||||
github.com/nwaples/rardecode/v2 v2.2.1/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
|
github.com/nwaples/rardecode/v2 v2.2.2/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
|
||||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||||
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
||||||
@@ -537,12 +537,12 @@ github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
|||||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||||
github.com/onsi/ginkgo/v2 v2.17.3 h1:oJcvKpIb7/8uLpDDtnQuf18xVnwKp8DTD7DQ6gTd/MU=
|
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/ginkgo/v2 v2.17.3/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc=
|
||||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
|
||||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
|
||||||
github.com/oracle/oci-go-sdk/v65 v65.104.0 h1:l9awEvzWvxmYhy/97A0hZ87pa7BncYXmcO/S8+rvgK0=
|
github.com/oracle/oci-go-sdk/v65 v65.108.2 h1:emoGAxw/vcqoKHgUy6a10RIhAQbaDPQPiuIcoZuoJGw=
|
||||||
github.com/oracle/oci-go-sdk/v65 v65.104.0/go.mod h1:oB8jFGVc/7/zJ+DbleE8MzGHjhs2ioCz5stRTdZdIcY=
|
github.com/oracle/oci-go-sdk/v65 v65.108.2/go.mod h1:8ZzvzuEG/cFLFZhxg/Mg1w19KqyXBKO3c17QIc5PkGs=
|
||||||
github.com/panjf2000/ants/v2 v2.11.3 h1:AfI0ngBoXJmYOpDh9m516vjqoUu2sLrIVgppI9TZVpg=
|
github.com/panjf2000/ants/v2 v2.11.5 h1:a7LMnMEeux/ebqTux140tRiaqcFTV0q2bEHF03nl6Rg=
|
||||||
github.com/panjf2000/ants/v2 v2.11.3/go.mod h1:8u92CYMUc6gyvTIw8Ru7Mt7+/ESnJahz5EVtqfrilek=
|
github.com/panjf2000/ants/v2 v2.11.5/go.mod h1:8u92CYMUc6gyvTIw8Ru7Mt7+/ESnJahz5EVtqfrilek=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||||
@@ -553,8 +553,8 @@ github.com/peterh/liner v1.2.2 h1:aJ4AOodmL+JxOZZEL2u9iJf8omNRpqHc/EbrK+3mAXw=
|
|||||||
github.com/peterh/liner v1.2.2/go.mod h1:xFwJyiKIXJZUKItq5dGHZSTBRAuG/CpeNpWLyiNRNwI=
|
github.com/peterh/liner v1.2.2/go.mod h1:xFwJyiKIXJZUKItq5dGHZSTBRAuG/CpeNpWLyiNRNwI=
|
||||||
github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
|
github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
|
||||||
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
||||||
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
|
github.com/pierrec/lz4/v4 v4.1.25 h1:kocOqRffaIbU5djlIBr7Wh+cx82C0vtFb0fOurZHqD0=
|
||||||
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
github.com/pierrec/lz4/v4 v4.1.25/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4=
|
||||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||||
github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28=
|
github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28=
|
||||||
@@ -576,8 +576,8 @@ github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UH
|
|||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
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 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
github.com/prometheus/common v0.67.2 h1:PcBAckGFTIHt2+L3I33uNRTlKTplNzFctXcWhPyAEN8=
|
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
||||||
github.com/prometheus/common v0.67.2/go.mod h1:63W3KZb1JOKgcjlIr64WW/LvFGAqKPj0atm+knVGEko=
|
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
||||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
||||||
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 h1:Y258uzXU/potCYnQd1r6wlAnoMB68BiCkCcCnKx1SH8=
|
||||||
@@ -596,8 +596,8 @@ 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/rclone/gofakes3 v0.0.4/go.mod h1:j/UoS+2/Mr7xAlfKhyVC58YyFQmh9uoQA5YZQXQUqmg=
|
||||||
github.com/relvacode/iso8601 v1.7.0 h1:BXy+V60stMP6cpswc+a93Mq3e65PfXCgDFfhvNNGrdo=
|
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/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.2.0 h1:8dAHL+WVAw06+7DkRKnRiFp1JL3QjcJEZFqDnndUaSI=
|
||||||
github.com/rfjakob/eme v1.1.2/go.mod h1:cVvpasglm/G3ngEfcfT/Wt0GwhkuO32pf/poW6Nyk1k=
|
github.com/rfjakob/eme v1.2.0/go.mod h1:cVvpasglm/G3ngEfcfT/Wt0GwhkuO32pf/poW6Nyk1k=
|
||||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
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=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
@@ -607,7 +607,6 @@ github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
|||||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
|
|
||||||
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a8tTFrMLUcfWwyC0pnifVo2ClaLq+hP8=
|
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a8tTFrMLUcfWwyC0pnifVo2ClaLq+hP8=
|
||||||
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8=
|
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8=
|
||||||
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI=
|
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI=
|
||||||
@@ -617,11 +616,11 @@ github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRo
|
|||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
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 h1:S77Pf5fIGMa7oSwp8SQPp7Hb4ZiI38K3RNBKD2LLeEM=
|
||||||
github.com/shabbyrobe/gocovmerge v0.0.0-20230507112040-c3350d9342df/go.mod h1:dcuzJZ83w/SqN9k4eQqwKYMgmKWzg/KzJAURBhRL1tc=
|
github.com/shabbyrobe/gocovmerge v0.0.0-20230507112040-c3350d9342df/go.mod h1:dcuzJZ83w/SqN9k4eQqwKYMgmKWzg/KzJAURBhRL1tc=
|
||||||
github.com/shirou/gopsutil/v4 v4.25.10 h1:at8lk/5T1OgtuCp+AwrDofFRjnvosn0nkN2OLQ6g8tA=
|
github.com/shirou/gopsutil/v4 v4.26.1 h1:TOkEyriIXk2HX9d4isZJtbjXbEjf5qyKPAzbzY0JWSo=
|
||||||
github.com/shirou/gopsutil/v4 v4.25.10/go.mod h1:+kSwyC8DRUD9XXEHCAFjK+0nuArFJM0lva+StQAcskM=
|
github.com/shirou/gopsutil/v4 v4.26.1/go.mod h1:medLI9/UNAb0dOI9Q3/7yWSqKkj00u+1tgY8nvv41pc=
|
||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af h1:Sp5TG9f7K39yfB+If0vjp97vuT74F72r8hfRpP8jLU0=
|
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
|
||||||
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
|
||||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
|
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/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
||||||
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
|
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
|
||||||
@@ -638,8 +637,8 @@ github.com/spacemonkeygo/monkit/v3 v3.0.25-0.20251022131615-eb24eb109368 h1:GyYC
|
|||||||
github.com/spacemonkeygo/monkit/v3 v3.0.25-0.20251022131615-eb24eb109368/go.mod h1:XkZYGzknZwkD0AKUnZaSXhRiVTLCkq7CWVa3IsE72gA=
|
github.com/spacemonkeygo/monkit/v3 v3.0.25-0.20251022131615-eb24eb109368/go.mod h1:XkZYGzknZwkD0AKUnZaSXhRiVTLCkq7CWVa3IsE72gA=
|
||||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||||
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||||
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
|
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||||
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
|
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
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 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
@@ -659,15 +658,15 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
|||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
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/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
github.com/t3rm1n4l/go-mega v0.0.0-20251031123324-a804aaa87491 h1:rrGZv6xYk37hx0tW2sYfgbO0PqStbHqz6Bq6oc9Hurg=
|
github.com/t3rm1n4l/go-mega v0.0.0-20251120131202-6845944c051c h1:dtcOwRimeiBFrlutmF6K94l0rxYFARNFMA+lSQ41C+M=
|
||||||
github.com/t3rm1n4l/go-mega v0.0.0-20251031123324-a804aaa87491/go.mod h1:ykucQyiE9Q2qx1wLlEtZkkNn1IURib/2O+Mvd25i1Fo=
|
github.com/t3rm1n4l/go-mega v0.0.0-20251120131202-6845944c051c/go.mod h1:BF/l2jNyK+2h/BJZ7VLMAz6m/IWjA2F67gTjV1C/+Bo=
|
||||||
github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8=
|
github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8=
|
||||||
github.com/tinylib/msgp v1.5.0 h1:GWnqAE54wmnlFazjq2+vgr736Akg58iiHImh+kPY2pc=
|
github.com/tinylib/msgp v1.6.3 h1:bCSxiTz386UTgyT1i0MSCvdbWjVW+8sG3PjkGsZQt4s=
|
||||||
github.com/tinylib/msgp v1.5.0/go.mod h1:cvjFkb4RiC8qSBOPMGPSzSAx47nAsfhLVTCZZNuHv5o=
|
github.com/tinylib/msgp v1.6.3/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA=
|
||||||
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
|
github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
|
||||||
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
|
github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
|
||||||
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
|
github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=
|
||||||
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
|
||||||
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c h1:u6SKchux2yDvFQnDHS3lPnIRmfVJ5Sxy3ao2SIdysLQ=
|
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c h1:u6SKchux2yDvFQnDHS3lPnIRmfVJ5Sxy3ao2SIdysLQ=
|
||||||
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM=
|
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
@@ -712,12 +711,12 @@ github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM=
|
|||||||
github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
|
github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
|
||||||
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
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/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.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
|
||||||
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
|
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
|
||||||
go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
|
go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
|
||||||
go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
|
go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
|
||||||
go.mongodb.org/mongo-driver v1.17.6 h1:87JUG1wZfWsr6rIz3ZmpH90rL5tea7O3IHuSwHUpsss=
|
go.mongodb.org/mongo-driver v1.17.9 h1:IexDdCuuNJ3BHrELgBlyaH9p60JXAvdzWR128q+U5tU=
|
||||||
go.mongodb.org/mongo-driver v1.17.6/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
|
go.mongodb.org/mongo-driver v1.17.9/go.mod h1:LlOhpH5NUEfhxcAwG0UEkMqwYcc4JU18gtCdGudk/tQ=
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
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.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
@@ -725,26 +724,28 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
|||||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
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/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.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8=
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0=
|
||||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
|
||||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
|
||||||
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
|
||||||
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
|
||||||
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
|
||||||
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
|
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
|
go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
|
||||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
|
||||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
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/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 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||||
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||||
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
|
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=
|
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
|
||||||
go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc=
|
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||||
go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU=
|
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||||
|
go4.org v0.0.0-20260112195520-a5071408f32f h1:ziUVAjmTPwQMBmYR1tbdRFJPtTcQUI12fH9QQjfb0Sw=
|
||||||
|
go4.org v0.0.0-20260112195520-a5071408f32f/go.mod h1:ZRJnO5ZI4zAwMFp+dS1+V6J6MSyAowhRqAE+DPa1Xp0=
|
||||||
goftp.io/server/v2 v2.0.2 h1:tkZpqyXys+vC15W5yGMi8Kzmbv1QSgeKr8qJXBnJbm8=
|
goftp.io/server/v2 v2.0.2 h1:tkZpqyXys+vC15W5yGMi8Kzmbv1QSgeKr8qJXBnJbm8=
|
||||||
goftp.io/server/v2 v2.0.2/go.mod h1:Fl1WdcV7fx1pjOWx7jEHb7tsJ8VwE7+xHu6bVJ6r2qg=
|
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 h1:z9JUEZWr8x4rR0OU6c4/4t6E6jOZ8/QBS2bBYBm4tx4=
|
||||||
@@ -764,8 +765,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
|
|||||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
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.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
||||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
@@ -776,12 +777,12 @@ 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-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-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-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY=
|
golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a h1:ovFr6Z0MNmU7nH8VaX5xqw+05ST2uO1exVfZPVqRC5o=
|
||||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70=
|
golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA=
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
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/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.32.0 h1:6lZQWq75h7L5IWNk0r+SCpUJ6tUVd3v4ZHnbRKLkUDQ=
|
golang.org/x/image v0.36.0 h1:Iknbfm1afbgtwPTmHnS2gTM/6PPZfH+z2EFuOkSbqwc=
|
||||||
golang.org/x/image v0.32.0/go.mod h1:/R37rrQmKXtO6tYXAjtDLwQgFLHmhW+V6ayXlxzP2Pc=
|
golang.org/x/image v0.36.0/go.mod h1:YsWD2TyyGKiIX1kZlu9QfKIsQ4nAAK9bdgdrIsE7xy4=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
@@ -794,8 +795,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/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-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-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||||
golang.org/x/mobile v0.0.0-20251021151156-188f512ec823 h1:M0DtBf/UvJoTH+tk6tgHT2NVxNEJCYhVu1g/xeD+GEk=
|
golang.org/x/mobile v0.0.0-20260217195705-b56b3793a9c4 h1:uT3oYo9M38vJa7JpT4kCie2lJwOpoUrx7FvV0H7kXSc=
|
||||||
golang.org/x/mobile v0.0.0-20251021151156-188f512ec823/go.mod h1:3QSlP0AtP6HPTLbsxfgfefGN76jpIB9yBsMqB8UY37I=
|
golang.org/x/mobile v0.0.0-20260217195705-b56b3793a9c4/go.mod h1:4OGHIUSBiIqyFAQDaX1tpY0BVnO20DvNDeATBu8aeFQ=
|
||||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
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.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
@@ -808,8 +809,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.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.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.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
|
||||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
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-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@@ -849,16 +850,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.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
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.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
||||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
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-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-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-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-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.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo=
|
golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ=
|
||||||
golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
golang.org/x/oauth2 v0.35.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-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-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -874,8 +875,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
|||||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
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.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
golang.org/x/sync v0.19.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-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-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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -925,8 +926,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.17.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.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.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
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-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
@@ -938,8 +939,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
|||||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
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.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||||
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
|
||||||
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
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.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
@@ -955,8 +956,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
|||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
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.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.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
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-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.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
@@ -1010,8 +1011,8 @@ 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.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
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.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
|
||||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
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-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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@@ -1034,8 +1035,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.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.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||||
google.golang.org/api v0.255.0 h1:OaF+IbRwOottVCYV2wZan7KUq7UeNUQn1BcPc4K7lE4=
|
google.golang.org/api v0.267.0 h1:w+vfWPMPYeRs8qH1aYYsFX68jMls5acWl/jocfLomwE=
|
||||||
google.golang.org/api v0.255.0/go.mod h1:d1/EtvCLdtiWEV4rAEHDHGh2bCnqsWhw+M8y2ECN4a8=
|
google.golang.org/api v0.267.0/go.mod h1:Jzc0+ZfLnyvXma3UtaTl023TdhZu6OMBP9tJ+0EmFD0=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
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.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
@@ -1071,12 +1072,12 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc
|
|||||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
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-20260128011058-8636f8732409 h1:VQZ/yAbAtjkHgH80teYd2em3xtIkkHd7ZhqfH2N9CsM=
|
||||||
google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s=
|
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409/go.mod h1:rxKD3IEILWEu3P44seeNOAwZN4SaoKaQ/2eTg4mM6EM=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20250804133106-a7a43d27e69b h1:ULiyYQ0FdsJhwwZUwbaXpZF5yUE3h+RA+gxvBu37ucc=
|
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20250804133106-a7a43d27e69b/go.mod h1:oDOGiMSXHL4sDTJvFvIB9nRQCGdLP1o/iVaqQK8zB+M=
|
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101 h1:tRPGkdGHuewF4UisLzzHHr1spKw92qLM98nIzxbC0wY=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d h1:t/LOSXPJ9R0B6fnZNyALBRfZBH0Uy0gT+uR+SJ6syqQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
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.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
@@ -1089,8 +1090,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.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.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.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
|
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||||
google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
|
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
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-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
@@ -1101,8 +1102,8 @@ 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.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.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
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-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 h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
@@ -1132,8 +1133,10 @@ moul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHc
|
|||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||||
storj.io/common v0.0.0-20251107171817-6221ae45072c h1:UDXSrdeLJe3QFouavSW10fYdpclK0YNu3KvQHzqq2+k=
|
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
|
||||||
storj.io/common v0.0.0-20251107171817-6221ae45072c/go.mod h1:XNX7uykja6aco92y2y8RuqaXIDRPpt1YA2OQDKlKEUk=
|
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
|
||||||
|
storj.io/common v0.0.0-20260212175235-9580cc9c5777 h1:FkBDQhObVplAc6Ug4SnQDfq4zpw8d3zFy19Zrc3hTvI=
|
||||||
|
storj.io/common v0.0.0-20260212175235-9580cc9c5777/go.mod h1:XNX7uykja6aco92y2y8RuqaXIDRPpt1YA2OQDKlKEUk=
|
||||||
storj.io/drpc v0.0.35-0.20250513201419-f7819ea69b55 h1:8OE12DvUnB9lfZcHe7IDGsuhjrY9GBAr964PVHmhsro=
|
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/drpc v0.0.35-0.20250513201419-f7819ea69b55/go.mod h1:Y9LZaa8esL1PW2IDMqJE7CFSNq7d5bQ3RI7mGPtmKMg=
|
||||||
storj.io/eventkit v0.0.0-20250410172343-61f26d3de156 h1:5MZ0CyMbG6Pi0rRzUWVG6dvpXjbBYEX2oyXuj+tT+sk=
|
storj.io/eventkit v0.0.0-20250410172343-61f26d3de156 h1:5MZ0CyMbG6Pi0rRzUWVG6dvpXjbBYEX2oyXuj+tT+sk=
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ func Walk(err error, f WalkFunc) {
|
|||||||
// *os.SyscallError and many others in the stdlib.
|
// *os.SyscallError and many others in the stdlib.
|
||||||
errType := reflect.TypeOf(err)
|
errType := reflect.TypeOf(err)
|
||||||
errValue := reflect.ValueOf(err)
|
errValue := reflect.ValueOf(err)
|
||||||
if errValue.IsValid() && errType.Kind() == reflect.Ptr {
|
if errValue.IsValid() && errType.Kind() == reflect.Pointer {
|
||||||
errType = errType.Elem()
|
errType = errType.Elem()
|
||||||
errValue = errValue.Elem()
|
errValue = errValue.Elem()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,18 +159,30 @@ func (p *Pacer) beginCall(limitConnections bool) {
|
|||||||
// XXX ms later we put another in. We could do this with a
|
// 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
|
// Ticker more accurately, but then we'd have to work out how
|
||||||
// not to run it when it wasn't needed
|
// not to run it when it wasn't needed
|
||||||
<-p.pacer
|
|
||||||
|
p.mu.Lock()
|
||||||
|
sleepTime := p.state.SleepTime
|
||||||
|
p.mu.Unlock()
|
||||||
|
|
||||||
|
if sleepTime > 0 {
|
||||||
|
<-p.pacer
|
||||||
|
|
||||||
|
// Re-read the sleep time as it may be stale
|
||||||
|
// after waiting for the pacer token
|
||||||
|
p.mu.Lock()
|
||||||
|
sleepTime = p.state.SleepTime
|
||||||
|
p.mu.Unlock()
|
||||||
|
|
||||||
|
// Restart the timer
|
||||||
|
go func(t time.Duration) {
|
||||||
|
time.Sleep(t)
|
||||||
|
p.pacer <- struct{}{}
|
||||||
|
}(sleepTime)
|
||||||
|
}
|
||||||
|
|
||||||
if limitConnections {
|
if limitConnections {
|
||||||
<-p.connTokens
|
<-p.connTokens
|
||||||
}
|
}
|
||||||
|
|
||||||
p.mu.Lock()
|
|
||||||
// Restart the timer
|
|
||||||
go func(t time.Duration) {
|
|
||||||
time.Sleep(t)
|
|
||||||
p.pacer <- struct{}{}
|
|
||||||
}(p.state.SleepTime)
|
|
||||||
p.mu.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// endCall implements the pacing algorithm
|
// endCall implements the pacing algorithm
|
||||||
|
|||||||
@@ -367,6 +367,39 @@ func TestCallMaxConnectionsRecursiveDeadlock(t *testing.T) {
|
|||||||
assert.Equal(t, errFoo, err)
|
assert.Equal(t, errFoo, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCallMaxConnectionsRecursiveDeadlock2(t *testing.T) {
|
||||||
|
p := New(CalculatorOption(NewDefault(MinSleep(1*time.Millisecond), MaxSleep(2*time.Millisecond))))
|
||||||
|
p.SetMaxConnections(1)
|
||||||
|
dp := &dummyPaced{retry: false}
|
||||||
|
wg := new(sync.WaitGroup)
|
||||||
|
|
||||||
|
// Normal
|
||||||
|
for range 100 {
|
||||||
|
wg.Go(func() {
|
||||||
|
err := p.Call(func() (bool, error) {
|
||||||
|
// check we have taken the connection token
|
||||||
|
assert.Equal(t, 0, len(p.connTokens))
|
||||||
|
return false, nil
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Now attempt a recursive call
|
||||||
|
wg.Go(func() {
|
||||||
|
err := p.Call(func() (bool, error) {
|
||||||
|
// check we have taken the connection token
|
||||||
|
assert.Equal(t, 0, len(p.connTokens))
|
||||||
|
// Do recursive call
|
||||||
|
return false, p.Call(dp.fn)
|
||||||
|
})
|
||||||
|
assert.Equal(t, errFoo, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tidy up
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
func TestRetryAfterError_NonNilErr(t *testing.T) {
|
func TestRetryAfterError_NonNilErr(t *testing.T) {
|
||||||
orig := errors.New("test failure")
|
orig := errors.New("test failure")
|
||||||
dur := 2 * time.Second
|
dur := 2 * time.Second
|
||||||
|
|||||||
@@ -290,9 +290,7 @@ func TestPoolMaxBufferMemory(t *testing.T) {
|
|||||||
)
|
)
|
||||||
const trials = 50
|
const trials = 50
|
||||||
for i := range trials {
|
for i := range trials {
|
||||||
wg.Add(1)
|
wg.Go(func() {
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
if i < trials/2 {
|
if i < trials/2 {
|
||||||
n := i%4 + 1
|
n := i%4 + 1
|
||||||
buf := bp.GetN(n)
|
buf := bp.GetN(n)
|
||||||
@@ -307,7 +305,7 @@ func TestPoolMaxBufferMemory(t *testing.T) {
|
|||||||
countBuf(-1)
|
countBuf(-1)
|
||||||
bp.Put(buf)
|
bp.Put(buf)
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|||||||
@@ -563,11 +563,9 @@ func TestRWConcurrency(t *testing.T) {
|
|||||||
writeTo := func(rw *RW, size int64) {
|
writeTo := func(rw *RW, size int64) {
|
||||||
in, out := io.Pipe()
|
in, out := io.Pipe()
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(1)
|
wg.Go(func() {
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
check(in, size, rw)
|
check(in, size, rw)
|
||||||
}()
|
})
|
||||||
var n int64
|
var n int64
|
||||||
for n < size {
|
for n < size {
|
||||||
nn, err := rw.WriteTo(out)
|
nn, err := rw.WriteTo(out)
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
@@ -374,26 +373,11 @@ func (api *Client) Call(ctx context.Context, opts *Opts) (resp *http.Response, e
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
|
|
||||||
|
|
||||||
func escapeQuotes(s string) string {
|
|
||||||
return quoteEscaper.Replace(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// multipartFileContentDisposition returns the value of a Content-Disposition header
|
|
||||||
// with the provided field name and file name.
|
|
||||||
func multipartFileContentDisposition(fieldname, filename string) string {
|
|
||||||
return fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
|
|
||||||
escapeQuotes(fieldname), escapeQuotes(filename))
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateFormFile is a convenience wrapper around [Writer.CreatePart]. It creates
|
// CreateFormFile is a convenience wrapper around [Writer.CreatePart]. It creates
|
||||||
// a new form-data header with the provided field name and file name.
|
// a new form-data header with the provided field name and file name.
|
||||||
func CreateFormFile(w *multipart.Writer, fieldname, filename, contentType string) (io.Writer, error) {
|
func CreateFormFile(w *multipart.Writer, fieldname, filename, contentType string) (io.Writer, error) {
|
||||||
h := make(textproto.MIMEHeader)
|
h := make(textproto.MIMEHeader)
|
||||||
// FIXME when go1.24 is no longer supported, change to
|
h.Set("Content-Disposition", multipart.FileContentDisposition(fieldname, filename))
|
||||||
// multipart.FileContentDisposition and remove definition above
|
|
||||||
h.Set("Content-Disposition", multipartFileContentDisposition(fieldname, filename))
|
|
||||||
if contentType != "" {
|
if contentType != "" {
|
||||||
h.Set("Content-Type", contentType)
|
h.Set("Content-Type", contentType)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -296,13 +296,11 @@ func main() {
|
|||||||
quit = make(chan struct{}, *iterations)
|
quit = make(chan struct{}, *iterations)
|
||||||
)
|
)
|
||||||
for range *number {
|
for range *number {
|
||||||
wg.Add(1)
|
wg.Go(func() {
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
t := NewTest(dir)
|
t := NewTest(dir)
|
||||||
defer t.Tidy()
|
defer t.Tidy()
|
||||||
t.RandomTests(*iterations, quit)
|
t.RandomTests(*iterations, quit)
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,9 +115,7 @@ func New(item Item, opt *vfscommon.Options, remote string, src fs.Object) (dls *
|
|||||||
src: src,
|
src: src,
|
||||||
remote: remote,
|
remote: remote,
|
||||||
}
|
}
|
||||||
dls.wg.Add(1)
|
dls.wg.Go(func() {
|
||||||
go func() {
|
|
||||||
defer dls.wg.Done()
|
|
||||||
ticker := time.NewTicker(backgroundKickerInterval)
|
ticker := time.NewTicker(backgroundKickerInterval)
|
||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
@@ -129,7 +127,7 @@ func New(item Item, opt *vfscommon.Options, remote string, src fs.Object) (dls *
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
ticker.Stop()
|
ticker.Stop()
|
||||||
}()
|
})
|
||||||
|
|
||||||
return dls
|
return dls
|
||||||
}
|
}
|
||||||
@@ -189,9 +187,7 @@ func (dls *Downloaders) _newDownloader(r ranges.Range) (dl *downloader, err erro
|
|||||||
|
|
||||||
dls.dls = append(dls.dls, dl)
|
dls.dls = append(dls.dls, dl)
|
||||||
|
|
||||||
dl.wg.Add(1)
|
dl.wg.Go(func() {
|
||||||
go func() {
|
|
||||||
defer dl.wg.Done()
|
|
||||||
n, err := dl.download()
|
n, err := dl.download()
|
||||||
_ = dl.close(err)
|
_ = dl.close(err)
|
||||||
dl.dls.countErrors(n, err)
|
dl.dls.countErrors(n, err)
|
||||||
@@ -202,7 +198,7 @@ func (dls *Downloaders) _newDownloader(r ranges.Range) (dl *downloader, err erro
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
fs.Errorf(dl.dls.src, "vfs cache: failed to kick waiters: %v", err)
|
fs.Errorf(dl.dls.src, "vfs cache: failed to kick waiters: %v", err)
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
|
|
||||||
return dl, nil
|
return dl, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -542,9 +542,7 @@ func TestItemReadWrite(t *testing.T) {
|
|||||||
assert.False(t, item.present())
|
assert.False(t, item.present())
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
for range 8 {
|
for range 8 {
|
||||||
wg.Add(1)
|
wg.Go(func() {
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
in := readers.NewPatternReader(size)
|
in := readers.NewPatternReader(size)
|
||||||
buf := make([]byte, 1024*1024)
|
buf := make([]byte, 1024*1024)
|
||||||
buf2 := make([]byte, 1024*1024)
|
buf2 := make([]byte, 1024*1024)
|
||||||
@@ -553,7 +551,7 @@ func TestItemReadWrite(t *testing.T) {
|
|||||||
offset := max(rand.Int63n(size+2*int64(blockSize))-int64(blockSize), 0)
|
offset := max(rand.Int63n(size+2*int64(blockSize))-int64(blockSize), 0)
|
||||||
_, _ = readCheckBuf(t, in, buf, buf2, item, offset, blockSize)
|
_, _ = readCheckBuf(t, in, buf, buf2, item, offset, blockSize)
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
require.NoError(t, item.Close(nil))
|
require.NoError(t, item.Close(nil))
|
||||||
|
|||||||
Reference in New Issue
Block a user