/* dCache - http://www.dcache.org/ * * Copyright (C) 2014 Deutsches Elektronen-Synchrotron * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.dcache.xrootd.door; import com.google.common.base.Splitter; import com.google.common.collect.Range; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Required; import javax.security.auth.Subject; import java.io.PrintWriter; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import diskCacheV111.poolManager.PoolMonitorV5; import diskCacheV111.util.CacheException; import diskCacheV111.util.FileLocality; import diskCacheV111.util.FsPath; import diskCacheV111.util.PermissionDeniedCacheException; import diskCacheV111.util.PnfsHandler; import diskCacheV111.util.PnfsId; import diskCacheV111.vehicles.DoorRequestInfoMessage; import diskCacheV111.vehicles.DoorTransferFinishedMessage; import diskCacheV111.vehicles.IoDoorEntry; import diskCacheV111.vehicles.IoDoorInfo; import diskCacheV111.vehicles.PoolIoFileMessage; import diskCacheV111.vehicles.PoolMoverKillMessage; import dmg.cells.nucleus.AbstractCellComponent; import dmg.cells.nucleus.CellCommandListener; import dmg.cells.nucleus.CellInfoProvider; import dmg.cells.nucleus.CellMessageReceiver; import dmg.cells.nucleus.CellPath; import dmg.cells.services.login.LoginManagerChildrenInfo; import org.dcache.acl.enums.AccessType; import org.dcache.auth.Origin; import org.dcache.auth.Subjects; import org.dcache.auth.attributes.Activity; import org.dcache.auth.attributes.Restriction; import org.dcache.cells.CellStub; import org.dcache.cells.MessageCallback; import org.dcache.namespace.ACLPermissionHandler; import org.dcache.namespace.ChainedPermissionHandler; import org.dcache.namespace.FileAttribute; import org.dcache.namespace.FileType; import org.dcache.namespace.PermissionHandler; import org.dcache.namespace.PosixPermissionHandler; import org.dcache.poolmanager.PoolManagerStub; import org.dcache.poolmanager.PoolMonitor; import org.dcache.util.Args; import org.dcache.util.Checksum; import org.dcache.util.FireAndForgetTask; import org.dcache.util.PingMoversTask; import org.dcache.util.Transfer; import org.dcache.util.TransferRetryPolicies; import org.dcache.util.TransferRetryPolicy; import org.dcache.vehicles.FileAttributes; import org.dcache.vehicles.PnfsListDirectoryMessage; import org.dcache.vehicles.XrootdDoorAdressInfoMessage; import org.dcache.vehicles.XrootdProtocolInfo; import org.dcache.xrootd.util.FileStatus; import static com.google.common.base.Preconditions.checkNotNull; import static org.dcache.namespace.FileAttribute.*; import static org.dcache.xrootd.protocol.XrootdProtocol.*; /** * Shared cell component used to interface with the rest of * dCache. * * Current implementation is more or less a copy of the old xrootd * code. Should be replaced by the equivalent component developed by * Tatjana and Tigran. */ public class XrootdDoor extends AbstractCellComponent implements CellMessageReceiver, CellCommandListener, CellInfoProvider { public static final String XROOTD_PROTOCOL_STRING = "Xrootd"; public static final int XROOTD_PROTOCOL_MAJOR_VERSION = 2; public static final int XROOTD_PROTOCOL_MINOR_VERSION = 7; public static final String XROOTD_PROTOCOL_VERSION = String.format("%d.%d", XROOTD_PROTOCOL_MAJOR_VERSION, XROOTD_PROTOCOL_MINOR_VERSION); private static final Logger _log = LoggerFactory.getLogger(XrootdDoor.class); private static final AtomicInteger _handleCounter = new AtomicInteger(); private static final long PING_DELAY = 300000; private static final TransferRetryPolicy RETRY_POLICY = TransferRetryPolicies.tryOncePolicy(Long.MAX_VALUE); private List<FsPath> _readPaths = Collections.singletonList(FsPath.ROOT); private List<FsPath> _writePaths = Collections.singletonList(FsPath.ROOT); private CellStub _poolStub; private PoolManagerStub _poolManagerStub; private CellStub _billingStub; private PoolMonitor _poolMonitor; private final PermissionHandler _pdp = new ChainedPermissionHandler ( new ACLPermissionHandler(), new PosixPermissionHandler()); private int _moverTimeout = 180000; private TimeUnit _moverTimeoutUnit = TimeUnit.MILLISECONDS; private PnfsHandler _pnfs; private String _ioQueue; private Map<UUID, DirlistRequestHandler> _requestHandlers = new ConcurrentHashMap<>(); private ScheduledExecutorService _scheduledExecutor; /** * Current xrootd transfers. The key is the xrootd file handle. */ private final Map<Integer,XrootdTransfer> _transfers = new ConcurrentHashMap<>(); @Required public void setPoolStub(CellStub stub) { _poolStub = stub; } @Required public void setPoolManagerStub(PoolManagerStub stub) { _poolManagerStub = stub; } @Required public void setBillingStub(CellStub stub) { _billingStub = stub; } @Required public void setPoolMonitor(PoolMonitor poolMonitor) { _poolMonitor = poolMonitor; } /** * Converts a colon separated list of paths to a List of FsPath. */ private List<FsPath> toFsPaths(String s) { List<FsPath> list = new ArrayList<>(); for (String path: Splitter.on(":").omitEmptyStrings().split(s)) { list.add(FsPath.create(path)); } return list; } /** * The list of paths which are authorized for xrootd write access. */ @Required public void setWritePaths(String s) { _writePaths = toFsPaths(s); } /** * Returns the list of write paths. * * Notice that the getter uses a different property name than the * setter. This is because the getter returns a different type * than set by the setter, and hence we must not use the same * property name (otherwise Spring complains). */ @Required public List<FsPath> getWritePathsList() { return _writePaths; } /** * The list of paths which are authorized for xrootd write access. */ @Required public void setReadPaths(String s) { _readPaths = toFsPaths(s); } /** * Returns the list of read paths. * * Notice that the getter uses a different property name than the * setter. This is because the getter returns a different type * than set by the setter, and hence we must not use the same * property name (otherwise Spring complains). */ public List<FsPath> getReadPathsList() { return _readPaths; } @Required public void setPnfsHandler(PnfsHandler pnfs) { _pnfs = pnfs; } /** * The actual mover queue on the pool onto which this request gets * scheduled. */ @Required public void setIoQueue(String ioQueue) { _ioQueue = ioQueue; } public String getIoQueue() { return _ioQueue; } /** * Returns the mover timeout in milliseconds. */ public int getMoverTimeout() { return _moverTimeout; } /** * The mover timeout is the time we wait for the mover to start * after having been enqueued. * * @param timeout The mover timeout in milliseconds */ @Required public void setMoverTimeout(int timeout) { if (timeout <= 0) { throw new IllegalArgumentException("Timeout must be positive"); } _moverTimeout = timeout; } public void setMoverTimeoutUnit(TimeUnit unit) { _moverTimeoutUnit = checkNotNull(unit); } public TimeUnit getMoverTimeoutUnit() { return _moverTimeoutUnit; } /** * Sets the ScheduledExecutorService used for periodic tasks. */ @Required public void setExecutor(ScheduledExecutorService executor) { _scheduledExecutor = executor; executor.scheduleAtFixedRate(new FireAndForgetTask(new PingMoversTask<>(_transfers.values())), PING_DELAY, PING_DELAY, TimeUnit.MILLISECONDS); } @Override public void getInfo(PrintWriter pw) { pw.println(String.format("Protocol Version %d.%d", XROOTD_PROTOCOL_MAJOR_VERSION, XROOTD_PROTOCOL_MINOR_VERSION)); } private XrootdTransfer createTransfer(InetSocketAddress client, FsPath path, String ioQueue, UUID uuid, InetSocketAddress local, Subject subject, Restriction restriction) { XrootdTransfer transfer = new XrootdTransfer(_pnfs, subject, restriction, path) { @Override public synchronized void finished(CacheException error) { super.finished(error); _transfers.remove(getFileHandle()); if (error == null) { notifyBilling(0, ""); _log.info("Transfer {}@{} finished", getPnfsId(), getPool()); } else { int rc = error.getRc(); String message = error.getMessage(); notifyBilling(rc, message); _log.info("Transfer {}@{} failed: {} (error code={})", getPnfsId(), getPool(), message, rc); } } }; transfer.setCellAddress(getCellAddress()); transfer.setPoolManagerStub(_poolManagerStub); transfer.setPoolStub(_poolStub); transfer.setBillingStub(_billingStub); transfer.setClientAddress(client); transfer.setUUID(uuid); transfer.setDoorAddress(local); transfer.setIoQueue(ioQueue == null ? _ioQueue : ioQueue); transfer.setFileHandle(_handleCounter.getAndIncrement()); return transfer; } public XrootdTransfer read(InetSocketAddress client, FsPath path, String ioQueue, UUID uuid, InetSocketAddress local, Subject subject, Restriction restriction) throws CacheException, InterruptedException { if (!isReadAllowed(path)) { throw new PermissionDeniedCacheException("Read permission denied"); } XrootdTransfer transfer = createTransfer(client, path, ioQueue, uuid, local, subject, restriction); int handle = transfer.getFileHandle(); InetSocketAddress address = null; _transfers.put(handle, transfer); String explanation = "unspecified problem"; try { transfer.readNameSpaceEntry(false); transfer.selectPoolAndStartMover(RETRY_POLICY); address = transfer.waitForRedirect(_moverTimeout, _moverTimeoutUnit); if (address == null) { throw new CacheException(transfer.getPool() + " failed to open TCP socket"); } transfer.setStatus("Mover " + transfer.getPool() + "/" + transfer.getMoverId() + ": Sending"); } catch (CacheException e) { explanation = e.getMessage(); transfer.notifyBilling(e.getRc(), e.getMessage()); throw e; } catch (InterruptedException e) { explanation = "transfer interrupted"; transfer.notifyBilling(CacheException.UNEXPECTED_SYSTEM_EXCEPTION, "Transfer interrupted"); throw e; } catch (RuntimeException e) { explanation = "bug found: " + e.toString(); transfer.notifyBilling(CacheException.UNEXPECTED_SYSTEM_EXCEPTION, e.toString()); throw e; } finally { if (address == null) { transfer.killMover(0, "killed by door: " + explanation); _transfers.remove(handle); } } return transfer; } public XrootdTransfer write(InetSocketAddress client, FsPath path, String ioQueue, UUID uuid, boolean createDir, boolean overwrite, Long size, InetSocketAddress local, Subject subject, Restriction restriction) throws CacheException, InterruptedException { if (!isWriteAllowed(path)) { throw new PermissionDeniedCacheException("Write permission denied"); } XrootdTransfer transfer = createTransfer(client, path, ioQueue, uuid, local, subject, restriction); transfer.setOverwriteAllowed(overwrite); int handle = transfer.getFileHandle(); InetSocketAddress address = null; _transfers.put(handle, transfer); String explanation = "problem within door"; try { if (createDir) { transfer.createNameSpaceEntryWithParents(); } else { transfer.createNameSpaceEntry(); } if (size != null) { transfer.setLength(size); } try { transfer.selectPoolAndStartMover(RETRY_POLICY); address = transfer.waitForRedirect(_moverTimeout, _moverTimeoutUnit); if (address == null) { throw new CacheException(transfer.getPool() + " failed to open TCP socket"); } transfer.setStatus("Mover " + transfer.getPool() + "/" + transfer.getMoverId() + ": Receiving"); } finally { if (address == null) { transfer.deleteNameSpaceEntry(); } } } catch (CacheException e) { explanation = e.getMessage(); transfer.notifyBilling(e.getRc(), e.getMessage()); throw e; } catch (InterruptedException e) { explanation = "transfer interrupted"; transfer.notifyBilling(CacheException.UNEXPECTED_SYSTEM_EXCEPTION, "Transfer interrupted"); throw e; } catch (RuntimeException e) { explanation = "bug found: " + e.toString(); transfer.notifyBilling(CacheException.UNEXPECTED_SYSTEM_EXCEPTION, e.toString()); throw e; } finally { if (address == null) { transfer.killMover(0, "killed by door: " + explanation); _transfers.remove(handle); } } return transfer; } /** * Delete the file denoted by path from the namespace * * @param path The path of the file that is going to be deleted * @throws CacheException Deletion of the file failed * @throws PermissionDeniedCacheException Caller does not have permission to delete the file */ public void deleteFile(FsPath path, Subject subject, Restriction restriction) throws PermissionDeniedCacheException, CacheException { PnfsHandler pnfsHandler = new PnfsHandler(_pnfs, subject, restriction); if (!isWriteAllowed(path)) { throw new PermissionDeniedCacheException("Write permission denied"); } Set<FileType> allowedSet = EnumSet.of(FileType.REGULAR); PnfsId pnfsId = pnfsHandler.deletePnfsEntry(path.toString(), allowedSet); sendRemoveInfoToBilling(pnfsId, path, subject); } private void sendRemoveInfoToBilling(PnfsId pnfsId, FsPath path, Subject subject) { DoorRequestInfoMessage infoRemove = new DoorRequestInfoMessage(getCellAddress(), "remove"); infoRemove.setSubject(subject); infoRemove.setBillingPath(path.toString()); infoRemove.setPnfsId(pnfsId); Origin origin = Subjects.getOrigin(subject); if (origin != null) { infoRemove.setClient(origin.getAddress().getHostAddress()); } _billingStub.notify(infoRemove); } /** * Delete the directory denoted by path from the namespace * * @param path The path of the directory that is going to be deleted * @throws CacheException */ public void deleteDirectory(FsPath path, Subject subject, Restriction restriction) throws CacheException { PnfsHandler pnfsHandler = new PnfsHandler(_pnfs, subject, restriction); if (!isWriteAllowed(path)) { throw new PermissionDeniedCacheException("Write permission denied"); } Set<FileType> allowedSet = EnumSet.of(FileType.DIR); pnfsHandler.deletePnfsEntry(path.toString(), allowedSet); } /** * Create the directory denoted by path in the namespace. * * @param path The path of the directory that is going to be created. * @param createParents Indicates whether the parent directories of the * directory should be created automatically if they do not yet * exist. * @throws CacheException Creation of the directory failed. */ public void createDirectory(FsPath path, boolean createParents, Subject subject, Restriction restriction) throws CacheException { PnfsHandler pnfsHandler = new PnfsHandler(_pnfs, subject, restriction); if (!isWriteAllowed(path)) { throw new PermissionDeniedCacheException("Write permission denied"); } if (createParents) { pnfsHandler.createDirectories(path); } else { pnfsHandler.createPnfsDirectory(path.toString()); } } /** * Emulate a file-move-operation by renaming sourcePath to targetPath in * the namespace * @param sourcePath the original path of the file that should be moved * @param targetPath the path to which the file should be moved * @throws CacheException */ public void moveFile(FsPath sourcePath, FsPath targetPath, Subject subject, Restriction restriction) throws CacheException { PnfsHandler pnfsHandler = new PnfsHandler(_pnfs, subject, restriction); if (!isWriteAllowed(sourcePath)) { throw new PermissionDeniedCacheException("No write permission on" + " source path!"); } if (!isWriteAllowed(targetPath)) { throw new PermissionDeniedCacheException("No write permission on" + " target path!"); } pnfsHandler.renameEntry(sourcePath.toString(), targetPath.toString(), false); } /** * List the contents of a path, usually a directory. In order to make * fragmented responses, as supported by the xrootd protocol, possible and * not block the processing thread in the door, this will register the * passed callback along with the UUID of the message that is sent to * PNFS-manager. * * Once PNFS-manager replies to the message, that callback is retrieved and * the response is processed by the callback. * * @param path The path that is listed * @param restriction The Restriction in effect * @param subject Representation of user that request listing * @param callback The callback that will process the response */ public void listPath(FsPath path, Subject subject, Restriction restriction, MessageCallback<PnfsListDirectoryMessage> callback, EnumSet<FileAttribute> attributes) { PnfsHandler pnfsHandler = new PnfsHandler(_pnfs, subject, restriction); PnfsListDirectoryMessage msg = new PnfsListDirectoryMessage( path.toString(), null, Range.<Integer>all(), attributes); UUID uuid = msg.getUUID(); try { DirlistRequestHandler requestHandler = new DirlistRequestHandler(uuid, pnfsHandler.getPnfsTimeout(), callback); _requestHandlers.put(uuid, requestHandler); pnfsHandler.send(msg); requestHandler.resetTimeout(); } catch (RejectedExecutionException ree) { _requestHandlers.remove(uuid); callback.failure(CacheException.UNEXPECTED_SYSTEM_EXCEPTION, ree.getMessage()); } } /** * Encapsulate the list directory callback into a handler that manages the * scheduled executor service for the timeout handling. * */ private class DirlistRequestHandler { private ScheduledFuture<?> _executionInstance; private final long _timeout; private final UUID _uuid; private final MessageCallback<PnfsListDirectoryMessage> _callback; public DirlistRequestHandler(UUID uuid, long responseTimeout, MessageCallback<PnfsListDirectoryMessage> callback) { _uuid = uuid; _timeout = responseTimeout; _callback = callback; } /** * Final listing result. Report back via callback and cancel * the timeout handler. * @param msg The reply containing the listing result. */ public synchronized void success(PnfsListDirectoryMessage msg) { if (_requestHandlers.remove(_uuid) == this) { cancelTimeout(); _callback.setReply(msg); _callback.success(); } } /** * Partial listing result, report that back to the callback. Also, * reset the timeout timer in anticipation of further listing results. * @param msg The reply containing the partial directory listing. */ public synchronized void continueListing(PnfsListDirectoryMessage msg) { _callback.setReply(msg); try { _callback.success(); resetTimeout(); } catch (RejectedExecutionException ree) { _requestHandlers.remove(_uuid); _callback.failure(CacheException.UNEXPECTED_SYSTEM_EXCEPTION, ree.getMessage()); } } /** * Remove the request handler from the list, report a failure to the * callback and cancel the timeout timer. * @param msg The reply received from PNFS */ public synchronized void failure(PnfsListDirectoryMessage msg) { if (_requestHandlers.remove(_uuid) == this) { cancelTimeout(); _callback.setReply(msg); _callback.failure(msg.getReturnCode(), msg.getErrorObject()); } } /** * Reschedule the timeout task with the same timeout as initially. * Rescheduling means cancelling the old task and submitting a new one. * @throws RejectedExecutionException */ public synchronized void resetTimeout() throws RejectedExecutionException { Runnable target = () -> { if (_requestHandlers.remove(_uuid) == DirlistRequestHandler.this) { _callback.timeout(null); } }; if (_executionInstance != null) { _executionInstance.cancel(false); } _executionInstance = _scheduledExecutor.schedule(target, _timeout, TimeUnit.MILLISECONDS); } public synchronized void cancelTimeout() { _executionInstance.cancel(false); } } /** * Check whether the given path matches against a list of allowed * write paths. * * @param path the path which is going to be checked */ private boolean isWriteAllowed(FsPath path) { for (FsPath prefix: _writePaths) { if (path.hasPrefix(prefix)) { return true; } } return false; } /** * Check whether the given path matches against a list of allowed * read paths. * * @param path the path which is going to be checked */ private boolean isReadAllowed(FsPath path) { for (FsPath prefix: _readPaths) { if (path.hasPrefix(prefix)) { return true; } } return false; } /** * Requests to start movers are processed synchronously by the * Transfer class. This message handler will only ever receive * replies for those requests for which the Transfer class timed * out or interrupted. * * To avoid that orphaned movers fill a transfer slot on the pool, * we kill it right away. */ public void messageArrived(PoolIoFileMessage message) { if (message.getReturnCode() == 0) { String pool = message.getPoolName(); _poolStub.notify(new CellPath(pool), new PoolMoverKillMessage(pool, message.getMoverId(), "door timed out before pool")); } } public void messageArrived(XrootdDoorAdressInfoMessage msg) { _log.trace("Received redirect msg from mover"); XrootdTransfer transfer = _transfers.get(msg.getXrootdFileHandle()); if (transfer != null) { transfer.redirect(msg.getSocketAddress()); } } public void messageArrived(DoorTransferFinishedMessage msg) { if ((msg.getProtocolInfo() instanceof XrootdProtocolInfo)) { XrootdProtocolInfo info = (XrootdProtocolInfo) msg.getProtocolInfo(); XrootdTransfer transfer = _transfers.get(info.getXrootdFileHandle()); if (transfer != null) { transfer.finished(msg); } } else { _log.warn("Ignoring unknown protocol info {} from pool {}", msg.getProtocolInfo(), msg.getPoolName()); } } /** * Try to find callback registered in listPath(...) and process the * response there * @param msg The reply to a PnfsListDirectoryMessage sent earlier. */ public void messageArrived(PnfsListDirectoryMessage msg) { UUID uuid = msg.getUUID(); DirlistRequestHandler request = _requestHandlers.get(uuid); if (request == null) { _log.info("Did not find the callback for directory listing " + "message with UUID {}.", uuid); return; } if (msg.getReturnCode() == 0 && msg.isFinal()) { request.success(msg); } else if (msg.getReturnCode() == 0) { request.continueListing(msg); } else { request.failure(msg); } } private int getFileStatusFlags(Subject subject, Restriction restriction, FsPath path, FileAttributes attributes) { int flags = 0; switch (attributes.getFileType()) { case DIR: boolean canListDir = _pdp.canListDir(subject, attributes) == AccessType.ACCESS_ALLOWED && !restriction.isRestricted(Activity.LIST, path); boolean canLookup = _pdp.canLookup(subject, attributes) == AccessType.ACCESS_ALLOWED && !restriction.isRestricted(Activity.READ_METADATA, path); boolean canCreateFile = _pdp.canCreateFile(subject, attributes) == AccessType.ACCESS_ALLOWED && !restriction.isRestricted(Activity.UPLOAD, path); boolean canCreateDir = _pdp.canCreateSubDir(subject, attributes) == AccessType.ACCESS_ALLOWED && !restriction.isRestricted(Activity.MANAGE, path); flags |= kXR_isDir; if (canLookup) { flags |= kXR_xset; } if (canCreateFile || canCreateDir) { flags |= kXR_writable; } if (canListDir) { flags |= kXR_readable; } break; case REGULAR: boolean canReadFile = _pdp.canReadFile(subject, attributes)== AccessType.ACCESS_ALLOWED && !restriction.isRestricted(Activity.DOWNLOAD, path); boolean canWriteFile = _pdp.canWriteFile(subject, attributes)== AccessType.ACCESS_ALLOWED && !restriction.isRestricted(Activity.UPLOAD, path); if (canWriteFile) { flags |= kXR_writable; } if (canReadFile) { flags |= kXR_readable; } if (attributes.getStorageInfo().isCreatedOnly()) { flags |= kXR_poscpend; } break; default: flags |= kXR_other; break; } return flags; } public Set<Checksum> getChecksums(FsPath fullPath, Subject subject, Restriction restriction) throws CacheException { PnfsHandler pnfsHandler = new PnfsHandler(_pnfs, subject, restriction); Set<FileAttribute> requestedAttributes = EnumSet.of(CHECKSUM); FileAttributes attributes = pnfsHandler.getFileAttributes(fullPath.toString(), requestedAttributes); return attributes.getChecksums(); } public FileStatus getFileStatus(FsPath fullPath, Subject subject, Restriction restriction, String clientHost) throws CacheException { /* Fetch file attributes. */ PnfsHandler pnfsHandler = new PnfsHandler(_pnfs, subject, restriction); Set<FileAttribute> requestedAttributes = getRequiredAttributesForFileStatus(); FileAttributes attributes = pnfsHandler.getFileAttributes(fullPath.toString(), requestedAttributes); return getFileStatus(subject, restriction, fullPath, clientHost, attributes); } public EnumSet<FileAttribute> getRequiredAttributesForFileStatus() { EnumSet<FileAttribute> requestedAttributes = EnumSet.of(TYPE, SIZE, MODIFICATION_TIME, STORAGEINFO); requestedAttributes.addAll(PoolMonitorV5.getRequiredAttributesForFileLocality()); requestedAttributes.addAll(_pdp.getRequiredAttributes()); return requestedAttributes; } public FileStatus getFileStatus(Subject subject, Restriction restriction, FsPath fullPath, String clientHost, FileAttributes attributes) { int flags = getFileStatusFlags(subject, restriction, fullPath, attributes); /* Determine file locality. */ if (attributes.getFileType() != FileType.DIR) { FileLocality locality = _poolMonitor.getFileLocality(attributes, clientHost); switch (locality) { case NEARLINE: case LOST: case UNAVAILABLE: flags |= kXR_offline; } } return new FileStatus(0, attributes.getSizeIfPresent().or(0L), flags, attributes.getModificationTime() / 1000); } public int[] getMultipleFileStatuses(FsPath[] allPaths, Subject subject, Restriction restriction) throws CacheException { PnfsHandler pnfsHandler = new PnfsHandler(_pnfs, subject, restriction); int[] flags = new int[allPaths.length]; // TODO: Use SpreadAndWait for (int i = 0; i < allPaths.length; i++) { try { Set<FileAttribute> requestedAttributes = EnumSet.of(TYPE); requestedAttributes.addAll(_pdp.getRequiredAttributes()); FileAttributes attributes = pnfsHandler.getFileAttributes(allPaths[i].toString(), requestedAttributes); flags[i] = getFileStatusFlags(subject, restriction, allPaths[i], attributes); } catch (CacheException e) { if (e.getRc() != CacheException.FILE_NOT_FOUND && e.getRc() != CacheException.NOT_IN_TRASH) { throw e; } flags[i] = kXR_other; } } return flags; } /** * To allow the transfer monitoring in the httpd cell to recognize us * as a door, we have to emulate LoginManager. To emulate * LoginManager we list ourselves as our child. */ public static final String hh_get_children = "[-binary]"; public Object ac_get_children(Args args) { boolean binary = args.hasOption("binary"); if (binary) { String [] list = new String[] { getCellName() }; return new LoginManagerChildrenInfo(getCellName(), getCellDomainName(), list); } else { return getCellName(); } } public static final String hh_get_door_info = "[-binary]"; public static final String fh_get_door_info = "Provides information about the door and current transfers"; public Object ac_get_door_info(Args args) { List<IoDoorEntry> entries = new ArrayList<>(); for (Transfer transfer: _transfers.values()) { entries.add(transfer.getIoDoorEntry()); } IoDoorInfo doorInfo = new IoDoorInfo(getCellName(), getCellDomainName()); doorInfo.setProtocol(XROOTD_PROTOCOL_STRING, XROOTD_PROTOCOL_VERSION); doorInfo.setOwner(""); doorInfo.setProcess(""); doorInfo.setIoDoorEntries(entries .toArray(new IoDoorEntry[entries.size()])); return args.hasOption("binary") ? doorInfo : doorInfo.toString(); } public static final String hh_kill_mover = " <pool> <moverid> # kill transfer on the pool"; public String ac_kill_mover_$_2(Args args) throws NumberFormatException { int mover = Integer.parseInt(args.argv(1)); String pool = args.argv(0); for (Transfer transfer : _transfers.values()) { if (transfer.getMoverId() == mover && transfer.getPool() != null && transfer.getPool().equals(pool)) { transfer.killMover(0, "killed by door 'kill mover' command"); return "Kill request to the mover " + mover + " has been submitted"; } } return "mover " + mover + " not found on the pool " + pool; } }