diff --git a/UPDATES.md b/UPDATES.md
index 77151ae..62bca5d 100644
--- a/UPDATES.md
+++ b/UPDATES.md
@@ -20,6 +20,10 @@ Hopefully, most issues can be found just by trying to compile rcbridge with `./g
If librclone gained new functionality that can replace current uses of internal APIs, then that new functionality should be used. librclone RPC-related tasks should be done purely on the Android side in [`RcloneRpc`](./app/src/main/java/com/chiller3/rsaf/rclone/RcloneRpc.kt), not in go.
+### Certificate reloading
+
+Check if there's any way hook into `fshttp.(*Transport).RoundTrip()`. The hook to update `tls.Config.RootCAs` is the only reason we need to fork rclone.
+
### `RbDocMkdir`
Check the `vfs.Dir.Mkdir()` implementation to see if it fails with EEXIST when the path already exists. If so, `RcloneProvider` can be updated to avoid an unnecessary stat when creating directories with Android semantics.
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index d47def3..64d0f43 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -57,7 +57,7 @@
Configure remote
Rerun the rclone configuration wizard.
Rename remote
- Change the name of the remote. If other remotes depends on this one, they will need to be manually updated with the new name.
+ Change the name of this remote. If other remotes depends on this one, they will need to be manually updated with the new name.
Duplicate remote
Create a copy of this remote with identical configuration.
Delete remote
@@ -93,7 +93,7 @@
Successfully edited remote %1$s
- Failed to delete %1$s: %2$s
+ Failed to delete remote %1$s: %2$s
Failed to rename remote %1$s to %2$s: %3$s
Failed to duplicate remote %1$s to %2$s: %3$s
Failed to set %1$s config option for remote %2$s: %3$s
@@ -127,7 +127,7 @@
Encryption password
Inactivity timeout
Enter a duration in seconds.
- There are file operations in progress. These operations will be interrupted and pending uploads in the VFS cache will be permanently deleted.
+ There are pending uploads in progress that may be interrupted. These will be permanently deleted from the VFS cache.
Add remote: %1$s
diff --git a/rcbridge/go.mod b/rcbridge/go.mod
index 2a69260..9958557 100644
--- a/rcbridge/go.mod
+++ b/rcbridge/go.mod
@@ -9,6 +9,8 @@ require (
golang.org/x/mobile v0.0.0-20250106192035-c31d5b91ecc3
)
+replace github.com/rclone/rclone v1.69.0 => github.com/chenxiaolong/rclone v1.69.0-rsaf
+
require (
cloud.google.com/go/auth v0.12.1 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect
diff --git a/rcbridge/go.sum b/rcbridge/go.sum
index b1fe111..e84ed3c 100644
--- a/rcbridge/go.sum
+++ b/rcbridge/go.sum
@@ -151,6 +151,8 @@ github.com/calebcase/tmpfile v1.0.3/go.mod h1:UAUc01aHeC+pudPagY/lWvt2qS9ZO5Zzof
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/chenxiaolong/rclone v1.69.0-rsaf h1:URDbtwBv0+aKN2k7MkLCl1wxyxxKVSNy+it1t8JqL7A=
+github.com/chenxiaolong/rclone v1.69.0-rsaf/go.mod h1:RfT8WA1rU1/wHyujQ1r0MZFdO89zLSDgCuu62uImArg=
github.com/chilts/sid v0.0.0-20190607042430-660e94789ec9 h1:z0uK8UQqjMVYzvk4tiiu3obv2B44+XBsvgEJREQfnO8=
github.com/chilts/sid v0.0.0-20190607042430-660e94789ec9/go.mod h1:Jl2neWsQaDanWORdqZ4emBl50J4/aRBBS4FyyG9/PFo=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
@@ -467,8 +469,6 @@ github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/quic-go/quic-go v0.40.1 h1:X3AGzUNFs0jVuO3esAGnTfvdgvL4fq655WaOi1snv1Q=
github.com/quic-go/quic-go v0.40.1/go.mod h1:PeN7kuVJ4xZbxSv/4OX6S1USOX8MJvydwpTx31vx60c=
-github.com/rclone/rclone v1.69.0 h1:fRfYu6Ha2Zn+nDLUa4ZuPXogddfeZG+jsCyhbOdAamw=
-github.com/rclone/rclone v1.69.0/go.mod h1:RfT8WA1rU1/wHyujQ1r0MZFdO89zLSDgCuu62uImArg=
github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4=
github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA=
github.com/relvacode/iso8601 v1.3.0 h1:HguUjsGpIMh/zsTczGN3DVJFxTU/GX+MMmzcKoMO7ko=
diff --git a/rcbridge/rcbridge.go b/rcbridge/rcbridge.go
index 99d71b0..0530be4 100644
--- a/rcbridge/rcbridge.go
+++ b/rcbridge/rcbridge.go
@@ -19,6 +19,7 @@ import (
// This package's init() MUST run first
_ "rcbridge/envhack"
+ "crypto/tls"
"crypto/x509"
"encoding/pem"
"fmt"
@@ -35,7 +36,6 @@ import (
"time"
_ "golang.org/x/mobile/event/key"
- "golang.org/x/sys/unix"
_ "github.com/rclone/rclone/backend/all"
"github.com/rclone/rclone/fs"
@@ -89,7 +89,7 @@ var (
config.ErrorConfigFileNotFound: syscall.ENOENT,
}
caCertsLock goSync.Mutex
- caCertsFile *os.File
+ caCertsPool *x509.CertPool
)
// Load as many certificates from the PEM data as possible and return the last
@@ -117,17 +117,17 @@ func parsePemCerts(data []byte) (certs []*x509.Certificate, err error) {
return certs, err
}
-// Generate a trust store in a single file that we can pass to rclone via its
-// CaCert config option. This is necessary because golang currently does not
-// support reading from the proper Android directories. We can't just set
-// SSL_CERT_DIR either, even with envhack, because the user CA directory
+// Generate a trust store pool that we can pass to rclone via the per-request
+// hook we add in our rclone fork. This is necessary because golang currently
+// does not support reading from the proper Android directories. We can't just
+// set SSL_CERT_DIR either, even with envhack, because the user CA directory
// contains DER-encoded certificates and golang only supports loading PEM.
//
// Additionally, our implementation will not trust any system CA certificates
// that the user explicitly disabled from Android's settings.
//
// https://github.com/golang/go/issues/71258
-func generateTrustStoreTempFile(tempDir string) *os.File {
+func generateTrustStorePool() *x509.CertPool {
systemDir := os.Getenv("ANDROID_ROOT")
dataDir := os.Getenv("ANDROID_DATA")
@@ -187,14 +187,7 @@ func generateTrustStoreTempFile(tempDir string) *os.File {
}
}
- fd, err := unix.Open(tempDir, unix.O_RDWR|unix.O_TMPFILE|unix.O_CLOEXEC, 0600)
- if err != nil {
- fs.Errorf(nil, "Failed to create temp file in: %v: %v", tempDir, err)
- return nil
- }
-
- path := fmt.Sprintf("/proc/self/fd/%d", fd)
- file := os.NewFile(uintptr(fd), path)
+ pool := x509.NewCertPool()
for _, path := range caFiles {
data, err := os.ReadFile(path)
@@ -213,22 +206,21 @@ func generateTrustStoreTempFile(tempDir string) *os.File {
}
for _, cert := range certs {
- block := &pem.Block{
- Type: "CERTIFICATE",
- Bytes: cert.Raw,
- }
-
- err = pem.Encode(file, block)
- if err != nil {
- fs.Logf(nil, "Failed to encode certificate: %v", cert)
- continue
- }
+ pool.AddCert(cert)
}
}
fs.Logf(nil, "Loaded %d certificates", len(caFiles))
- return file
+ return pool
+}
+
+// Set the trusted CA certificates on every HTTP request.
+func perRequestHook(config *tls.Config) {
+ caCertsLock.Lock()
+ defer caCertsLock.Unlock()
+
+ config.RootCAs = caCertsPool
}
// Initialize global aspects of the library.
@@ -238,34 +230,16 @@ func RbInit() {
// Don't allow interactive password prompts
ci := fs.GetConfig(context.Background())
ci.AskPassword = false
+
+ fshttp.SetRoundTripHook(perRequestHook)
}
-// Reload certificates from the system and user trust stores. This is thread
-// safe within RSAF, but may race with rclone's NewTransportCustom(). Note that
-// reloading is not that useful because the changes do not affect fshttp clients
-// that were previously created.
-func RbReloadCerts() bool {
+// Reload certificates from the system and user trust stores.
+func RbReloadCerts() {
caCertsLock.Lock()
defer caCertsLock.Unlock()
- ci := fs.GetConfig(context.Background())
- ci.CaCert = nil
-
- if caCertsFile != nil {
- caCertsFile.Close()
- caCertsFile = nil
- }
-
- caCertsFile = generateTrustStoreTempFile(config.GetCacheDir())
- if caCertsFile == nil {
- return false
- }
-
- ci.CaCert = append(ci.CaCert, caCertsFile.Name())
-
- fshttp.ResetTransport()
-
- return true
+ caCertsPool = generateTrustStorePool()
}
// Clean up library resources.
@@ -355,7 +329,10 @@ func RbCacheClearRemote(remote string) {
}
}
- cache.ClearConfig(remote)
+ parsed, err := fspath.Parse(remote)
+ if err == nil {
+ cache.ClearConfig(parsed.Name)
+ }
}
// Clear cached fs and vfs instances. All vfs instances will be shut down