Siyao Meng created HDDS-16762:
---------------------------------
Summary: FSO rename to the bucket root silently replaces or
shadows an existing root entry and orphans its subtree
Key: HDDS-16762
URL: https://issues.apache.org/jira/browse/HDDS-16762
Project: Apache Ozone
Issue Type: Bug
Reporter: Siyao Meng
Attachments:
MC-6-check-destination-name-on-rename-to-bucket-root.patch,
TestBugMC6RenameToBucketRoot.java
h3. Mechanism
With an empty {{toKeyName}}, {{getOMKeyInfoIfExists}} returns null, so
{{OMKeyRenameRequestWithFSO.validateAndUpdateCache}} takes the "destination
absent" branch. {{renameKey}} then substitutes the source leaf name and puts
{{/volId/bucketId/bucketId/<leaf>}} without looking that key up in
{{directoryTable}} or {{fileTable}}. The existing destination checks (cases 3
to 6) are skipped.
When that key is also the key of the source's own parent (source {{p/p}}), the
table cache applies parent then destination while the RocksDB batch in
{{OMKeyRenameResponseWithFSO.addToDBBatch}} applies destination then parent, so
cache and DB disagree.
h3. Trigger
One client renames an FSO entry to the bucket root while an entry with the same
leaf name exists there: {{FileSystem.rename}} to {{/vol/bucket}} through
{{ofs}} or to {{/}} through {{o3fs}} (both end up sending an empty destination
key), or {{OzoneBucket.renameKey(from, "")}}.
h3. Impact
The rename returns OK and:
* an existing root directory is replaced and its whole subtree becomes
unreachable, with its rows left in RocksDB even after the bucket is deleted;
* an existing root file is silently replaced;
* a file can be stored under the name of a root directory and is unreachable;
* renaming {{p/p}} to the root makes the renamed directory and its subtree
disappear once the flush lands.
The state survives an OM restart and nothing reclaims the orphan rows.
h3. Reproduction
PASS, deterministic, unmodified source. [^TestBugMC6RenameToBucketRoot.java]
({{hadoop-ozone/ozone-manager}}) drives the public bucket client against one
real OM. Only the cache read before flush in the {{p/p}} variant uses
{{OzoneManagerDoubleBuffer#pause}}. Files in the test are zero length, so block
leakage is argued from the leftover {{fileTable}} rows, not measured.
h3. Suggested fix
Treat an empty {{toKeyName}} as the bucket root directory: look up the source
leaf name directly under the bucket and fail with {{KEY_ALREADY_EXISTS}} when
it is taken, the same rule case 5 applies to every other destination directory.
Resolving the empty name to the leaf name before the existing destination
lookup would be wrong: an existing root directory of that name would then be
treated as "move the source under it".
h3. Patch
[^MC-6-check-destination-name-on-rename-to-bucket-root.patch], against
20e6a1c0f6039248373efd3eb1d7f2afcf7f1535. It also applies to master at
c99d291e863.
In the "destination absent" branch of
{{OMKeyRenameRequestWithFSO.validateAndUpdateCache}}, an empty {{toKeyName}}
now triggers a lookup of the source leaf name under the bucket (directory
table, then file table, both cache aware) and fails with {{KEY_ALREADY_EXISTS}}
if it is taken. A move into the bucket root with a free name is unchanged. A
source already at the bucket root keeps its current behaviour, because existing
tests rely on {{renameKey(rootKey, "")}} succeeding. With the check in place
the cache and batch order difference in {{addToDBBatch}} is no longer
reachable, so the patch does not reorder it.
Covered by five cases in the existing {{TestOMKeyRenameRequestWithFSO}}.
Without the change, {{testRenameToBucketRootWithExistingFile}},
{{testRenameToBucketRootWithExistingDirectory}} and
{{testRenameDirectoryToBucketRootOntoItsParent}} fail with "expected:
<KEY_ALREADY_EXISTS> but was: <OK>". The rename and double buffer unit suites
(48 tests) and the {{ofs}} / {{o3fs}} rename integration tests (23 tests,
including {{testKeyRenameToBucketLevel}}) pass, checkstyle is clean. The patch
prevents new damage only; rows already orphaned are not repaired.
Found by TLA+ model checking and code review of the OM FSO namespace mutation
paths under HDDS-15926, on commit 20e6a1c0f6039248373efd3eb1d7f2afcf7f1535.
Checked against HDDS issues and apache/ozone pull requests for duplicates
before filing. The attached patch is a proposal for review. Generated with
Specula (Claude Opus 5.5).
--
This message was sent by Atlassian Jira
(v8.20.10#820010)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]