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