mirror of
https://github.com/rclone/rclone.git
synced 2026-01-21 20:03: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
|
||||
}
|
||||
|
||||
@@ -336,14 +336,44 @@ func (f *Fs) ftpConnection(ctx context.Context) (c *ftp.ServerConn, err error) {
|
||||
fs.Debugf(f, "Connecting to FTP server")
|
||||
|
||||
// Make ftp library dial with fshttp dialer optionally using TLS
|
||||
initialConnection := true
|
||||
dial := func(network, address string) (conn net.Conn, err error) {
|
||||
fs.Debugf(f, "dial(%q,%q)", network, address)
|
||||
defer func() {
|
||||
fs.Debugf(f, "> dial: conn=%T, err=%v", conn, err)
|
||||
}()
|
||||
conn, err = fshttp.NewDialer(ctx).Dial(network, address)
|
||||
if f.tlsConf != nil && err == nil {
|
||||
conn = tls.Client(conn, f.tlsConf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
// Connect using cleartext only for non TLS
|
||||
if f.tlsConf == nil {
|
||||
return conn, nil
|
||||
}
|
||||
// Initial connection only needs to be cleartext for explicit TLS
|
||||
if f.opt.ExplicitTLS && initialConnection {
|
||||
initialConnection = false
|
||||
return conn, nil
|
||||
}
|
||||
// Upgrade connection to TLS
|
||||
tlsConn := tls.Client(conn, f.tlsConf)
|
||||
// Do the initial handshake - tls.Client doesn't do it for us
|
||||
// If we do this then connections to proftpd/pureftpd lock up
|
||||
// See: https://github.com/rclone/rclone/issues/6426
|
||||
// See: https://github.com/jlaffaye/ftp/issues/282
|
||||
if false {
|
||||
err = tlsConn.HandshakeContext(ctx)
|
||||
if err != nil {
|
||||
_ = conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return tlsConn, nil
|
||||
}
|
||||
ftpConfig := []ftp.DialOption{
|
||||
ftp.DialWithContext(ctx),
|
||||
ftp.DialWithDialFunc(dial),
|
||||
}
|
||||
ftpConfig := []ftp.DialOption{ftp.DialWithDialFunc(dial)}
|
||||
|
||||
if f.opt.TLS {
|
||||
// Our dialer takes care of TLS but ftp library also needs tlsConf
|
||||
@@ -351,12 +381,6 @@ func (f *Fs) ftpConnection(ctx context.Context) (c *ftp.ServerConn, err error) {
|
||||
ftpConfig = append(ftpConfig, ftp.DialWithTLS(f.tlsConf))
|
||||
} else if f.opt.ExplicitTLS {
|
||||
ftpConfig = append(ftpConfig, ftp.DialWithExplicitTLS(f.tlsConf))
|
||||
// Initial connection needs to be cleartext for explicit TLS
|
||||
conn, err := fshttp.NewDialer(ctx).Dial("tcp", f.dialAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ftpConfig = append(ftpConfig, ftp.DialWithNetConn(conn))
|
||||
}
|
||||
if f.opt.DisableEPSV {
|
||||
ftpConfig = append(ftpConfig, ftp.DialWithDisabledEPSV(true))
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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