From 7817174e3ec17edc3061ba18586c04af04759964 Mon Sep 17 00:00:00 2001 From: Albert Louis Rossi Date: Wed, 3 May 2023 14:17:56 -0500 Subject: [PATCH] dcache-core,dcache-xroot,dcache-bulk,dcache-frontend: resolve path prefixes and paths for symlinks Motivation: https://rb.dcache.org/r/13937/ master@51594993ee1ac88d8b8ad350788f5153e0665b98 and https://rb.dcache.org/r/13923/ master@c491f2d312abcbadefcf1df2af85c5620e1e1867 introduced support for relative paths in the Frontend/Bulk requests and in the xroot door, respectively. It did not consider, however, the case when path prefixes may themselves involve symlinks. This could happen either by a symlink in the configured prefix from the door or user root, on in the prefix part of the actual target. Modification: Since https://rb.dcache.org/r/13908/ we have the stored procedure and PnfsManager support for resolving symlinks. This patch adds a PnfsMessage and uses the PnfsHandler to ask for resolution of the symlinks in the Frontend and xroot doors; in addition, symlink resolution is done on the paths of the initial targets given to the Bulk service when they are processed. Result: The support for both absolute and relative paths does not fail if there are symlinks affecting the path prefix. Target: master Request 9.0 (partial -- for frontend) Patch: https://rb.dcache.org/r/13971/ Requires-notes: yes (for 9.0) Acked-by: Tigran --- .../auth/attributes/PrefixRestriction.java | 1 + .../bulk/job/AbstractRequestContainerJob.java | 9 +++- .../bulk/job/PrestoreRequestContainerJob.java | 12 ++--- .../bulk/job/RequestContainerJob.java | 36 +++++++++----- .../restful/resources/bulk/BulkResources.java | 16 ++++-- .../resources/tape/ReleaseResources.java | 13 ++++- .../resources/tape/StageResources.java | 12 ++++- .../dcache/restful/util/HandlerBuilders.java | 7 +++ .../restful/util/HttpServletRequests.java | 16 +++++- .../bulk/BulkRequestJsonParseTest.java | 17 ++++++- .../vehicles/PnfsResolveSymlinksMessage.java | 49 +++++++++++++++++++ .../org/dcache/xrootd/door/XrootdDoor.java | 6 +++ .../xrootd/door/XrootdRedirectHandler.java | 10 ++-- .../namespace/PnfsManagerV3.java | 14 ++++++ 14 files changed, 188 insertions(+), 30 deletions(-) create mode 100644 modules/dcache-vehicles/src/main/java/org/dcache/vehicles/PnfsResolveSymlinksMessage.java diff --git a/modules/common/src/main/java/org/dcache/auth/attributes/PrefixRestriction.java b/modules/common/src/main/java/org/dcache/auth/attributes/PrefixRestriction.java index 2b8c88eb163..cc5c1565bfc 100644 --- a/modules/common/src/main/java/org/dcache/auth/attributes/PrefixRestriction.java +++ b/modules/common/src/main/java/org/dcache/auth/attributes/PrefixRestriction.java @@ -36,6 +36,7 @@ public class PrefixRestriction implements Restriction { private transient Function resolver; public PrefixRestriction(FsPath... prefixes) { + this.prefixes = ImmutableSet.copyOf(prefixes); } diff --git a/modules/dcache-bulk/src/main/java/org/dcache/services/bulk/job/AbstractRequestContainerJob.java b/modules/dcache-bulk/src/main/java/org/dcache/services/bulk/job/AbstractRequestContainerJob.java index 9ddc511b48d..2ddd9b0f002 100644 --- a/modules/dcache-bulk/src/main/java/org/dcache/services/bulk/job/AbstractRequestContainerJob.java +++ b/modules/dcache-bulk/src/main/java/org/dcache/services/bulk/job/AbstractRequestContainerJob.java @@ -104,6 +104,7 @@ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING import org.dcache.util.list.DirectoryStream; import org.dcache.util.list.ListDirectoryHandler; import org.dcache.vehicles.FileAttributes; +import org.dcache.vehicles.PnfsResolveSymlinksMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -445,7 +446,7 @@ protected void expandDepthFirst(Long id, PID pid, FsPath path, FileAttributes di } protected List getInitialTargets() { - return targetStore.getInitialTargets(rid, true); + return targetStore.getInitialTargets(rid, true); } protected boolean hasBeenCancelled(Long id, PID pid, FsPath path, FileAttributes attributes) { @@ -483,6 +484,12 @@ protected void removeTarget(BulkRequestTarget target) { checkTransitionToDirs(); } + protected FsPath resolvePath(String targetPath) throws CacheException { + PnfsResolveSymlinksMessage message = new PnfsResolveSymlinksMessage(targetPath, null); + message = pnfsHandler.request(message); + return FsPath.create(message.getResolvedPath()); + } + private void checkTransitionToDirs() { if (semaphore.availablePermits() == activity.getMaxPermits()) { synchronized (this) { diff --git a/modules/dcache-bulk/src/main/java/org/dcache/services/bulk/job/PrestoreRequestContainerJob.java b/modules/dcache-bulk/src/main/java/org/dcache/services/bulk/job/PrestoreRequestContainerJob.java index ede94c56572..36a6a8401cf 100644 --- a/modules/dcache-bulk/src/main/java/org/dcache/services/bulk/job/PrestoreRequestContainerJob.java +++ b/modules/dcache-bulk/src/main/java/org/dcache/services/bulk/job/PrestoreRequestContainerJob.java @@ -226,22 +226,22 @@ protected void retryFailed(BatchedResult result, FileAttributes attributes) private void addInfo(BulkRequestTarget target) { Long id = target.getId(); - FsPath path = target.getPath(); - if (targetPrefix != null && !path.contains(targetPrefix)) { - path = computeFsPath(targetPrefix, target.getPath().toString()); - } try { + FsPath path = resolvePath(target.getPath().toString()); + if (targetPrefix != null && !path.contains(targetPrefix)) { + path = computeFsPath(targetPrefix, target.getPath().toString()); + } targetInfo.add(new TargetInfo(id, path, pnfsHandler.getFileAttributes(path, MINIMALLY_REQUIRED_ATTRIBUTES))); } catch (CacheException e) { - LOGGER.error("addInfo {}, path {}, error {}.", ruid, path, e.getMessage()); + LOGGER.error("addInfo {}, path {}, error {}.", ruid, target.getPath(), e.getMessage()); target.setState(FAILED); target.setErrorObject(e); statistics.increment(FAILED.name()); try { targetStore.storeOrUpdate(target); } catch (BulkStorageException ex) { - LOGGER.error("addInfo {}, path {}, could not store, error {}.", ruid, path, + LOGGER.error("addInfo {}, path {}, could not store, error {}.", ruid, target.getPath(), ex.getMessage()); } } finally { diff --git a/modules/dcache-bulk/src/main/java/org/dcache/services/bulk/job/RequestContainerJob.java b/modules/dcache-bulk/src/main/java/org/dcache/services/bulk/job/RequestContainerJob.java index 3e17db2f4ab..38c7a4ea4c9 100644 --- a/modules/dcache-bulk/src/main/java/org/dcache/services/bulk/job/RequestContainerJob.java +++ b/modules/dcache-bulk/src/main/java/org/dcache/services/bulk/job/RequestContainerJob.java @@ -129,17 +129,31 @@ protected void processFileTargets() throws InterruptedException { for (BulkRequestTarget tgt : requestTargets) { checkForRequestCancellation(); Long id = tgt.getId(); - FsPath path = tgt.getPath(); - if (targetPrefix != null && !path.contains(targetPrefix)) { - path = computeFsPath(targetPrefix, tgt.getPath().toString()); - } - - switch (depth) { - case NONE: - perform(id, PID.INITIAL, path, null); - break; - default: - handleTarget(id, PID.INITIAL, path); + try { + FsPath path = resolvePath(tgt.getPath().toString()); + if (targetPrefix != null && !path.contains(targetPrefix)) { + path = computeFsPath(targetPrefix, tgt.getPath().toString()); + } + + switch (depth) { + case NONE: + perform(id, PID.INITIAL, path, null); + break; + default: + handleTarget(id, PID.INITIAL, path); + } + } catch (CacheException e) { + LOGGER.error("problem handling target {}: {}.", tgt, e.toString()); + tgt.setState(FAILED); + tgt.setErrorObject(e); + statistics.increment(FAILED.name()); + try { + targetStore.storeOrUpdate(tgt); + } catch (BulkStorageException ex) { + LOGGER.error("processFileTargets {}, path {}, could not store, error {}.", ruid, + tgt.getPath(), + ex.getMessage()); + } } } } diff --git a/modules/dcache-frontend/src/main/java/org/dcache/restful/resources/bulk/BulkResources.java b/modules/dcache-frontend/src/main/java/org/dcache/restful/resources/bulk/BulkResources.java index 2c16db09f31..b5a8ad3cc59 100644 --- a/modules/dcache-frontend/src/main/java/org/dcache/restful/resources/bulk/BulkResources.java +++ b/modules/dcache-frontend/src/main/java/org/dcache/restful/resources/bulk/BulkResources.java @@ -67,6 +67,7 @@ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING import com.google.common.base.Strings; import com.google.gson.Gson; import com.google.gson.JsonParseException; +import diskCacheV111.util.PnfsHandler; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; @@ -84,6 +85,7 @@ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING import java.util.Set; import java.util.stream.Collectors; import javax.inject.Inject; +import javax.inject.Named; import javax.security.auth.Subject; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.BadRequestException; @@ -104,6 +106,8 @@ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING import org.dcache.auth.Subjects; import org.dcache.auth.attributes.Restriction; import org.dcache.auth.attributes.Restrictions; +import org.dcache.cells.CellStub; +import org.dcache.restful.util.HandlerBuilders; import org.dcache.restful.util.RequestUser; import org.dcache.restful.util.bulk.BulkServiceCommunicator; import org.dcache.services.bulk.BulkRequest; @@ -136,6 +140,10 @@ public final class BulkResources { @Inject private BulkServiceCommunicator service; + @Inject + @Named("pnfs-stub") + private CellStub pnfsmanager; + /** * @return List of bulk request summaries that have not been cleared. * If the client includes no query string then the response contains all bulk requests @@ -224,8 +232,8 @@ public Response submit( String requestPayload) { Subject subject = getSubject(); Restriction restriction = getRestriction(); - - BulkRequest request = toBulkRequest(requestPayload, this.request); + PnfsHandler handler = HandlerBuilders.unrestrictedPnfsHandler(pnfsmanager); + BulkRequest request = toBulkRequest(requestPayload, this.request, handler); /* * Frontend sets the URL. The backend service provides the UUID. @@ -414,7 +422,7 @@ public static Restriction getRestriction() { * they are defined in the Bulk service as well. */ @VisibleForTesting - static BulkRequest toBulkRequest(String requestPayload, HttpServletRequest httpServletRequest) { + static BulkRequest toBulkRequest(String requestPayload, HttpServletRequest httpServletRequest, PnfsHandler handler) { if (Strings.emptyToNull(requestPayload) == null) { throw new BadRequestException("empty request payload."); } @@ -448,7 +456,7 @@ static BulkRequest toBulkRequest(String requestPayload, HttpServletRequest httpS string = removeEntry(map, String.class, "target_prefix", "target-prefix", "targetPrefix"); if (httpServletRequest != null) { - request.setTargetPrefix(getUserRootAwareTargetPrefix(httpServletRequest, string)); + request.setTargetPrefix(getUserRootAwareTargetPrefix(httpServletRequest, string, handler)); } else { request.setTargetPrefix(string); } diff --git a/modules/dcache-frontend/src/main/java/org/dcache/restful/resources/tape/ReleaseResources.java b/modules/dcache-frontend/src/main/java/org/dcache/restful/resources/tape/ReleaseResources.java index a61624e4699..3609b01e474 100644 --- a/modules/dcache-frontend/src/main/java/org/dcache/restful/resources/tape/ReleaseResources.java +++ b/modules/dcache-frontend/src/main/java/org/dcache/restful/resources/tape/ReleaseResources.java @@ -64,6 +64,7 @@ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING import static org.dcache.restful.util.RequestUser.getRestriction; import static org.dcache.restful.util.RequestUser.getSubject; +import diskCacheV111.util.PnfsHandler; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; @@ -75,6 +76,7 @@ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING import java.util.List; import java.util.Map; import javax.inject.Inject; +import javax.inject.Named; import javax.security.auth.Subject; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.BadRequestException; @@ -87,6 +89,8 @@ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.dcache.auth.attributes.Restriction; +import org.dcache.cells.CellStub; +import org.dcache.restful.util.HandlerBuilders; import org.dcache.restful.util.bulk.BulkServiceCommunicator; import org.dcache.services.bulk.BulkRequest; import org.dcache.services.bulk.BulkRequest.Depth; @@ -112,6 +116,10 @@ public final class ReleaseResources { @Inject private BulkServiceCommunicator service; + @Inject + @Named("pnfs-stub") + private CellStub pnfsmanager; + /** * Release files belonging to a bulk STAGE request. *

