iclouddrive: support folders shared by another Apple ID (read + write) - fixes #9477#9487
Open
shadow-enthusiast wants to merge 4 commits into
Open
iclouddrive: support folders shared by another Apple ID (read + write) - fixes #9477#9487shadow-enthusiast wants to merge 4 commits into
shadow-enthusiast wants to merge 4 commits into
Conversation
…adable
Operations one level below the root of a folder shared by another Apple
ID returned HTTP 400. Contrary to the "wrong zone" theory, the CloudKit
zone string is com.apple.CloudDocs for every item; the real cause is that
items inside a shared folder have a FOLDER_IN_SHARED_FOLDER drivewsid and
physically live in the OWNER's zone, which the drivews endpoints (which
only operate in the caller's own zone) cannot address.
Fix the read/navigation path by routing shared subfolders through the
docws item-id endpoints, which resolve shared zones cross-zone:
- api.IsSharedFolderChildID: detect FOLDER_IN_SHARED_FOLDER /
FILE_IN_SHARED_FOLDER (and the empty-drivewsid case that deeper shared
descendants arrive as).
- Thread item_id through the dir cache (drivewsid#etag#itemID) only for
shared items; own-zone items keep drivewsid#etag unchanged.
- listAll: enumerate shared folders via GET /v1/enumerate/{item_id}
instead of the drivews retrieveItemDetailsInFolders call that 400s.
- readMetaData: resolve via dirCache.FindPath so the item_id survives
(Fs.FindPath strips it), fixing cat/NewObject inside shared subfolders.
- Object.Open: download shared files via the enumerate-provided
url_download, since they have no usable drivewsid.
Listing and downloading one or more levels below the share root now work
where they previously returned 400; normal own-drive behaviour is
unchanged. Writing into a shared subfolder is addressed in a following
commit.
rclone#9477
Writing one level inside a folder shared by another Apple ID failed: the drivews/docws upload+move endpoints operate only in the caller's own zone and return HTTP 400 for any FOLDER_IN_SHARED_FOLDER parent, and creating the CloudKit records directly is refused with a PCS chaining error because a new record has no Protected Cloud Storage key. Avoid client-side PCS crypto entirely by letting the server do it: upload the file into the share root the normal way (drivews performs PCS chaining there, in the owner's zone), then re-parent the resulting documentStructure into the target sub-folder via the ckdatabasews CloudKit shared database. The server re-chains PCS itself because the moved record already carries its own key. Adds api/clouddocs.go, a small ckdatabasews client (zone discovery, records/lookup, records/modify re-parent + delete, changes/zone scan), and routes Object.Update through it when the destination is a shared sub-folder. Own-zone and share-root writes and the existing read path are unchanged. Fixes rclone#9477
Add offline, table-driven unit tests covering the deterministic helpers introduced for reading from and writing into folders shared by another Apple ID: - api.IsSharedFolderChildID classification (empty / FOLDER_IN_SHARED_FOLDER / FILE_IN_SHARED_FOLDER vs own-zone and share-root IDs) - Construct/Deconstruct/GetDocIDFromDriveID round-trip and short-ID fallback - DriveItemRaw.SplitName and DriveItemRaw.IntoDriveItem conversion - Document.DriveID zone defaulting - the dir-cache ID helpers parseSharedItemID, folderID, IDJoin and parseNormalizedID, including the drivewsid#etag#itemID round-trip All tests run without network access. rclone#9477
ncw
requested changes
Jun 2, 2026
ncw
left a comment
Member
There was a problem hiding this comment.
Thanks for your PR :-)
See inline notes and these more general ones:
Please make sure all API calls are wrapped in the pacer for rate-limit, retries etc.
api/clouddocs.go builds requests as nested map[string]any literals throughout, whereas the rest of the api package uses typed request/response structs. Typed structs would be more idiomatic and self-documenting and reusable.
Did you run the integration tests for the backend with your changes?
Thanks
- pacer-wrap all ckdatabasews (clouddocs) calls, with reauth on 401/421/423 - replace map[string]any request/response bodies with typed structs - refresh the short-lived shared download URL on open failure and retry - guard changes/zone pagination against an empty or unchanged syncToken - debug-log a missing existing file on shared overwrite so duplicates are diagnosable - log when multiple shared zones exist and none matches the expected name - cache the stable shareRootID per name on the Fs - use %q error formatting for the shared-folder-root lookup rclone#9487
Author
|
Thanks for the review @ncw — all addressed in 98ca683. General
Inline
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What is the purpose of this change?
Make a folder shared by another Apple ID (current account is a non-owner participant with edit access) usable through the
iclouddrivebackend. Previously any operation one level below the share root returned HTTP 400, so shared folders were unusable beyond their top level (#9477).Two commits:
Read / navigate — listing/downloading one or more levels below the share root returned HTTP 400. The CloudKit zone string is
com.apple.CloudDocsfor every item; the real cause is that items inside a shared folder have aFOLDER_IN_SHARED_FOLDERdrivewsid and live in the owner's zone, which thedrivewsendpoints (caller's own zone only) cannot address. Fixed by routing shared subfolders through thedocwsitem-id endpoints, which resolve the owner's zone cross-zone (enumerate by item_id, thread item_id through the dir cache, download via the enumerate-provided URL).Write — writing into a shared subfolder failed (HTTP 400 on the
drivewsmove; HTTP 412 PCS-chaining error on a direct create, because a new record has no Protected Cloud Storage key). Rather than reimplement Apple's client-side PCS key-wrapping, this lets the server do the crypto: upload the file into the share root the normal way (the server performs PCS chaining there, in the owner's zone), then re-parent the resulting record into the target subfolder via theckdatabasewsCloudKit shared database — the server re-chains PCS itself because the moved record already carries its own key. Adds a smallapi/clouddocs.goclient (zone discovery, records/lookup, records/modify re-parent + delete). This adds no client-side cryptography, so it is not sensitive to Apple rotating keys or changing theprotectionInfoformat.All shared-only code paths are gated on
FOLDER_IN_SHARED_FOLDER/FILE_IN_SHARED_FOLDER, so own-zone and share-root behaviour is unchanged.Verified live against a real shared folder:
lsf,cat, andcopyto(incl. overwrite) one level inside the share now work where stock rclone returns HTTP 400.Was the change discussed in an issue or in the forum before?
Yes — #9477.
Known limitations
TestICloudremote backed by a folder shared from another Apple ID — happy to add if you can advise on the fixture.Mkdirof new shared subfolders and nesting more than one level below the share root are not implemented here (out of scope).Checklist
iclouddrive: <summary>.