/* * This file is part of DrFTPD, Distributed FTP Daemon. * * DrFTPD is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * DrFTPD 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with DrFTPD; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.drftpd.master.vfs; import org.drftpd.common.io.PermissionDeniedException; import org.drftpd.common.slave.LightRemoteInode; import org.drftpd.master.GlobalContext; import org.drftpd.master.slavemanagement.RemoteSlave; import org.drftpd.master.usermanager.User; import org.drftpd.slave.exceptions.FileExistsException; import java.io.FileNotFoundException; import java.io.IOException; import java.util.*; /** * @author zubov * @version $Id$ */ public class DirectoryHandle extends InodeHandle implements DirectoryHandleInterface { public DirectoryHandle(String path) { super(path); } public boolean collisionhandle() { return GlobalContext.getConfig().getMainProperties().getProperty("delete.collision.files", "true").equals("true"); } /** * @param reason * @throws FileNotFoundException if this Directory does not exist */ public void abortAllTransfers(String reason) throws FileNotFoundException { for (FileHandle file : getFilesUnchecked()) { try { file.abortTransfers(reason); } catch (FileNotFoundException e) { } } } /** * Returns a DirectoryHandle for a possibly non-existant directory in this path * No verification to its existence is made * * @param name * @return */ public DirectoryHandle getNonExistentDirectoryHandle(String name) { // There are more than one use cases here // We can be in the root and still pass a absolute path // First we check if the path starts with the expected separator and then asume absolute path if (name.startsWith(VirtualFileSystem.separator)) { // absolute path, easy to handle return new DirectoryHandle(name); } // If we are in root and we did not present an absolute path we add the root here if (isRoot()) { // We are at the root, file must start with a single separator return new DirectoryHandle(VirtualFileSystem.separator + name); } // path must be relative return new DirectoryHandle(getPath() + VirtualFileSystem.separator + name); } /** * Returns a LinkHandle for a possibly non-existant directory in this path * No verification to its existence is made * * @param name * @return */ public LinkHandle getNonExistentLinkHandle(String name) { return new LinkHandle(getPath() + VirtualFileSystem.separator + name); } @Override public VirtualFileSystemDirectory getInode() throws FileNotFoundException { VirtualFileSystemInode inode = super.getInode(); if (inode instanceof VirtualFileSystemDirectory) { return (VirtualFileSystemDirectory) inode; } throw new ClassCastException( "DirectoryHandle object pointing to Inode:" + inode); } /** * @return all InodeHandles inside this dir. * @throws FileNotFoundException */ public Set<InodeHandle> getInodeHandles(User user) throws FileNotFoundException { Set<InodeHandle> inodes = getInodeHandlesUnchecked(); for (Iterator<InodeHandle> iter = inodes.iterator(); iter.hasNext(); ) { InodeHandle inode = iter.next(); try { checkHiddenPath(inode, user); } catch (FileNotFoundException e) { // file is hidden or a race just happened. iter.remove(); } } return inodes; } /** * @return all InodeHandles inside this dir. * @throws FileNotFoundException */ public Set<InodeHandle> getInodeHandlesUnchecked() throws FileNotFoundException { return getInode().getInodes(); } public ArrayList<FileHandle> getAllFilesRecursiveUnchecked() { ArrayList<FileHandle> files = new ArrayList<>(); try { for (InodeHandle inode : getInodeHandlesUnchecked()) { if (inode.isFile()) { files.add((FileHandle) inode); } else if (inode.isDirectory()) { files.addAll(((DirectoryHandle) inode).getAllFilesRecursiveUnchecked()); } } } catch (FileNotFoundException e) { // oh well, we just won't have any files to add } return files; } /** * This method *does* check for hidden paths. * * @return a set containing only the files of this dir. * (no links or directories included.) * @throws FileNotFoundException */ public Set<FileHandle> getFiles(User user) throws FileNotFoundException { return getFilesUnchecked(getInodeHandles(user)); } /** * This method *does* check for hidden paths. * * @return a sorted set containing only the files of this dir. * (no links or directories included.) * @throws FileNotFoundException */ public Set<FileHandle> getSortedFiles(User user) throws FileNotFoundException { ArrayList<InodeHandle> sortedInodes = new ArrayList<>(getInodeHandles(user)); sortedInodes.sort(VirtualFileSystem.INODE_HANDLE_CASE_INSENSITIVE_COMPARATOR); return getFilesUnchecked(sortedInodes); } /** * This method does not check for hidden paths. * * @return a set containing only the files of this dir. * (no links or directories included.) * @throws FileNotFoundException */ public Set<FileHandle> getFilesUnchecked() throws FileNotFoundException { return getFilesUnchecked(getInodeHandlesUnchecked()); } /** * This method does not check for hidden paths. * * @return a sorted set containing only the files of this dir. * (no links or directories included.) * @throws FileNotFoundException */ public Set<FileHandle> getSortedFilesUnchecked() throws FileNotFoundException { ArrayList<InodeHandle> sortedInodes = new ArrayList<>(getInodeHandlesUnchecked()); sortedInodes.sort(VirtualFileSystem.INODE_HANDLE_CASE_INSENSITIVE_COMPARATOR); return getFilesUnchecked(sortedInodes); } private Set<FileHandle> getFilesUnchecked(Collection<InodeHandle> inodes) throws FileNotFoundException { Set<FileHandle> set = new LinkedHashSet<>(); for (InodeHandle handle : getInode().getInodes()) { if (handle instanceof FileHandle) { set.add((FileHandle) handle); } } return set; } /** * . * This method *does* check for hiddens paths. * * @return a set containing only the directories of this dir. (no links or files included.) * @throws FileNotFoundException */ public Set<DirectoryHandle> getDirectories(User user) throws FileNotFoundException { return getDirectoriesUnchecked(getInodeHandles(user)); } /** * . * This method *does* check for hiddens paths. * * @return a sorted set containing only the directories of this dir. (no links or files included.) * @throws FileNotFoundException */ public Set<DirectoryHandle> getSortedDirectories(User user) throws FileNotFoundException { ArrayList<InodeHandle> sortedInodes = new ArrayList<>(getInodeHandles(user)); sortedInodes.sort(VirtualFileSystem.INODE_HANDLE_CASE_INSENSITIVE_COMPARATOR); return getDirectoriesUnchecked(sortedInodes); } /** * This method does not check for hiddens paths. * * @return a set containing only the directories of this dir. (no links or files included.) * @throws FileNotFoundException */ public Set<DirectoryHandle> getDirectoriesUnchecked() throws FileNotFoundException { return getDirectoriesUnchecked(getInodeHandlesUnchecked()); } /** * This method does not check for hiddens paths. * * @return a sorted set containing only the directories of this dir. (no links or files included.) * @throws FileNotFoundException */ public Set<DirectoryHandle> getSortedDirectoriesUnchecked() throws FileNotFoundException { ArrayList<InodeHandle> sortedInodes = new ArrayList<>(getInodeHandlesUnchecked()); sortedInodes.sort(VirtualFileSystem.INODE_HANDLE_CASE_INSENSITIVE_COMPARATOR); return getDirectoriesUnchecked(sortedInodes); } /** * This method iterates through the given Collection, removing non-Directory objects. * * @return a set containing only the directories of this dir. (no links or files included.) * @throws FileNotFoundException */ private Set<DirectoryHandle> getDirectoriesUnchecked(Collection<InodeHandle> inodes) throws FileNotFoundException { Set<DirectoryHandle> set = new LinkedHashSet<>(); for (InodeHandle handle : inodes) { if (handle instanceof DirectoryHandle) { set.add((DirectoryHandle) handle); } } return set; } /** * This method *does* check for hiddens paths. * * @return a set containing only the links of this dir. * (no directories or files included.) * @throws FileNotFoundException */ public Set<LinkHandle> getLinks(User user) throws FileNotFoundException { return getLinksUnchecked(getInodeHandles(user)); } /** * This method *does* check for hiddens paths. * * @return a sorted set containing only the links of this dir. * (no directories or files included.) * @throws FileNotFoundException */ public Set<LinkHandle> getSortedLinks(User user) throws FileNotFoundException { ArrayList<InodeHandle> sortedInodes = new ArrayList<>(getInodeHandles(user)); sortedInodes.sort(VirtualFileSystem.INODE_HANDLE_CASE_INSENSITIVE_COMPARATOR); return getLinksUnchecked(sortedInodes); } /** * This method does not check for hiddens paths. * * @return a set containing only the links of this dir. * (no directories or files included.) * @throws FileNotFoundException */ public Set<LinkHandle> getLinksUnchecked() throws FileNotFoundException { return getLinksUnchecked(getInodeHandlesUnchecked()); } /** * This method does not check for hiddens paths. * * @return a sorted set containing only the links of this dir. * (no directories or files included.) * @throws FileNotFoundException */ public Set<LinkHandle> getSortedLinksUnchecked() throws FileNotFoundException { ArrayList<InodeHandle> sortedInodes = new ArrayList<>(getInodeHandlesUnchecked()); sortedInodes.sort(VirtualFileSystem.INODE_HANDLE_CASE_INSENSITIVE_COMPARATOR); return getLinksUnchecked(sortedInodes); } private Set<LinkHandle> getLinksUnchecked(Collection<InodeHandle> inodes) { Set<LinkHandle> set = new LinkedHashSet<>(); for (InodeHandle handle : inodes) { if (handle instanceof LinkHandle) { set.add((LinkHandle) handle); } } return set; } /** * This method *does* check for hiddens paths. * * @return true if the dir has offline files. * @throws FileNotFoundException */ public boolean hasOfflineFiles(User user) throws FileNotFoundException { return getOfflineFiles(user).size() != 0; } /** * This method does not check for hidden paths. * * @return true if the dir has offline files. * @throws FileNotFoundException */ public boolean hasOfflineFilesUnchecked() throws FileNotFoundException { return getOfflineFilesUnchecked().size() != 0; } /** * This method *does* check for hiddens paths. * * @return a set containing only the offline files of this dir. * @throws FileNotFoundException */ private Set<FileHandle> getOfflineFiles(User user) throws FileNotFoundException { Set<FileHandle> allFiles = user == null ? getFilesUnchecked() : getFiles(user); Set<FileHandle> offlineFiles = new LinkedHashSet<>(allFiles.size()); for (FileHandle file : allFiles) { if (!file.isAvailable()) offlineFiles.add(file); } return offlineFiles; } /** * This method does not check for hidden paths. * * @return a set containing only the offline files of this dir. * @throws FileNotFoundException */ private Set<FileHandle> getOfflineFilesUnchecked() throws FileNotFoundException { return getOfflineFiles(null); } /** * @param name * @throws FileNotFoundException */ public InodeHandle getInodeHandle(String name, User user) throws FileNotFoundException { InodeHandle inode = getInodeHandleUnchecked(name); checkHiddenPath(inode, user); return inode; } public InodeHandle getInodeHandleUnchecked(String name) throws FileNotFoundException { VirtualFileSystemInode inode = getInode().getInodeByName(name); if (inode.isDirectory()) { return new DirectoryHandle(inode.getPath()); } else if (inode.isFile()) { return new FileHandle(inode.getPath()); } else if (inode.isLink()) { return new LinkHandle(inode.getPath()); } throw new IllegalStateException( "Not a directory, file, or link -- punt"); } public DirectoryHandle getDirectory(String name, User user) throws FileNotFoundException, ObjectNotValidException { DirectoryHandle dir = getDirectoryUnchecked(name); checkHiddenPath(dir, user); return dir; } public DirectoryHandle getDirectoryUnchecked(String name) throws FileNotFoundException, ObjectNotValidException { if (name.equals(VirtualFileSystem.separator)) { return new DirectoryHandle("/"); } logger.debug("getDirectory({})", name); if (name.equals("..")) { return getParent(); } else if (name.startsWith("../")) { // strip off the ../ return getParent().getDirectoryUnchecked(name.substring(3)); } else if (name.equals(".")) { return this; } else if (name.startsWith("./")) { return getDirectoryUnchecked(name.substring(2)); } InodeHandle handle = getInodeHandleUnchecked(name); if (handle.isDirectory()) { return (DirectoryHandle) handle; } if (handle.isLink()) { return ((LinkHandle) handle).getTargetDirectoryUnchecked(); } throw new ObjectNotValidException(name + " is not a directory"); } public FileHandle getFile(String name, User user) throws FileNotFoundException, ObjectNotValidException { FileHandle file = getFileUnchecked(name); checkHiddenPath(file.getParent(), user); return file; } public FileHandle getFileUnchecked(String name) throws FileNotFoundException, ObjectNotValidException { InodeHandle handle = getInodeHandleUnchecked(name); if (handle.isFile()) { return (FileHandle) handle; } else if (handle.isLink()) { LinkHandle link = (LinkHandle) handle; return link.getTargetFileUnchecked(); } throw new ObjectNotValidException(name + " is not a file"); } public LinkHandle getLink(String name, User user) throws FileNotFoundException, ObjectNotValidException { LinkHandle link = getLinkUnchecked(name); checkHiddenPath(link.getTargetInode(user), user); return link; } public LinkHandle getLinkUnchecked(String name) throws FileNotFoundException, ObjectNotValidException { InodeHandle handle = getInodeHandleUnchecked(name); if (handle.isLink()) { return (LinkHandle) handle; } throw new ObjectNotValidException(name + " is not a link"); } private void createRemergedFile(LightRemoteInode lrf, RemoteSlave rslave, boolean collision) throws FileExistsException, FileNotFoundException { String name = lrf.getName(); if (collision) { name = lrf.getName() + ".collision." + rslave.getName(); rslave.simpleRename(getPath() + lrf.getPath(), getPath(), name); } FileHandle newFile = createFileUnchecked(name, "drftpd", "drftpd", rslave, lrf.lastModified(), true, lrf.length()); newFile.setCheckSum(0); if (rslave.remergeChecksums() && lrf.length() != 0L) { rslave.putCRCQueue(newFile); } } public void collisionHandler(LightRemoteInode lrf, RemoteSlave rslave) { rslave.simpleDelete(getPath() + lrf.getPath()); } public void remerge(List<LightRemoteInode> files, RemoteSlave rslave, long lastModified) throws IOException { Iterator<LightRemoteInode> sourceIter = files.iterator(); // source comes pre-sorted from the slave List<InodeHandle> destinationList = null; try { destinationList = new ArrayList<>(getInodeHandlesUnchecked()); } catch (FileNotFoundException e) { try { // create directory for merging getParent().createDirectoryRecursive(getName(), true); } catch (FileExistsException e1) { // Can happen if another slave is remerging the same directory and // that thread created the dir between this thread checking and // not finding the dir and trying to create it. } // lets try this again, this time, if it doesn't work, we throw an // IOException up the chain destinationList = new ArrayList<>(getInodeHandlesUnchecked()); } try { // Update the last modified on the dir, this allows us to get a correct // timestamp on higher level dirs created recursively when remerging a // lower level. Additionally if the same dir exists on multiple slaves it // ensures we use the latest timestamp for the dir from all slaves in the // VFS compareAndUpdateLastModified(lastModified); } catch (FileNotFoundException e) { // Not sure this should be able to happen, for now log an error logger.error("Directory not found but was there a second ago!", e); } destinationList.sort(VirtualFileSystem.INODE_HANDLE_CASE_INSENSITIVE_COMPARATOR); Iterator<InodeHandle> destinationIter = destinationList.iterator(); LightRemoteInode source = null; InodeHandle destination = null; if (sourceIter.hasNext()) { source = sourceIter.next(); } if (destinationIter.hasNext()) { destination = destinationIter.next(); } while (true) { /*logger.debug("loop, [destination=" + (destination == null ? "null" : destination.getName()) + "][source=" + (source == null ? "null" : source.getName()) + "]"); */ // source & destination are set at the "next to process" one OR are // null and at the end of that list // case1 : source list is out, remove slave from all remaining // files/directories if (source == null) { while (destination != null) { // can removeSlave()'s from all types of Inodes, no type // checking needed destination.removeSlave(rslave); if (destinationIter.hasNext()) { destination = destinationIter.next(); } else { destination = null; } } // all done, both lists are empty return; } // case2: destination list is out, add files if (destination == null) { while (source != null) { if (source.isFile()) { try { createRemergedFile(source, rslave, false); } catch (FileExistsException e) { // File created by another slaves thread since this thread // listed the directory, just need to add this slave to the // list for the file try { getFileUnchecked(source.getName()).addSlave(rslave); } catch (ObjectNotValidException e1) { // File has collided with a dir/link in VFS, create this // as a collision if (collisionhandle()) { collisionHandler(source, rslave); } else { try { createRemergedFile(source, rslave, true); } catch (FileExistsException e2) { } continue; } } } } else { throw new IOException( source.getName() + " from slave " + rslave.getName() + " isDirectory() -- this shouldn't happen, this directory should already be created through a previous remerge process"); } if (sourceIter.hasNext()) { source = sourceIter.next(); } else { source = null; } } // all done, both lists are empty return; } // both source and destination are non-null // we don't know which one is first alphabetically int compare = source.getName().compareToIgnoreCase( destination.getName()); // compare is < 0, source comes before destination // compare is > 0, source comes after destination // compare is == 0, they have the same name if (compare < 0) { if (source.isFile()) { // add the file try { createRemergedFile(source, rslave, false); } catch (FileExistsException e) { // File created by another slaves thread since this thread // listed the directory, just need to add this slave to the // list for the file try { getFileUnchecked(source.getName()).addSlave(rslave); } catch (ObjectNotValidException e1) { // File has collided with a dir/link in VFS, create this // as a collision if (collisionhandle()) { collisionHandler(source, rslave); } else { createRemergedFile(source, rslave, true); } } } } else { throw new IOException( source.getName() + " from slave " + rslave.getName() + " isDirectory() -- this shouldn't happen, this directory should already be created through a previous remerge process"); } // advance one runner if (sourceIter.hasNext()) { source = sourceIter.next(); } else { source = null; } } else if (compare > 0) { // remove the slave destination.removeSlave(rslave); // advance one runner if (destinationIter.hasNext()) { destination = destinationIter.next(); } else { destination = null; } } else if (compare == 0) { if (destination.isLink()) { // this is bad, links don't exist on slaves // name collision if (source.isFile()) { logger.warn("In remerging {}, a file on the slave ({}" + VirtualFileSystem.separator + "{}) collided with a link on the master", rslave.getName(), getPath(), source.getName()); // set crc now? if (collisionhandle()) { collisionHandler(source, rslave); } else { createRemergedFile(source, rslave, true); } } else { // source.isDirectory() // Nothing to worry about // Just log it for your info and move on logger.warn("In remerging {}, a directory on the slave ({}" + VirtualFileSystem.separator + "{}) collided with a link on the master", rslave.getName(), getPath(), source.getName()); } } else if (source.isFile() && destination.isFile()) { // both files FileHandle destinationFile = (FileHandle) destination; long destinationCRC; try { destinationCRC = destinationFile.getCheckSumCached(); } catch (FileNotFoundException e) { destinationCRC = 0L; } if (rslave.remergeChecksums() && destinationCRC == 0L && source.length() != 0L && source.length() == destinationFile.getSize()) { // source file and dest file same size but no crc found in vfs, get crc from slave rslave.putCRCQueue(destinationFile); } if (source.length() != destinationFile.getSize()) { // handle collision Set<RemoteSlave> rslaves = destinationFile.getSlaves(); if (rslaves.contains(rslave) && rslaves.size() == 1) { // size of the file has changed, but since this is the only slave with the file, just change the size destinationFile.setSize(source.length()); } else { if (rslaves.contains(rslave)) { // the master thought the slave had the file, it's not the same size anymore, remove it destinationFile.removeSlave(rslave); } logger.warn("In remerging {}, a file on the slave ({}" + VirtualFileSystem.separator + "{}) collided with a file on the master", rslave.getName(), getPath(), source.getName()); if (collisionhandle()) { collisionHandler(source, rslave); } else { createRemergedFile(source, rslave, true); } } } else { destinationFile.addSlave(rslave); } } else if (source.isDirectory() && destination.isDirectory()) { // this is good, do nothing other than take up this case } else { // we have a directory/name collision, let's find which one // :) if (source.isDirectory()) { // & destination.isFile() // we don't care about directories on the slaves, let's // just skip it logger.warn("In remerging {}, a directory on the slave ({}" + VirtualFileSystem.separator + "{}) collided with a file on the master", rslave.getName(), getPath(), source.getName()); } else { // source.isFile() && destination.isDirectory() // handle collision if (collisionhandle()) { collisionHandler(source, rslave); } else { createRemergedFile(source, rslave, true); } // set crc now? } } // advance both runners, they were equal if (destinationIter.hasNext()) { destination = destinationIter.next(); } else { destination = null; } if (sourceIter.hasNext()) { source = sourceIter.next(); } else { source = null; } } } } /** * Shortcut to create "owner-less" directories. * * @param name * @return the created directory * @throws FileExistsException * @throws FileNotFoundException */ public DirectoryHandle createDirectorySystem(String name) throws FileExistsException, FileNotFoundException { return createDirectorySystem(name, false); } /** * Shortcut to create "owner-less" directories. * * @param name * @param placeHolderLastModified * @return the created directory * @throws FileExistsException * @throws FileNotFoundException */ protected DirectoryHandle createDirectorySystem(String name, boolean placeHolderLastModified) throws FileExistsException, FileNotFoundException { return createDirectoryUnchecked(name, "drftpd", "drftpd", placeHolderLastModified); } /** * Given a DirectoryHandle, it makes sure that this directory and all of its parent(s) exist * * @param name * @throws FileExistsException * @throws FileNotFoundException */ public void createDirectoryRecursive(String name) throws FileExistsException, FileNotFoundException { createDirectoryRecursive(name, false); } /** * Given a DirectoryHandle, it makes sure that this directory and all of its parent(s) exist * * @param name * @param placeHolderLastModified * @throws FileExistsException * @throws FileNotFoundException */ public void createDirectoryRecursive(String name, boolean placeHolderLastModified) throws FileExistsException, FileNotFoundException { DirectoryHandle dir = null; try { dir = createDirectorySystem(name, placeHolderLastModified); } catch (FileNotFoundException e) { getParent().createDirectoryRecursive(getName(), placeHolderLastModified); } catch (FileExistsException e) { throw new FileExistsException("Object already exists -- " + getPath() + name); } if (dir == null) { dir = createDirectorySystem(name, placeHolderLastModified); } logger.debug("Created directory {}", dir); } /** * Creates a Directory object in the FileSystem with this directory as its parent.<br> * This method does not check for permissions, so be careful while using it.<br> * * @param name * @param user * @param group * @return the created directory. * @throws FileNotFoundException * @throws FileExistsException */ public DirectoryHandle createDirectoryUnchecked(String name, String user, String group) throws FileExistsException, FileNotFoundException { return createDirectoryUnchecked(name, user, group, false); } /** * Creates a Directory object in the FileSystem with this directory as its parent.<br> * This method does not check for permissions, so be careful while using it.<br> * * @param name * @param user * @param group * @param placeHolderLastModified * @return the created directory. * @throws FileNotFoundException * @throws FileExistsException */ protected DirectoryHandle createDirectoryUnchecked(String name, String user, String group, boolean placeHolderLastModified) throws FileExistsException, FileNotFoundException { getInode().createDirectory(name, user, group, placeHolderLastModified); try { return getDirectoryUnchecked(name); } catch (FileNotFoundException e) { throw new RuntimeException("Something really funky happened, we just created it", e); } catch (ObjectNotValidException e) { throw new RuntimeException("Something really funky happened, we just created it", e); } } /** * Attempts to create a Directory in the FileSystem with this directory as parent. * * @param user * @param name * @return the created directory. * @throws PermissionDeniedException if the given user is not allowed to create dirs. * @throws FileExistsException * @throws FileNotFoundException */ public DirectoryHandle createDirectory(User user, String name) throws PermissionDeniedException, FileExistsException, FileNotFoundException { if (user == null) { throw new PermissionDeniedException("User cannot be null"); } DirectoryHandle newDir = getNonExistentDirectoryHandle(name); checkHiddenPath(newDir, user); if (!getVFSPermissions().checkPathPermission("makedir", user, newDir)) { throw new PermissionDeniedException("You are not allowed to create a directory at " + newDir.getParent()); } return createDirectoryUnchecked(name, user.getName(), user.getGroup().getName()); } /** * Creates a File object in the FileSystem with this directory as its parent.<br> * This method does not check for permissions, so be careful while using it.<br> * * @param name * @param user * @param group * @param initialSlave * @return the created file. * @throws FileExistsException * @throws FileNotFoundException */ public FileHandle createFileUnchecked(String name, String user, String group, RemoteSlave initialSlave) throws FileExistsException, FileNotFoundException { return createFileUnchecked(name, user, group, initialSlave, 0L, false, 0L); } /** * Creates a File object in the FileSystem with this directory as its parent.<br> * This method does not check for permissions, so be careful while using it.<br> * * @param name * @param user * @param group * @param initialSlave * @param lastModified * @param setLastModified * @param size * @return the created file. * @throws FileExistsException * @throws FileNotFoundException */ protected FileHandle createFileUnchecked(String name, String user, String group, RemoteSlave initialSlave, long lastModified, boolean setLastModified, long size) throws FileExistsException, FileNotFoundException { getInode().createFile(name, user, group, initialSlave.getName(), lastModified, setLastModified, size); try { return getFileUnchecked(name); } catch (FileNotFoundException e) { throw new RuntimeException("Something really funky happened, we just created it", e); } catch (ObjectNotValidException e) { throw new RuntimeException("Something really funky happened, we just created it", e); } } /** * Attempts to create a File in the FileSystem having this directory as parent. * * @param user * @param name * @param initialSlave * @return * @throws PermissionDeniedException if the user is not allowed to create a file in this dir. * @throws FileExistsException * @throws FileNotFoundException */ public FileHandle createFile(User user, String name, RemoteSlave initialSlave) throws PermissionDeniedException, FileExistsException, FileNotFoundException { if (user == null) { throw new PermissionDeniedException("User cannot be null"); } checkHiddenPath(this, user); if (!getVFSPermissions().checkPathPermission("upload", user, getNonExistentFileHandle(name))) { throw new PermissionDeniedException("You are not allowed to upload to " + getPath()); } return createFileUnchecked(name, user.getName(), user.getGroup().getName(), initialSlave); } /** * Creates a Link object in the FileSystem with this directory as its parent */ public LinkHandle createLinkUnchecked(String name, String target, String user, String group) throws FileExistsException, FileNotFoundException { getInode().createLink(name, target, user, group); try { return getLinkUnchecked(name); } catch (FileNotFoundException e) { throw new RuntimeException("Something really funky happened, we just created it", e); } catch (ObjectNotValidException e) { throw new RuntimeException("Something really funky happened, we just created it", e); } } public LinkHandle createLink(User user, String name, String target) throws FileExistsException, FileNotFoundException, PermissionDeniedException { if (user == null) { throw new PermissionDeniedException("User cannot be null"); } // check if this dir is hidden. checkHiddenPath(this, user); InodeHandle inode = getInodeHandle(target, user); // check if the target is hidden checkHiddenPath(inode, user); if (inode.isLink()) { throw new PermissionDeniedException("Impossible to point a link to a link"); } return createLinkUnchecked(name, target, user.getName(), user.getGroup().getName()); } public boolean isRoot() { return equals(GlobalContext.getGlobalContext().getRoot()); } /** * For use during PRET * Returns a FileHandle for a possibly non-existant directory in this path * No verification to its existence is made * * @return */ public FileHandle getNonExistentFileHandle(String argument) { if (argument.startsWith(VirtualFileSystem.separator)) { // absolute path, easy to handle return new FileHandle(argument); } // path must be relative return new FileHandle(getPath() + VirtualFileSystem.separator + argument); } public void removeSlave(RemoteSlave rslave) throws FileNotFoundException { if (getInode().getRefCountForSlave(rslave.getName()) == 0) { return; } boolean empty = isEmptyUnchecked(); for (InodeHandle inode : getInodeHandlesUnchecked()) { inode.removeSlave(rslave); } if (!empty && isEmptyUnchecked()) { // if it wasn't empty before, but is now, delete it deleteUnchecked(); } } public boolean isEmptyUnchecked() throws FileNotFoundException { return getInodeHandlesUnchecked().size() == 0; } public boolean isEmpty(User user) throws FileNotFoundException, PermissionDeniedException { // let's fetch the list of existent files inside this dir // if the dir does not exist, FileNotFoundException is thrown // if the dir exists the operation continues smoothly. getInode(); try { checkHiddenPath(this, user); } catch (FileNotFoundException e) { // either a race condition happened or the dir is hidden // cuz we just checked and the dir was here. throw new PermissionDeniedException("Unable to check if the directory is empty."); } return isEmptyUnchecked(); } @Override public boolean isDirectory() { return true; } @Override public boolean isFile() { return false; } @Override public boolean isLink() { return false; } @Override public void deleteUnchecked() throws FileNotFoundException { abortAllTransfers("Directory " + getPath() + " is being deleted"); GlobalContext.getGlobalContext().getSlaveManager().deleteOnAllSlaves(this); super.deleteUnchecked(); } public long validateSizeRecursive() throws FileNotFoundException { Set<InodeHandle> inodes = getInodeHandlesUnchecked(); long newSize = 0; long oldSize = getSize(); for (InodeHandle inode : inodes) { if (inode.isDirectory()) { ((DirectoryHandle) inode).validateSizeRecursive(); } newSize += inode.getSize(); } getInode().setSize(newSize); return oldSize - newSize; } protected void compareAndUpdateLastModified(long lastModified) throws FileNotFoundException { getInode().compareAndUpdateLastModified(lastModified); } public void recalcSlaveRefCounts() throws FileNotFoundException { getInode().recalcSlaveRefCounts(); } }