@@ -173,7 +181,10 @@ public Response release( * Frontend sets the URL. The backend service provides the UUID. */ request.setUrlPrefix(this.request.getRequestURL().toString()); - request.setTargetPrefix(getUserRootAwareTargetPrefix(this.request, null)); + + PnfsHandler handler = HandlerBuilders.unrestrictedPnfsHandler(pnfsmanager); + + request.setTargetPrefix(getUserRootAwareTargetPrefix(this.request, null, handler)); BulkRequestMessage message = new BulkRequestMessage(request, restriction); message.setSubject(subject); diff --git a/modules/dcache-frontend/src/main/java/org/dcache/restful/resources/tape/StageResources.java b/modules/dcache-frontend/src/main/java/org/dcache/restful/resources/tape/StageResources.java index 6931166e874..61583b633e0 100644 --- a/modules/dcache-frontend/src/main/java/org/dcache/restful/resources/tape/StageResources.java +++ b/modules/dcache-frontend/src/main/java/org/dcache/restful/resources/tape/StageResources.java @@ -65,6 +65,7 @@ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING import static org.dcache.restful.util.JSONUtils.newBadRequestException; import com.google.common.base.Strings; +import diskCacheV111.util.PnfsHandler; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; @@ -77,6 +78,7 @@ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING import java.util.Map; import java.util.Optional; import javax.inject.Inject; +import javax.inject.Named; import javax.security.auth.Subject; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.BadRequestException; @@ -92,7 +94,9 @@ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import org.dcache.auth.attributes.Restriction; +import org.dcache.cells.CellStub; import org.dcache.restful.providers.tape.StageRequestInfo; +import org.dcache.restful.util.HandlerBuilders; import org.dcache.restful.util.bulk.BulkServiceCommunicator; import org.dcache.services.bulk.BulkRequest; import org.dcache.services.bulk.BulkRequest.Depth; @@ -125,6 +129,10 @@ public final class StageResources { @Inject private BulkServiceCommunicator service; + @Inject + @Named("pnfs-stub") + private CellStub pnfsmanager; + private String[] supportedSitenames; /** @@ -341,7 +349,9 @@ private BulkRequest toBulkRequest(String requestPayload) { request.setClearOnFailure(false); request.setClearOnSuccess(false); request.setActivity("STAGE"); - request.setTargetPrefix(getUserRootAwareTargetPrefix(this.request, null)); + + PnfsHandler handler = HandlerBuilders.unrestrictedPnfsHandler(pnfsmanager); + request.setTargetPrefix(getUserRootAwareTargetPrefix(this.request, null, handler)); try { JSONObject reqPayload = new JSONObject(requestPayload); diff --git a/modules/dcache-frontend/src/main/java/org/dcache/restful/util/HandlerBuilders.java b/modules/dcache-frontend/src/main/java/org/dcache/restful/util/HandlerBuilders.java index a53f03e0ca7..0d107096149 100644 --- a/modules/dcache-frontend/src/main/java/org/dcache/restful/util/HandlerBuilders.java +++ b/modules/dcache-frontend/src/main/java/org/dcache/restful/util/HandlerBuilders.java @@ -45,4 +45,11 @@ public static PnfsHandler roleAwarePnfsHandler(CellStub pnfsManager) { return handler; } + + public static PnfsHandler unrestrictedPnfsHandler(CellStub pnfsManager) { + PnfsHandler handler = pnfsHandler(pnfsManager); + handler.setSubject(Subjects.ROOT); + handler.setRestriction(Restrictions.none()); + return handler; + } } diff --git a/modules/dcache-frontend/src/main/java/org/dcache/restful/util/HttpServletRequests.java b/modules/dcache-frontend/src/main/java/org/dcache/restful/util/HttpServletRequests.java index be0384d2ab1..0ce8838ca15 100644 --- a/modules/dcache-frontend/src/main/java/org/dcache/restful/util/HttpServletRequests.java +++ b/modules/dcache-frontend/src/main/java/org/dcache/restful/util/HttpServletRequests.java @@ -20,10 +20,13 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; +import diskCacheV111.util.CacheException; import diskCacheV111.util.FsPath; +import diskCacheV111.util.PnfsHandler; import java.util.Set; import javax.security.auth.Subject; import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.BadRequestException; import org.dcache.auth.Subjects; import org.dcache.auth.attributes.LoginAttribute; import org.dcache.auth.attributes.LoginAttributes; @@ -31,6 +34,7 @@ import org.dcache.auth.attributes.Restrictions; import org.dcache.auth.attributes.RootDirectory; import org.dcache.http.AuthenticationHandler; +import org.dcache.vehicles.PnfsResolveSymlinksMessage; /** * Utility class for methods that operate on an HttpServletRequest object. @@ -63,7 +67,7 @@ public static Restriction roleAwareRestriction(HttpServletRequest request) { } public static String getUserRootAwareTargetPrefix(HttpServletRequest request, - String includedPrefix) { + String includedPrefix, PnfsHandler handler) throws BadRequestException { FsPath userRootPath = getLoginAttributes(request).stream() .filter(RootDirectory.class::isInstance) .findFirst() @@ -71,7 +75,15 @@ public static String getUserRootAwareTargetPrefix(HttpServletRequest request, .map(RootDirectory::getRoot) .map(FsPath::create) .orElse(FsPath.ROOT); - return getTargetPrefixFromUserRoot(userRootPath, includedPrefix); + PnfsResolveSymlinksMessage message = new PnfsResolveSymlinksMessage(userRootPath.toString(), + includedPrefix); + try { + message = handler.request(message); + } catch (CacheException e) { + throw new BadRequestException(e); + } + return getTargetPrefixFromUserRoot(FsPath.create(message.getResolvedPath()), + message.getPrefix()); } @VisibleForTesting diff --git a/modules/dcache-frontend/src/test/java/org/dcache/restful/resources/bulk/BulkRequestJsonParseTest.java b/modules/dcache-frontend/src/test/java/org/dcache/restful/resources/bulk/BulkRequestJsonParseTest.java index 81aa1e8314a..33e78a20f3f 100644 --- a/modules/dcache-frontend/src/test/java/org/dcache/restful/resources/bulk/BulkRequestJsonParseTest.java +++ b/modules/dcache-frontend/src/test/java/org/dcache/restful/resources/bulk/BulkRequestJsonParseTest.java @@ -62,11 +62,16 @@ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING import static org.dcache.restful.resources.bulk.BulkResources.toBulkRequest; import static org.junit.Assert.assertEquals; +import diskCacheV111.util.CacheException; +import diskCacheV111.util.PnfsHandler; +import diskCacheV111.vehicles.PnfsMessage; +import dmg.cells.nucleus.CellPath; import java.util.List; import java.util.concurrent.TimeUnit; import javax.ws.rs.BadRequestException; import org.dcache.services.bulk.BulkRequest; import org.dcache.services.bulk.BulkRequest.Depth; +import org.dcache.vehicles.PnfsResolveSymlinksMessage; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -91,6 +96,10 @@ public class BulkRequestJsonParseTest { Boolean clearOnFailure; Depth expandDirectories; + PnfsHandler handler; + + PnfsResolveSymlinksMessage message; + @Before public void setUp() { activity = "PIN"; @@ -101,6 +110,12 @@ public void setUp() { clearOnSuccess = true; clearOnFailure = true; expandDirectories = Depth.ALL; + handler = new PnfsHandler(new CellPath()) { + public T request(T msg) + throws CacheException { + return msg; + } + }; } @Test @@ -167,6 +182,6 @@ private void givenJsonWithArrayTargetAttribute() { } private void whenParsed() { - bulkRequest = toBulkRequest(requestJson, null); + bulkRequest = toBulkRequest(requestJson, null, handler); } } diff --git a/modules/dcache-vehicles/src/main/java/org/dcache/vehicles/PnfsResolveSymlinksMessage.java b/modules/dcache-vehicles/src/main/java/org/dcache/vehicles/PnfsResolveSymlinksMessage.java new file mode 100644 index 00000000000..cc61105cda6 --- /dev/null +++ b/modules/dcache-vehicles/src/main/java/org/dcache/vehicles/PnfsResolveSymlinksMessage.java @@ -0,0 +1,49 @@ +/* + * $Id: PnfsRenameMessage.java,v 1.2 2005-02-21 15:49:33 tigran Exp $ + */ + +package org.dcache.vehicles; + +import diskCacheV111.vehicles.PnfsMessage; + +public class PnfsResolveSymlinksMessage extends PnfsMessage { + + private static final long serialVersionUID = 5670471419391898275L; + + private final String prefix; + + private String resolvedPrefix; + + private String resolvedPath; + + public PnfsResolveSymlinksMessage(String path) { + this(path, null); + } + + public PnfsResolveSymlinksMessage(String path, String prefix) { + setPnfsPath(path); + this.prefix = prefix; + setReplyRequired(true); + } + + public String getPrefix() { + return prefix; + } + + public String getResolvedPath() { + return resolvedPath; + } + + public void setResolvedPath(String resolvedPath) { + this.resolvedPath = resolvedPath; + setSucceeded(); + } + + public String getResolvedPrefix() { + return resolvedPrefix; + } + + public void setResolvedPrefix(String resolvedPrefix) { + this.resolvedPrefix = resolvedPrefix; + } +} diff --git a/modules/dcache-xrootd/src/main/java/org/dcache/xrootd/door/XrootdDoor.java b/modules/dcache-xrootd/src/main/java/org/dcache/xrootd/door/XrootdDoor.java index baae0a3c13e..61a860c2ed1 100644 --- a/modules/dcache-xrootd/src/main/java/org/dcache/xrootd/door/XrootdDoor.java +++ b/modules/dcache-xrootd/src/main/java/org/dcache/xrootd/door/XrootdDoor.java @@ -120,6 +120,7 @@ import org.dcache.util.TransferRetryPolicy; import org.dcache.vehicles.FileAttributes; import org.dcache.vehicles.PnfsListDirectoryMessage; +import org.dcache.vehicles.PnfsResolveSymlinksMessage; import org.dcache.vehicles.XrootdDoorAdressInfoMessage; import org.dcache.vehicles.XrootdProtocolInfo; import org.dcache.xrootd.door.proxy.NettyXrootProxyAdapter; @@ -314,6 +315,11 @@ private List toFsPaths(String s) { return list; } + public PnfsResolveSymlinksMessage sendResolveRequest(String root, String path) + throws CacheException { + return _pnfs.request(new PnfsResolveSymlinksMessage(path, root)); + } + /** * The list of paths which are authorized for xrootd write access. */ diff --git a/modules/dcache-xrootd/src/main/java/org/dcache/xrootd/door/XrootdRedirectHandler.java b/modules/dcache-xrootd/src/main/java/org/dcache/xrootd/door/XrootdRedirectHandler.java index ae010e61762..212102313c1 100644 --- a/modules/dcache-xrootd/src/main/java/org/dcache/xrootd/door/XrootdRedirectHandler.java +++ b/modules/dcache-xrootd/src/main/java/org/dcache/xrootd/door/XrootdRedirectHandler.java @@ -99,6 +99,7 @@ import org.dcache.util.Checksum; import org.dcache.util.list.DirectoryEntry; import org.dcache.vehicles.PnfsListDirectoryMessage; +import org.dcache.vehicles.PnfsResolveSymlinksMessage; import org.dcache.xrootd.LoginTokens; import org.dcache.xrootd.core.XrootdException; import org.dcache.xrootd.core.XrootdSession; @@ -1112,7 +1113,7 @@ protected XrootdResponse doOnDirList(ChannelHandlerContext ctx, EnumSet.noneOf(FileAttribute.class)); } return null; - } catch (PermissionDeniedCacheException e) { + } catch (CacheException e) { throw xrootdException(e); } } @@ -1376,7 +1377,7 @@ private void loggedIn(LoginEvent event) { * of the path returned. */ private FsPath createFullPath(String path, Map opaque) - throws PermissionDeniedCacheException { + throws CacheException { String fromOpaque = opaque.get(EFFECTIVE_ROOT_NAME); FsPath root = fromOpaque != null ? FsPath.create(fromOpaque) : effectiveRoot(); @@ -1384,7 +1385,10 @@ private FsPath createFullPath(String path, Map opaque) path = "/" + path; } - FsPath fullPath = FsPath.create(path); + PnfsResolveSymlinksMessage message = _door.sendResolveRequest(root.toString(), path); + + root = FsPath.create(message.getResolvedPrefix()); + FsPath fullPath = FsPath.create(message.getResolvedPath()); if (fullPath.hasPrefix(root)) { path = fullPath.stripPrefix(root); diff --git a/modules/dcache/src/main/java/diskCacheV111/namespace/PnfsManagerV3.java b/modules/dcache/src/main/java/diskCacheV111/namespace/PnfsManagerV3.java index 3b5ccbe2da1..09191272a02 100644 --- a/modules/dcache/src/main/java/diskCacheV111/namespace/PnfsManagerV3.java +++ b/modules/dcache/src/main/java/diskCacheV111/namespace/PnfsManagerV3.java @@ -146,6 +146,7 @@ import org.dcache.vehicles.PnfsGetFileAttributes; import org.dcache.vehicles.PnfsListDirectoryMessage; import org.dcache.vehicles.PnfsRemoveChecksumMessage; +import org.dcache.vehicles.PnfsResolveSymlinksMessage; import org.dcache.vehicles.PnfsSetFileAttributes; import org.dcache.vehicles.quota.PnfsManagerGetQuotaMessage; import org.dcache.vehicles.quota.PnfsManagerQuotaMessage; @@ -2680,6 +2681,8 @@ boolean processMessageTransactionally(CellMessage message, PnfsMessage pnfsMessa removeExtendedAttributes((PnfsRemoveExtendedAttributesMessage) pnfsMessage); } else if (pnfsMessage instanceof PnfsRemoveLabelsMessage) { removeLabel((PnfsRemoveLabelsMessage) pnfsMessage); + } else if (pnfsMessage instanceof PnfsResolveSymlinksMessage) { + resolveSymlinks((PnfsResolveSymlinksMessage) pnfsMessage); } else { LOGGER.warn("Unexpected message class [{}] from source [{}]", pnfsMessage.getClass(), message.getSourcePath()); @@ -3309,6 +3312,17 @@ private void checkRestriction(Restriction restriction, Set mask, } } + private void resolveSymlinks(PnfsResolveSymlinksMessage message) { + String prefix = message.getPrefix(); + String path = message.getPnfsPath(); + if (Strings.emptyToNull(prefix) != null) { + message.setResolvedPrefix(resolveSymlinks(prefix).toString()); + } + + message.setResolvedPath(resolveSymlinks(path).toString()); + message.setSucceeded(); + } + private FsPath resolveSymlinks(String target) { try { return recursivelyResolveSymlinks(target);