use of org.dcache.xrootd.protocol.XrootdProtocol.FilePerm in project dcache by dCache.
the class XrootdRedirectHandler method doOnOpen.
/**
* For client-server read and write, the open, if successful, will always result in a redirect
* response to the proper pool; hence no subsequent requests like sync, read, write or close are
* expected at the door.
* <p>
* For third-party copy where dCache is the source, the interactions are as follows:
* <p>
* 1. The client opens the file to check availability (the 'placement' stage). An OK response
* is followed by the client closing the file. 2. The client opens the file again with
* rendezvous metadata. The client will close the file only when notified by the destination
* server that the transfer has completed. 3. The destination server will open the file for the
* actual read.
* <p>
* The order of 2, 3 is not deterministic; hence the response here must provide for the
* possibility that the destination server attempts an open before the client specifies a
* time-to-live on the rendezvous point.
* <p>
* The strategy adopted is therefore as follows: response to (1) is simply to check file
* permissions. No metadata is generated and a "dummy" file handle is returned. For 2 and 3,
* whichever occurs first will cause a metadata object to be stored. If the destination server
* open occurs first, a wait response will tell the server to try again in a maximum of 3
* seconds; otherwise, if the request matches and occurs within the ttl, the mover will be
* started and the destination redirected to the pool. Response to the client will carry a file
* handle but will not actually open a mover. The close from the client is handled at the door
* by removing the rendezvous information.
* <p>
* Third-party copy where dCache is the destination should proceed with the usual upload
* transfer creation, but when the client is redirected to the pool and calls kXR_open there, a
* third-party client will be started which does read requests from the source and then writes
* the data to the mover channel.
* <p>
* NOTE: with the changed TPC Lite protocol, the client is not required to open the source
* again during the copy phase (2) if delegation is being used.
*/
@Override
protected XrootdResponse<OpenRequest> doOnOpen(ChannelHandlerContext ctx, OpenRequest req) {
/*
* TODO
*
* We ought to process this asynchronously to not block the calling thread during
* staging or queuing. We should also switch to an asynchronous reply model if
* the request is nearline or is queued on a pool. The naive approach to always
* use an asynchronous reply model doesn't work because the xrootd 3.x client
* introduces an artificial 1 second delay when processing such a response.
*/
InetSocketAddress localAddress = getDestinationAddress();
InetSocketAddress remoteAddress = getSourceAddress();
LoginSessionInfo loginSessionInfo = sessionInfo();
Map<String, String> opaque;
try {
opaque = OpaqueStringParser.getOpaqueMap(req.getOpaque());
if (opaque.isEmpty()) {
/*
* create a new HashMap as empty opaque map is immutable
*/
opaque = new HashMap<>();
}
} catch (ParseException e) {
_log.warn("Ignoring malformed open opaque {}: {}", req.getOpaque(), e.getMessage());
opaque = new HashMap<>();
}
try {
FsPath path = createFullPath(req.getPath());
XrootdResponse response = conditionallyHandleThirdPartyRequest(req, loginSessionInfo, opaque, path, remoteAddress.getHostName());
if (response != null) {
return response;
}
FilePerm neededPerm = req.getRequiredPermission();
_log.info("Opening {} for {}", req.getPath(), neededPerm.xmlText());
if (_log.isDebugEnabled()) {
logDebugOnOpen(req);
}
String ioQueue = appSpecificQueue(req);
Long size = null;
try {
String value = opaque.get("oss.asize");
if (value != null) {
size = Long.valueOf(value);
}
} catch (NumberFormatException exception) {
_log.warn("Ignoring malformed oss.asize: {}", exception.getMessage());
}
_log.info("OPAQUE : {}", opaque);
Set<String> triedHosts = extractTriedHosts(opaque);
UUID uuid = UUID.randomUUID();
opaque.put(UUID_PREFIX, uuid.toString());
/*
* In case this is a third-party open as destination,
* pass the client information to the pool.
*/
opaque.put("org.dcache.xrootd.client", getTpcClientId(req.getSession()));
String opaqueString = OpaqueStringParser.buildOpaqueString(opaque);
/*
* Interact with core dCache to open the requested file.
*/
XrootdTransfer transfer;
if (neededPerm == FilePerm.WRITE) {
/**
* boolean createDir = req.isMkPath() has
* been changed to default to true
* so as to conform to the general expectations that this
* behavior should not depend on the client.
*/
boolean overwrite = req.isDelete() && !req.isNew();
boolean persistOnSuccessfulClose = (req.getOptions() & XrootdProtocol.kXR_posc) == XrootdProtocol.kXR_posc;
// TODO: replace with req.isPersistOnSuccessfulClose() with the latest xrootd4j
transfer = _door.write(remoteAddress, path, triedHosts, ioQueue, uuid, true, overwrite, size, loginSessionInfo.getMaximumUploadSize(), localAddress, loginSessionInfo.getSubject(), loginSessionInfo.getRestriction(), persistOnSuccessfulClose, ((loginSessionInfo.isLoggedIn()) ? loginSessionInfo.getUserRootPath() : _rootPath), req.getSession().getDelegatedCredential(), opaque);
} else {
/*
* If this is a tpc transfer, then dCache is source here.
*
* Since we accept (from the destination server) any
* valid form of authentication, but without requiring
* the associated user to be mapped, we can override
* file permission restrictions (since we possess the
* 'token' rendezvous key, and the client file permissions
* have been checked during its open request).
*/
Subject subject;
if (opaque.get("tpc.key") == null) {
subject = loginSessionInfo.getSubject();
} else {
subject = Subjects.ROOT;
}
transfer = _door.read(remoteAddress, path, triedHosts, ioQueue, uuid, localAddress, subject, loginSessionInfo.getRestriction(), opaque);
/*
* Again, if this is a tpc transfer, then dCache is source here.
* The transfer is initiated by the destination server
* (= current session). However, we wish the doorinfo
* client in billing to reflect the original user connection,
* so we overwrite the transfer client address, which
* is unused by the mover.
*/
String client = opaque.get("tpc.org");
if (client != null) {
int index = client.indexOf("@");
if (index != -1 && index < client.length() - 1) {
client = client.substring(index + 1);
transfer.setClientAddress(new InetSocketAddress(client, 0));
}
}
}
/*
* ok, open was successful
*/
InetSocketAddress address = transfer.getRedirect();
/*
* Do not use the IP address as host name, as this will block
* TLS from working.
*
* According to https://tools.ietf.org/html/rfc5280#section-4.2.1.6
* an IP is required to be in the list of Subject Alternative Names
* in the host certificate, but these are rarely added in practice.
* TLS enforces the RFC and this is a workaround.
*/
String host = address.getHostName();
if (InetAddresses.isInetAddress(host)) {
_log.warn("Unable to resolve IP address {} " + "to a canonical name", host);
}
_log.info("Redirecting to {}, {}", host, address);
return new RedirectResponse<>(req, host, address.getPort(), opaqueString, "");
} catch (ParseException e) {
return withError(req, kXR_ArgInvalid, "Path arguments do not parse");
} catch (FileNotFoundCacheException e) {
return withError(req, xrootdErrorCode(e.getRc()), "No such file");
} catch (FileExistsCacheException e) {
return withError(req, kXR_NotAuthorized, "File already exists");
} catch (TimeoutCacheException e) {
return withError(req, xrootdErrorCode(e.getRc()), "Internal timeout");
} catch (PermissionDeniedCacheException e) {
return withError(req, xrootdErrorCode(e.getRc()), e.getMessage());
} catch (FileIsNewCacheException e) {
return withError(req, xrootdErrorCode(e.getRc()), "File is locked by upload");
} catch (NotFileCacheException e) {
return withError(req, xrootdErrorCode(e.getRc()), "Not a file");
} catch (CacheException e) {
return withError(req, xrootdErrorCode(e.getRc()), String.format("Failed to open file (%s [%d])", e.getMessage(), e.getRc()));
} catch (InterruptedException e) {
/* Interrupt may be caused by cell shutdown or client
* disconnect. If the client disconnected, then the error
* message will never reach the client, so saying that the
* server shut down is okay.
*/
return withError(req, kXR_ServerError, "Server shutdown");
} catch (XrootdException e) {
return withError(req, e.getError(), e.getMessage());
}
}
Aggregations