mirror of
https://github.com/rclone/rclone.git
synced 2026-02-03 10:13:22 +00:00
Compare commits
28 Commits
fix-6426-f
...
pr-6474-pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3450d049b5 | ||
|
|
16b383e18f | ||
|
|
d59909fb8c | ||
|
|
cec47699d3 | ||
|
|
0687a263f8 | ||
|
|
3192e00f4f | ||
|
|
14534c573a | ||
|
|
408d0c729b | ||
|
|
a716dc2533 | ||
|
|
28f0c08a98 | ||
|
|
458c477ad8 | ||
|
|
7130a6d2e4 | ||
|
|
4e078765f9 | ||
|
|
7fbc928a19 | ||
|
|
27096323db | ||
|
|
7e547822d6 | ||
|
|
67625b1dbd | ||
|
|
88086643f7 | ||
|
|
5f13d84135 | ||
|
|
07efdb55fa | ||
|
|
fb6ddd680c | ||
|
|
bc09105d2e | ||
|
|
4f374bc264 | ||
|
|
1c99661d8c | ||
|
|
04b54bbb1e | ||
|
|
90cda2d6c2 | ||
|
|
dbd9ce78e6 | ||
|
|
cbc18e2693 |
14
.github/workflows/build.yml
vendored
14
.github/workflows/build.yml
vendored
@@ -97,12 +97,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
stable: 'false'
|
||||
go-version: ${{ matrix.go }}
|
||||
@@ -162,7 +162,7 @@ jobs:
|
||||
env
|
||||
|
||||
- name: Go module cache
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
@@ -226,7 +226,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Code quality test
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
@@ -242,18 +242,18 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
# Upgrade together with NDK version
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v1
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.19.x
|
||||
|
||||
- name: Go module cache
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
name: Build image job
|
||||
steps:
|
||||
- name: Checkout master
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Build and publish image
|
||||
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
name: Build image job
|
||||
steps:
|
||||
- name: Checkout master
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Get actual patch version
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
name: Build docker plugin job
|
||||
steps:
|
||||
- name: Checkout master
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Build and publish docker plugin
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"github.com/rclone/rclone/fs/config/configstruct"
|
||||
"github.com/rclone/rclone/fs/fspath"
|
||||
"github.com/rclone/rclone/fs/hash"
|
||||
"github.com/rclone/rclone/fs/log"
|
||||
"github.com/rclone/rclone/fs/object"
|
||||
"github.com/rclone/rclone/fs/operations"
|
||||
)
|
||||
@@ -367,13 +368,16 @@ func (f *Fs) NewObject(ctx context.Context, remote string) (fs.Object, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
meta := readMetadata(ctx, mo)
|
||||
if meta == nil {
|
||||
return nil, errors.New("error decoding metadata")
|
||||
meta, err := readMetadata(ctx, mo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding metadata: %w", err)
|
||||
}
|
||||
// Create our Object
|
||||
o, err := f.Fs.NewObject(ctx, makeDataName(remote, meta.CompressionMetadata.Size, meta.Mode))
|
||||
return f.newObject(o, mo, meta), err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f.newObject(o, mo, meta), nil
|
||||
}
|
||||
|
||||
// checkCompressAndType checks if an object is compressible and determines it's mime type
|
||||
@@ -677,7 +681,7 @@ func (f *Fs) putWithCustomFunctions(ctx context.Context, in io.Reader, src fs.Ob
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return f.newObject(dataObject, mo, meta), err
|
||||
return f.newObject(dataObject, mo, meta), nil
|
||||
}
|
||||
|
||||
// Put in to the remote path with the modTime given of the given size
|
||||
@@ -1040,24 +1044,19 @@ func newMetadata(size int64, mode int, cmeta sgzip.GzipMetadata, md5 string, mim
|
||||
}
|
||||
|
||||
// This function will read the metadata from a metadata object.
|
||||
func readMetadata(ctx context.Context, mo fs.Object) (meta *ObjectMetadata) {
|
||||
func readMetadata(ctx context.Context, mo fs.Object) (meta *ObjectMetadata, err error) {
|
||||
// Open our meradata object
|
||||
rc, err := mo.Open(ctx)
|
||||
if err != nil {
|
||||
return nil
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
err := rc.Close()
|
||||
if err != nil {
|
||||
fs.Errorf(mo, "Error closing object: %v", err)
|
||||
}
|
||||
}()
|
||||
defer fs.CheckClose(rc, &err)
|
||||
jr := json.NewDecoder(rc)
|
||||
meta = new(ObjectMetadata)
|
||||
if err = jr.Decode(meta); err != nil {
|
||||
return nil
|
||||
return nil, err
|
||||
}
|
||||
return meta
|
||||
return meta, nil
|
||||
}
|
||||
|
||||
// Remove removes this object
|
||||
@@ -1102,6 +1101,9 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
|
||||
origName := o.Remote()
|
||||
if o.meta.Mode != Uncompressed || compressible {
|
||||
newObject, err = o.f.putWithCustomFunctions(ctx, in, o.f.wrapInfo(src, origName, src.Size()), options, o.f.Fs.Put, updateMeta, compressible, mimeType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if newObject.Object.Remote() != o.Object.Remote() {
|
||||
if removeErr := o.Object.Remove(ctx); removeErr != nil {
|
||||
return removeErr
|
||||
@@ -1115,9 +1117,9 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
|
||||
}
|
||||
// If we are, just update the object and metadata
|
||||
newObject, err = o.f.putWithCustomFunctions(ctx, in, src, options, update, updateMeta, compressible, mimeType)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Update object metadata and return
|
||||
o.Object = newObject.Object
|
||||
@@ -1128,6 +1130,9 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
|
||||
|
||||
// This will initialize the variables of a new press Object. The metadata object, mo, and metadata struct, meta, must be specified.
|
||||
func (f *Fs) newObject(o fs.Object, mo fs.Object, meta *ObjectMetadata) *Object {
|
||||
if o == nil {
|
||||
log.Trace(nil, "newObject(%#v, %#v, %#v) called with nil o", o, mo, meta)
|
||||
}
|
||||
return &Object{
|
||||
Object: o,
|
||||
f: f,
|
||||
@@ -1140,6 +1145,9 @@ func (f *Fs) newObject(o fs.Object, mo fs.Object, meta *ObjectMetadata) *Object
|
||||
|
||||
// This initializes the variables of a press Object with only the size. The metadata will be loaded later on demand.
|
||||
func (f *Fs) newObjectSizeAndNameOnly(o fs.Object, moName string, size int64) *Object {
|
||||
if o == nil {
|
||||
log.Trace(nil, "newObjectSizeAndNameOnly(%#v, %#v, %#v) called with nil o", o, moName, size)
|
||||
}
|
||||
return &Object{
|
||||
Object: o,
|
||||
f: f,
|
||||
@@ -1167,7 +1175,7 @@ func (o *Object) loadMetadataIfNotLoaded(ctx context.Context) (err error) {
|
||||
return err
|
||||
}
|
||||
if o.meta == nil {
|
||||
o.meta = readMetadata(ctx, o.mo)
|
||||
o.meta, err = readMetadata(ctx, o.mo)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -34,9 +34,9 @@ func deriveFs(ctx context.Context, t *testing.T, f fs.Fs, opts settings) fs.Fs {
|
||||
// test that big file uploads do not cause network i/o timeout
|
||||
func (f *Fs) testUploadTimeout(t *testing.T) {
|
||||
const (
|
||||
fileSize = 100000000 // 100 MiB
|
||||
idleTimeout = 40 * time.Millisecond // small because test server is local
|
||||
maxTime = 10 * time.Second // prevent test hangup
|
||||
fileSize = 100000000 // 100 MiB
|
||||
idleTimeout = 1 * time.Second // small because test server is local
|
||||
maxTime = 10 * time.Second // prevent test hangup
|
||||
)
|
||||
|
||||
if testing.Short() {
|
||||
|
||||
@@ -891,6 +891,12 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e
|
||||
}).Fill(ctx, f)
|
||||
f.srv.SetErrorHandler(errorHandler)
|
||||
|
||||
// Disable change polling in China region
|
||||
// See: https://github.com/rclone/rclone/issues/6444
|
||||
if f.opt.Region == regionCN {
|
||||
f.features.ChangeNotify = nil
|
||||
}
|
||||
|
||||
// Renew the token in the background
|
||||
f.tokenRenewer = oauthutil.NewRenew(f.String(), ts, func() error {
|
||||
_, _, err := f.readMetaDataForPath(ctx, "")
|
||||
|
||||
@@ -1566,8 +1566,21 @@ isn't set then "acl" is used instead.`,
|
||||
Help: "arn:aws:kms:*",
|
||||
}},
|
||||
}, {
|
||||
Name: "sse_customer_key",
|
||||
Help: "If using SSE-C you must provide the secret encryption key used to encrypt/decrypt your data.",
|
||||
Name: "sse_customer_key",
|
||||
Help: `To use SSE-C you may provide the secret encryption key used to encrypt/decrypt your data.
|
||||
|
||||
Alternatively you can provide --sse-customer-key-base64.`,
|
||||
Provider: "AWS,Ceph,ChinaMobile,Minio",
|
||||
Advanced: true,
|
||||
Examples: []fs.OptionExample{{
|
||||
Value: "",
|
||||
Help: "None",
|
||||
}},
|
||||
}, {
|
||||
Name: "sse_customer_key_base64",
|
||||
Help: `If using SSE-C you must provide the secret encryption key encoded in base64 format to encrypt/decrypt your data.
|
||||
|
||||
Alternatively you can provide --sse-customer-key.`,
|
||||
Provider: "AWS,Ceph,ChinaMobile,Minio",
|
||||
Advanced: true,
|
||||
Examples: []fs.OptionExample{{
|
||||
@@ -2142,6 +2155,7 @@ type Options struct {
|
||||
SSEKMSKeyID string `config:"sse_kms_key_id"`
|
||||
SSECustomerAlgorithm string `config:"sse_customer_algorithm"`
|
||||
SSECustomerKey string `config:"sse_customer_key"`
|
||||
SSECustomerKeyBase64 string `config:"sse_customer_key_base64"`
|
||||
SSECustomerKeyMD5 string `config:"sse_customer_key_md5"`
|
||||
StorageClass string `config:"storage_class"`
|
||||
UploadCutoff fs.SizeSuffix `config:"upload_cutoff"`
|
||||
@@ -2679,6 +2693,16 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e
|
||||
if opt.BucketACL == "" {
|
||||
opt.BucketACL = opt.ACL
|
||||
}
|
||||
if opt.SSECustomerKeyBase64 != "" && opt.SSECustomerKey != "" {
|
||||
return nil, errors.New("s3: can't use sse_customer_key and sse_customer_key_base64 at the same time")
|
||||
} else if opt.SSECustomerKeyBase64 != "" {
|
||||
// Decode the base64-encoded key and store it in the SSECustomerKey field
|
||||
decoded, err := base64.StdEncoding.DecodeString(opt.SSECustomerKeyBase64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("s3: Could not decode sse_customer_key_base64: %w", err)
|
||||
}
|
||||
opt.SSECustomerKey = string(decoded)
|
||||
}
|
||||
if opt.SSECustomerKey != "" && opt.SSECustomerKeyMD5 == "" {
|
||||
// calculate CustomerKeyMD5 if not supplied
|
||||
md5sumBinary := md5.Sum([]byte(opt.SSECustomerKey))
|
||||
|
||||
@@ -62,7 +62,7 @@ func startProgress() func() {
|
||||
printProgress("")
|
||||
fs.LogPrint = oldLogPrint
|
||||
operations.SyncPrintf = oldSyncPrint
|
||||
fmt.Println("")
|
||||
fmt.Fprintln(terminal.Out, "")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -637,3 +637,7 @@ put them back in again.` >}}
|
||||
* Ryan Morey <4590343+rmorey@users.noreply.github.com>
|
||||
* Simon Bos <simonbos9@gmail.com>
|
||||
* YFdyh000 <yfdyh000@gmail.com> * Josh Soref <2119212+jsoref@users.noreply.github.com>
|
||||
* Øyvind Heddeland Instefjord <instefjord@outlook.com>
|
||||
* Dmitry Deniskin <110819396+ddeniskin@users.noreply.github.com>
|
||||
* Alexander Knorr <106825+opexxx@users.noreply.github.com>
|
||||
* Richard Bateman <richard@batemansr.us>
|
||||
|
||||
@@ -341,8 +341,6 @@ mount sftp1:subdir /mnt/data -t rclone -o vfs_cache_mode=writes,sftp_key_file=/p
|
||||
or create systemd mount units:
|
||||
```
|
||||
# /etc/systemd/system/mnt-data.mount
|
||||
[Unit]
|
||||
After=network-online.target
|
||||
[Mount]
|
||||
Type=rclone
|
||||
What=sftp1:subdir
|
||||
@@ -354,7 +352,6 @@ optionally accompanied by systemd automount unit
|
||||
```
|
||||
# /etc/systemd/system/mnt-data.automount
|
||||
[Unit]
|
||||
After=network-online.target
|
||||
Before=remote-fs.target
|
||||
[Automount]
|
||||
Where=/mnt/data
|
||||
|
||||
@@ -14,7 +14,7 @@ Rclone is a Go program and comes as a single binary file.
|
||||
* Run `rclone config` to setup. See [rclone config docs](/docs/) for more details.
|
||||
* Optionally configure [automatic execution](#autostart).
|
||||
|
||||
See below for some expanded Linux / macOS instructions.
|
||||
See below for some expanded Linux / macOS / Windows instructions.
|
||||
|
||||
See the [usage](/docs/) docs for how to use rclone, or
|
||||
run `rclone -h`.
|
||||
@@ -35,7 +35,9 @@ For beta installation, run:
|
||||
Note that this script checks the version of rclone installed first and
|
||||
won't re-download if not needed.
|
||||
|
||||
## Linux installation from precompiled binary
|
||||
## Linux installation {#linux}
|
||||
|
||||
### Precompiled binary {#linux-precompiled}
|
||||
|
||||
Fetch and unpack
|
||||
|
||||
@@ -59,7 +61,9 @@ Run `rclone config` to setup. See [rclone config docs](/docs/) for more details.
|
||||
|
||||
rclone config
|
||||
|
||||
## macOS installation with brew
|
||||
## macOS installation {#macos}
|
||||
|
||||
### Installation with brew {#macos-brew}
|
||||
|
||||
brew install rclone
|
||||
|
||||
@@ -68,7 +72,12 @@ NOTE: This version of rclone will not support `mount` any more (see
|
||||
on macOS, either install a precompiled binary or enable the relevant option
|
||||
when [installing from source](#install-from-source).
|
||||
|
||||
## macOS installation from precompiled binary, using curl
|
||||
Note that this is a third party installer not controlled by the rclone
|
||||
developers so it may be out of date. Its current version is as below.
|
||||
|
||||
[](https://repology.org/project/rclone/versions)
|
||||
|
||||
### Precompiled binary, using curl {#macos-precompiled}
|
||||
|
||||
To avoid problems with macOS gatekeeper enforcing the binary to be signed and
|
||||
notarized it is enough to download with `curl`.
|
||||
@@ -96,7 +105,7 @@ Run `rclone config` to setup. See [rclone config docs](/docs/) for more details.
|
||||
|
||||
rclone config
|
||||
|
||||
## macOS installation from precompiled binary, using a web browser
|
||||
### Precompiled binary, using a web browser {#macos-precompiled-web}
|
||||
|
||||
When downloading a binary with a web browser, the browser will set the macOS
|
||||
gatekeeper quarantine attribute. Starting from Catalina, when attempting to run
|
||||
@@ -109,11 +118,73 @@ The simplest fix is to run
|
||||
|
||||
xattr -d com.apple.quarantine rclone
|
||||
|
||||
## Install with docker
|
||||
## Windows installation {#windows}
|
||||
|
||||
The rclone maintains a [docker image for rclone](https://hub.docker.com/r/rclone/rclone).
|
||||
These images are autobuilt by docker hub from the rclone source based
|
||||
on a minimal Alpine linux image.
|
||||
### Precompiled binary {#windows-precompiled}
|
||||
|
||||
Fetch the correct binary for your processor type by clicking on these
|
||||
links. If not sure, use the first link.
|
||||
|
||||
- [Intel/AMD - 64 Bit](https://downloads.rclone.org/rclone-current-linux-amd64.zip)
|
||||
- [Intel/AMD - 32 Bit](https://downloads.rclone.org/rclone-current-linux-386.zip)
|
||||
- [ARM - 64 Bit](https://downloads.rclone.org/rclone-current-linux-arm64.zip)
|
||||
|
||||
Open this file in the Explorer and extract `rclone.exe`. Rclone is a
|
||||
portable executable so you can place it wherever is convenient.
|
||||
|
||||
Open a CMD window (or powershell) and run the binary. Note that rclone
|
||||
does not launch a GUI by default, it runs in the CMD Window.
|
||||
|
||||
- Run `rclone.exe config` to setup. See [rclone config docs](/docs/) for more details.
|
||||
- Optionally configure [automatic execution](#autostart).
|
||||
|
||||
If you are planning to use the [rclone mount](/commands/rclone_mount/)
|
||||
feature then you will need to install the third party utility
|
||||
[WinFsp](https://winfsp.dev/) also.
|
||||
|
||||
### Chocolatey package manager {#windows-chocolatey}
|
||||
|
||||
Make sure you have [Choco](https://chocolatey.org/) installed
|
||||
|
||||
```
|
||||
choco search rclone
|
||||
choco install rclone
|
||||
```
|
||||
|
||||
This will install rclone on your Windows machine. If you are planning
|
||||
to use [rclone mount](/commands/rclone_mount/) then
|
||||
|
||||
```
|
||||
choco install winfsp
|
||||
```
|
||||
|
||||
will install that too.
|
||||
|
||||
Note that this is a third party installer not controlled by the rclone
|
||||
developers so it may be out of date. Its current version is as below.
|
||||
|
||||
[](https://repology.org/project/rclone/versions)
|
||||
|
||||
## Package manager installation {#package-manager}
|
||||
|
||||
Many Linux, Windows, macOS and other OS distributions package and
|
||||
distribute rclone.
|
||||
|
||||
The distributed versions of rclone are often quite out of date and for
|
||||
this reason we recommend one of the other installation methods if
|
||||
possible.
|
||||
|
||||
You can get an idea of how up to date or not your OS distribution's
|
||||
package is here.
|
||||
|
||||
[](https://repology.org/project/rclone/versions)
|
||||
|
||||
## Docker installation {#docker}
|
||||
|
||||
The rclone developers maintain a [docker image for rclone](https://hub.docker.com/r/rclone/rclone).
|
||||
|
||||
These images are built as part of the release process based on a
|
||||
minimal Alpine Linux.
|
||||
|
||||
The `:latest` tag will always point to the latest stable release. You
|
||||
can use the `:beta` tag to get the latest build from master. You can
|
||||
@@ -188,16 +259,7 @@ ls ~/data/mount
|
||||
kill %1
|
||||
```
|
||||
|
||||
## Install on Windows via Chocolateley Packet Manager
|
||||
|
||||
Make sure you have [Choco](https://chocolatey.org/) installed
|
||||
```
|
||||
choco search rclone
|
||||
choco install rclone
|
||||
```
|
||||
This will install rclone on your windows machine
|
||||
|
||||
## Install from source
|
||||
## Source installation {#source}
|
||||
|
||||
Make sure you have git and [Go](https://golang.org/) installed.
|
||||
Go version 1.17 or newer is required, latest release is recommended.
|
||||
@@ -216,7 +278,7 @@ in the same folder. As an initial check you can now run `./rclone version`
|
||||
(`.\rclone version` on Windows).
|
||||
|
||||
Note that on macOS and Windows the [mount](https://rclone.org/commands/rclone_mount/)
|
||||
command will not be available unless you specify additional build tag `cmount`.
|
||||
command will not be available unless you specify an additional build tag `cmount`.
|
||||
|
||||
```
|
||||
go build -tags cmount
|
||||
@@ -235,7 +297,7 @@ distribution (make sure you install it in the classic mingw64 subsystem, the
|
||||
ucrt64 version is not compatible).
|
||||
|
||||
Additionally, on Windows, you must install the third party utility
|
||||
[WinFsp](http://www.secfs.net/winfsp/), with the "Developer" feature selected.
|
||||
[WinFsp](https://winfsp.dev/), with the "Developer" feature selected.
|
||||
If building with cgo, you must also set environment variable CPATH pointing to
|
||||
the fuse include directory within the WinFsp installation
|
||||
(normally `C:\Program Files (x86)\WinFsp\inc\fuse`).
|
||||
@@ -250,9 +312,10 @@ go build -trimpath -ldflags -s -tags cmount
|
||||
```
|
||||
|
||||
Instead of executing the `go build` command directly, you can run it via the
|
||||
Makefile, which also sets version information and copies the resulting rclone
|
||||
executable into your GOPATH bin folder (`$(go env GOPATH)/bin`, which
|
||||
corresponds to `~/go/bin/rclone` by default).
|
||||
Makefile. It changes the version number suffix from "-DEV" to "-beta" and
|
||||
appends commit details. It also copies the resulting rclone executable into
|
||||
your GOPATH bin folder (`$(go env GOPATH)/bin`, which corresponds to
|
||||
`~/go/bin/rclone` by default).
|
||||
|
||||
```
|
||||
make
|
||||
@@ -264,7 +327,15 @@ To include mount command on macOS and Windows with Makefile build:
|
||||
make GOTAGS=cmount
|
||||
```
|
||||
|
||||
As an alternative you can download the source, build and install rclone in one
|
||||
There are other make targets that can be used for more advanced builds,
|
||||
such as cross-compiling for all supported os/architectures, embedding
|
||||
icon and version info resources into windows executable, and packaging
|
||||
results into release artifacts.
|
||||
See [Makefile](https://github.com/rclone/rclone/blob/master/Makefile)
|
||||
and [cross-compile.go](https://github.com/rclone/rclone/blob/master/bin/cross-compile.go)
|
||||
for details.
|
||||
|
||||
Another alternative is to download the source, build and install rclone in one
|
||||
operation, as a regular Go package. The source will be stored it in the Go
|
||||
module cache, and the resulting executable will be in your GOPATH bin folder
|
||||
(`$(go env GOPATH)/bin`, which corresponds to `~/go/bin/rclone` by default).
|
||||
@@ -283,7 +354,7 @@ with the current version):
|
||||
go get github.com/rclone/rclone
|
||||
```
|
||||
|
||||
## Installation with Ansible
|
||||
## Ansible installation {#ansible}
|
||||
|
||||
This can be done with [Stefan Weichinger's ansible
|
||||
role](https://github.com/stefangweichinger/ansible-rclone).
|
||||
@@ -299,7 +370,7 @@ Instructions
|
||||
- rclone
|
||||
```
|
||||
|
||||
## Portable installation
|
||||
## Portable installation {#portable}
|
||||
|
||||
As mentioned [above](https://rclone.org/install/#quickstart), rclone is single
|
||||
executable (`rclone`, or `rclone.exe` on Windows) that you can download as a
|
||||
|
||||
@@ -26,9 +26,6 @@ var (
|
||||
// When nil, no encryption will be used for saving.
|
||||
configKey []byte
|
||||
|
||||
// PasswordPromptOutput is output of prompt for password
|
||||
PasswordPromptOutput = os.Stderr
|
||||
|
||||
// PassConfigKeyForDaemonization if set to true, the configKey
|
||||
// is obscured with obscure.Obscure and saved to a temp file
|
||||
// when it is calculated from the password. The path of that
|
||||
|
||||
@@ -716,9 +716,9 @@ func checkPassword(password string) (string, error) {
|
||||
|
||||
// GetPassword asks the user for a password with the prompt given.
|
||||
func GetPassword(prompt string) string {
|
||||
_, _ = fmt.Fprintln(PasswordPromptOutput, prompt)
|
||||
_, _ = fmt.Fprintln(terminal.Out, prompt)
|
||||
for {
|
||||
_, _ = fmt.Fprint(PasswordPromptOutput, "password:")
|
||||
_, _ = fmt.Fprint(terminal.Out, "password:")
|
||||
password := ReadPassword()
|
||||
password, err := checkPassword(password)
|
||||
if err == nil {
|
||||
|
||||
@@ -9,17 +9,17 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/rclone/rclone/fs/config"
|
||||
"github.com/rclone/rclone/lib/terminal"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// redirectStderr to the file passed in
|
||||
func redirectStderr(f *os.File) {
|
||||
passPromptFd, err := unix.Dup(int(os.Stderr.Fd()))
|
||||
termFd, err := unix.Dup(int(os.Stderr.Fd()))
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to duplicate stderr: %v", err)
|
||||
}
|
||||
config.PasswordPromptOutput = os.NewFile(uintptr(passPromptFd), "passPrompt")
|
||||
terminal.RawOut = os.NewFile(uintptr(termFd), "termOut")
|
||||
err = unix.Dup2(int(f.Fd()), int(os.Stderr.Fd()))
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to redirect stderr to file: %v", err)
|
||||
|
||||
@@ -12,29 +12,43 @@ package log
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"github.com/rclone/rclone/lib/terminal"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
kernel32 = syscall.MustLoadDLL("kernel32.dll")
|
||||
procSetStdHandle = kernel32.MustFindProc("SetStdHandle")
|
||||
)
|
||||
|
||||
func setStdHandle(stdhandle int32, handle syscall.Handle) error {
|
||||
r0, _, e1 := syscall.Syscall(procSetStdHandle.Addr(), 2, uintptr(stdhandle), uintptr(handle), 0)
|
||||
if r0 == 0 {
|
||||
if e1 != 0 {
|
||||
return error(e1)
|
||||
}
|
||||
return syscall.EINVAL
|
||||
// dup oldfd creating a functional copy as newfd
|
||||
// conceptually the same as the unix `dup()` function
|
||||
func dup(oldfd uintptr) (newfd uintptr, err error) {
|
||||
var (
|
||||
newfdHandle windows.Handle
|
||||
processHandle = windows.CurrentProcess()
|
||||
)
|
||||
err = windows.DuplicateHandle(
|
||||
processHandle, // hSourceProcessHandle
|
||||
windows.Handle(oldfd), // hSourceHandle
|
||||
processHandle, // hTargetProcessHandle
|
||||
&newfdHandle, // lpTargetHandle
|
||||
0, // dwDesiredAccess
|
||||
true, // bInheritHandle
|
||||
windows.DUPLICATE_SAME_ACCESS, // dwOptions
|
||||
)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return nil
|
||||
return uintptr(newfdHandle), nil
|
||||
}
|
||||
|
||||
// redirectStderr to the file passed in
|
||||
func redirectStderr(f *os.File) {
|
||||
err := setStdHandle(syscall.STD_ERROR_HANDLE, syscall.Handle(f.Fd()))
|
||||
termFd, err := dup(os.Stderr.Fd())
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to duplicate stderr: %v", err)
|
||||
}
|
||||
terminal.RawOut = os.NewFile(termFd, "termOut")
|
||||
err = windows.SetStdHandle(windows.STD_ERROR_HANDLE, windows.Handle(f.Fd()))
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to redirect stderr to file: %v", err)
|
||||
}
|
||||
os.Stderr = f
|
||||
}
|
||||
|
||||
2
go.mod
2
go.mod
@@ -2,8 +2,6 @@ module github.com/rclone/rclone
|
||||
|
||||
go 1.17
|
||||
|
||||
replace github.com/jlaffaye/ftp v0.0.0-20220904184306-99be0634ab9a => github.com/ncw/ftp v0.0.0-20220916150959-909597844e2a
|
||||
|
||||
require (
|
||||
bazil.org/fuse v0.0.0-20200524192727-fb710f7dfd05
|
||||
github.com/Azure/azure-pipeline-go v0.2.3
|
||||
|
||||
6
go.sum
6
go.sum
@@ -378,6 +378,10 @@ github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZ
|
||||
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
|
||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||
github.com/jlaffaye/ftp v0.0.0-20190624084859-c1312a7102bf/go.mod h1:lli8NYPQOFy3O++YmYbqVgOcQ1JPCwdOy+5zSjKJ9qY=
|
||||
github.com/jlaffaye/ftp v0.0.0-20220630165035-11536801d1ff h1:tN6UCYCBFNrPwvKf4RP9cIhGo6GcZ/IQTN8nqD7eCok=
|
||||
github.com/jlaffaye/ftp v0.0.0-20220630165035-11536801d1ff/go.mod h1:hhq4G4crv+nW2qXtNYcuzLeOudG92Ps37HEKeg2e3lE=
|
||||
github.com/jlaffaye/ftp v0.0.0-20220904184306-99be0634ab9a h1:s4ryRQyC5HKZh6qkjNAFcvmD7gImK5bZuj/YZkXy1vw=
|
||||
github.com/jlaffaye/ftp v0.0.0-20220904184306-99be0634ab9a/go.mod h1:hhq4G4crv+nW2qXtNYcuzLeOudG92Ps37HEKeg2e3lE=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
@@ -456,8 +460,6 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
|
||||
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/ncw/ftp v0.0.0-20220916150959-909597844e2a h1:BglfrImWBkK0SyMLSsKmAgutB0rzkoLzt5j5Sjn+heg=
|
||||
github.com/ncw/ftp v0.0.0-20220916150959-909597844e2a/go.mod h1:hhq4G4crv+nW2qXtNYcuzLeOudG92Ps37HEKeg2e3lE=
|
||||
github.com/ncw/go-acd v0.0.0-20201019170801-fe55f33415b1 h1:nAjWYc03awJAjsozNehdGZsm5LP7AhLOvjgbS8zN1tk=
|
||||
github.com/ncw/go-acd v0.0.0-20201019170801-fe55f33415b1/go.mod h1:MLIrzg7gp/kzVBxRE1olT7CWYMCklcUWU+ekoxOD9x0=
|
||||
github.com/ncw/swift/v2 v2.0.1 h1:q1IN8hNViXEv8Zvg3Xdis4a3c4IlIGezkYz09zQL5J0=
|
||||
|
||||
@@ -68,17 +68,23 @@ const (
|
||||
var (
|
||||
// make sure that start is only called once
|
||||
once sync.Once
|
||||
|
||||
// RawOut is the underlying *os.File intended for terminal output
|
||||
RawOut = os.Stderr
|
||||
)
|
||||
|
||||
// Start the terminal - must be called before use
|
||||
func Start() {
|
||||
once.Do(func() {
|
||||
f := os.Stdout
|
||||
f := RawOut
|
||||
if !IsTerminal(int(f.Fd())) {
|
||||
// If stdout not a tty then remove escape codes
|
||||
// If output is not a tty then remove escape codes
|
||||
Out = colorable.NewNonColorable(f)
|
||||
} else if runtime.GOOS == "windows" && os.Getenv("TERM") != "" {
|
||||
// If TERM is set just use stdout
|
||||
// If TERM is set on Windows then we should just send output
|
||||
// straight to the terminal for cygwin/git bash environments.
|
||||
// We don't want to use NewColorable here because it will
|
||||
// use Windows console calls which cygwin/git bash don't support.
|
||||
Out = f
|
||||
} else {
|
||||
Out = colorable.NewColorable(f)
|
||||
|
||||
@@ -34,5 +34,5 @@ func ReadPassword(fd int) ([]byte, error) {
|
||||
|
||||
// WriteTerminalTitle writes a string to the terminal title
|
||||
func WriteTerminalTitle(title string) {
|
||||
fmt.Printf(ChangeTitle + title + BEL)
|
||||
fmt.Fprintf(Out, ChangeTitle+title+BEL)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user