From 898085a379271e6ac337b233a5c9c1a6394da4bd Mon Sep 17 00:00:00 2001 From: Tigran Mkrtchyan Date: Thu, 17 Sep 2026 22:18:06 +0200 Subject: [PATCH] nfs: remove transfer from the door list if pool selection failed Motivation: As pNFS layouts are independent of file open, they are invalidated only when layout return is issued. The door logic creates a transfer, adds transfer into transfers list and then performs namespace lookup, pool selection and starting a mover. In cases, when pool selection fails due to stage protection or an attempt to overwrite, the layout get will return "permission deny", thus layout return will never be issued and transfer will stay in the door forever. Modification: Update layoutGet error handling such that on permission deny error pool less transfers will be removed. An unexpected conditions are logged. Result: orphan transfers like "WriteTransfer null@N/A," are removed when it's safe. Acked-by: Marina Sahakyan Target: master, 12.0, 11.2 Require-book: no Require-notes: yes (cherry picked from commit ecdd82e074496e7bb78402a4fe21548dd1845b6e) Signed-off-by: Tigran Mkrtchyan --- .../chimera/nfsv41/door/NFSv41Door.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/modules/dcache-nfs/src/main/java/org/dcache/chimera/nfsv41/door/NFSv41Door.java b/modules/dcache-nfs/src/main/java/org/dcache/chimera/nfsv41/door/NFSv41Door.java index 605706f86dc..eb8697bbac6 100644 --- a/modules/dcache-nfs/src/main/java/org/dcache/chimera/nfsv41/door/NFSv41Door.java +++ b/modules/dcache-nfs/src/main/java/org/dcache/chimera/nfsv41/door/NFSv41Door.java @@ -814,6 +814,7 @@ public Layout layoutGet(CompoundContext context, LAYOUTGET4args args) final stateid4 stateid = Stateids.getCurrentStateidIfNeeded(context, args.loga_stateid); LayoutDriver layoutDriver = getLayoutDriver(layoutType); + NfsTransfer transfer = null; final NFS4Client client; if (context.getMinorversion() == 0) { @@ -847,7 +848,7 @@ public Layout layoutGet(CompoundContext context, LAYOUTGET4args args) throw new LayoutUnavailableException("special DOT file"); } - NfsTransfer transfer = layoutStateid != null ? _transfers.get(layoutStateid) : null; + transfer = layoutStateid != null ? _transfers.get(layoutStateid) : null; if (transfer == null) { Transfer.initSession(false, false); NDC.push(pnfsId.toString()); @@ -924,6 +925,20 @@ public Layout layoutGet(CompoundContext context, LAYOUTGET4args args) */ client.releaseState(stateid); throw new StaleException("File is removed", e); + } catch (PermissionDeniedCacheException e) { + // pool selection due to stage protection or an overwrite attempt of an existing file. + if (transfer.getPool() == null) { + // safe to forget + _log.error("Removing pool-less transfer for client {} for file {}: {}", + toAddrString(context.getRemoteSocketAddress().getAddress()), + transfer.getPnfsId(), e.getMessage()); + _transfers.remove(transfer.getStateid().stateid()); + } else { + // should never happen + _log.error("Report to support@dcache.org: Unexpected permission denied for transfer {} with assigned pool: {}", transfer, e.toString()); + } + + throw new PermException(e.getMessage(), e); } catch (CacheException | ChimeraFsException | TimeoutException | ExecutionException e) { throw asNfsException(e, LayoutTryLaterException.class); } catch (InterruptedException e) { @@ -1539,7 +1554,7 @@ deviceid4[] selectDataServers(long timeout) throws * allow writes only into new files */ if (!attr.getStorageInfo().isCreatedOnly()) { - throw new PermException("Can't modify existing file"); + throw new PermissionDeniedCacheException("Can't modify existing file"); } // REVISIT: this have to go into Transfer class.