protondrive: tidy small review findings
Log lines no longer cut a name inside a UTF-8 sequence, the "superseded upload" error says the file's state rather than claiming a revision is active, a test-only block decrypter moves to the tests, a one-line alias of encryptPassphrase goes, the upload godoc is rewrapped and the fake's server struct is gofmt clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
30cfcac416
commit
d3aee2af19
7 files changed
+43
-32
No files matched your search
@@ -559,13 +559,6 @@ func s2kSpecifier(op *packet.OpaquePacket) ([]byte, error) {
|
||||
return rest[i:], nil
|
||||
}
|
||||
|
||||
// reencryptPassphrase returns the passphrase of keys encrypted to
|
||||
// newParentKR, with the same session key, and a new detached signature
|
||||
// by signerKR, for a move.
|
||||
func reencryptPassphrase(keys *nodeKeys, newParentKR, signerKR *crypto.KeyRing, now time.Time) (encPassphrase, passphraseSig string, err error) {
|
||||
return encryptPassphrase(keys, newParentKR, signerKR, now)
|
||||
}
|
||||
|
||||
// encryptName returns name encrypted to parentKR and signed by
|
||||
// signerKR, without AEAD, with the session key sk, or a new one if sk
|
||||
// is nil.
|
||||
@@ -795,20 +788,6 @@ func encryptBlockTo(enc io.Writer, plain io.Reader, sk *crypto.SessionKey, nodeK
|
||||
return encSignature, nil
|
||||
}
|
||||
|
||||
// decryptBlock returns the encrypted block enc (SEIPDv1 or v2)
|
||||
// decrypted with the content session key sk.
|
||||
func decryptBlock(enc []byte, sk *crypto.SessionKey) ([]byte, error) {
|
||||
r, err := blockDecrypter(bytes.NewReader(enc), sk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
plain, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decrypt block: %w", err)
|
||||
}
|
||||
return plain, nil
|
||||
}
|
||||
|
||||
// blockDecrypter returns a reader of the encrypted block enc (SEIPDv1
|
||||
// or v2) decrypted with the content session key sk.
|
||||
//
|
||||
|
||||
@@ -356,7 +356,7 @@ func TestReencryptPassphrase(t *testing.T) {
|
||||
parent, newParent, signer, newSigner := newTestKeyRing(t, now), newTestKeyRing(t, now), newTestKeyRing(t, now), newTestKeyRing(t, now)
|
||||
armoredKey, encPassphrase, _, keys := testNodeKeys(t, parent, signer, true, now)
|
||||
|
||||
enc, sig, err := reencryptPassphrase(keys, newParent, newSigner, now)
|
||||
enc, sig, err := encryptPassphrase(keys, newParent, newSigner, now)
|
||||
require.NoError(t, err)
|
||||
requireNonAEAD(t, enc)
|
||||
assert.NotEqual(t, encPassphrase, enc)
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/ProtonMail/gopenpgp/v3/constants"
|
||||
"github.com/ProtonMail/gopenpgp/v3/crypto"
|
||||
@@ -623,7 +624,11 @@ func truncateName(name string) string {
|
||||
if len(name) <= maxLen {
|
||||
return name
|
||||
}
|
||||
return name[:maxLen] + "..."
|
||||
cut := maxLen
|
||||
for cut > 0 && !utf8.RuneStart(name[cut]) {
|
||||
cut--
|
||||
}
|
||||
return name[:cut] + "..."
|
||||
}
|
||||
|
||||
// reportOnce calls log unless a problem of kind with node id has
|
||||
@@ -1267,7 +1272,8 @@ func (c *client) encryptForNewParent(ctx context.Context, op string, n *node, ne
|
||||
return nil, err
|
||||
}
|
||||
passphraseSig := ""
|
||||
out.passphrase, passphraseSig, err = reencryptPassphrase(n.keys, newParent.keys.keyRing, signKR, now)
|
||||
// The passphrase keeps its session key, as in the SDK.
|
||||
out.passphrase, passphraseSig, err = encryptPassphrase(n.keys, newParent.keys.keyRing, signKR, now)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/rclone/rclone/backend/protondrive/api"
|
||||
"github.com/rclone/rclone/backend/protondrive/protontest"
|
||||
@@ -1306,3 +1307,10 @@ func TestCopyReadBackLag(t *testing.T) {
|
||||
assert.Equal(t, "copy.txt", cp.Name)
|
||||
assert.Equal(t, file.sha1(), cp.sha1())
|
||||
}
|
||||
|
||||
func TestTruncateName(t *testing.T) {
|
||||
assert.Equal(t, "short", truncateName("short"))
|
||||
got := truncateName("a" + strings.Repeat("é", 40))
|
||||
assert.True(t, utf8.ValidString(got), "%q", got)
|
||||
assert.Equal(t, "a"+strings.Repeat("é", 31)+"...", got)
|
||||
}
|
||||
@@ -102,9 +102,6 @@ type Server struct {
|
||||
requests map[string]int // count per "METHOD pattern"
|
||||
faults []*fault
|
||||
clockSkew time.Duration
|
||||
// driveRequirements is sent as x-pm-drive-requirements headers if
|
||||
// not empty; see SetDriveRequirements.
|
||||
driveRequirements []string
|
||||
users map[string]*User // by username
|
||||
sessions map[string]*session // by UID
|
||||
srp map[string]*srpLogin // by SRPSession
|
||||
@@ -121,6 +118,10 @@ type Server struct {
|
||||
readLag int // reads of a node after a change which don't see it; see SetReadLag
|
||||
readLagView LagView // what lagging reads of a changed node see
|
||||
lagging map[string]*lagView // nodes whose reads lag, by ID
|
||||
|
||||
// driveRequirements is sent as x-pm-drive-requirements headers if
|
||||
// not empty; see SetDriveRequirements.
|
||||
driveRequirements []string
|
||||
}
|
||||
|
||||
// New starts a fake server which is closed when the test ends.
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package protondrive
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/ProtonMail/gopenpgp/v3/crypto"
|
||||
"github.com/rclone/rclone/backend/protondrive/protontest"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -31,3 +35,17 @@ func newTestClientFor(t testing.TB, srv *protontest.Server, u *protontest.User,
|
||||
t.Cleanup(c.close)
|
||||
return c
|
||||
}
|
||||
|
||||
// decryptBlock returns the encrypted block enc (SEIPDv1 or v2)
|
||||
// decrypted with the content session key sk.
|
||||
func decryptBlock(enc []byte, sk *crypto.SessionKey) ([]byte, error) {
|
||||
r, err := blockDecrypter(bytes.NewReader(enc), sk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
plain, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decrypt block: %w", err)
|
||||
}
|
||||
return plain, nil
|
||||
}
|
||||
@@ -103,10 +103,9 @@ type draft struct {
|
||||
// and a failed commit whose revision the file read back doesn't show is
|
||||
// errUploadOutcomeUnknown: the upload may have succeeded. A commit which
|
||||
// succeeded but whose file has another revision by the time it is read
|
||||
// back is errUploadSuperseded,
|
||||
// and one the server still doesn't show after a few reads is
|
||||
// errUploadUnconfirmed. The returned node is always the upload's own
|
||||
// revision.
|
||||
// back is errUploadSuperseded, and one the server still doesn't show
|
||||
// after a few reads is errUploadUnconfirmed. The returned node is always
|
||||
// the upload's own revision.
|
||||
func (c *client) upload(ctx context.Context, r uploadRequest) (*node, error) {
|
||||
what := fmt.Sprintf("upload %q", r.Name)
|
||||
if r.Existing != nil {
|
||||
@@ -443,7 +442,7 @@ func (c *client) uploadDraft(ctx context.Context, d *draft, r uploadRequest, sig
|
||||
case commitErr == nil && err != nil:
|
||||
return nil, true, fmt.Errorf("read the uploaded file: %w", err)
|
||||
case commitErr == nil:
|
||||
return nil, true, fmt.Errorf("%w (revision %s is active, not %s)", errUploadSuperseded, n.RevisionID, d.revisionID)
|
||||
return nil, true, fmt.Errorf("%w (the file is in state %d with revision %q active, not %s)", errUploadSuperseded, n.State, n.RevisionID, d.revisionID)
|
||||
case err != nil:
|
||||
// Any draft left is replaced by the next upload of the name.
|
||||
return nil, true, fmt.Errorf("commit: %w: %w (reading the file failed: %v)", errUploadOutcomeUnknown, commitErr, err)
|
||||
|
||||
Reference in new issue
Block a user