/** * Get the container which security callback affects this item. This searches up the path of * parents to see wether it can find any container with a callback. If no callback can be found, * null is returned. * * @param vfsItem * @return */ public static VFSContainer findInheritingSecurityCallbackContainer(VFSItem vfsItem) { if (vfsItem == null) return null; // first resolve delegates of any NamedContainers to get the actual container (might be a // MergeSource) if (vfsItem instanceof NamedContainerImpl) return findInheritingSecurityCallbackContainer(((NamedContainerImpl) vfsItem).getDelegate()); // special treatment for MergeSource if (vfsItem instanceof MergeSource) { MergeSource mergeSource = (MergeSource) vfsItem; VFSContainer rootWriteContainer = mergeSource.getRootWriteContainer(); if (rootWriteContainer != null && rootWriteContainer.getLocalSecurityCallback() != null) { // if the root write container has a security callback set, it will always override // any local securitycallback set on the mergesource return rootWriteContainer; } else if (mergeSource.getLocalSecurityCallback() != null) { return mergeSource; } else if (mergeSource.getParentContainer() != null) { return findInheritingSecurityCallbackContainer(mergeSource.getParentContainer()); } } else { if ((vfsItem instanceof VFSContainer) && (vfsItem.getLocalSecurityCallback() != null)) return (VFSContainer) vfsItem; if (vfsItem.getParentContainer() != null) return findInheritingSecurityCallbackContainer(vfsItem.getParentContainer()); } return null; }
/** * Check the quota usage on this VFSContainer. If no security callback is provided, this returns * -1 (meaning no quota on this folder). Similarly, if no quota is defined, * VFSSecurityCallback.NO_QUOTA_DEFINED will be returned to signal no quota on this container. * * @param securityCallback * @param container * @return */ public static long getQuotaLeftKB(VFSContainer container) { VFSContainer inheritingItem = findInheritingSecurityCallbackContainer(container); if (inheritingItem == null || inheritingItem.getLocalSecurityCallback().getQuota() == null) return Quota.UNLIMITED; long usageKB = getUsageKB(inheritingItem); return inheritingItem.getLocalSecurityCallback().getQuota().getQuotaKB().longValue() - usageKB; }
/** * Check if descendant is child of parent or same as parent. * * @param descendant * @param root * @return */ public static boolean isSelfOrParent(VFSContainer descendant, VFSContainer parent) { if (parent.isSame(descendant)) return true; VFSContainer parentContainer = descendant.getParentContainer(); if (parentContainer != null && parentContainer.isSame(parent)) return true; return false; }
/** * Check if descendant is indeed a descendant of root.. * * @param parent * @param child * @return */ public static boolean isContainerDescendantOrSelf(VFSContainer descendant, VFSContainer root) { if (root.isSame(descendant)) return true; VFSContainer parentContainer = descendant.getParentContainer(); while (parentContainer != null) { if (parentContainer.isSame(root)) return true; parentContainer = parentContainer.getParentContainer(); } return false; }
/** * Resolves a file path in the base container or creates this file under the given path. The * method creates any missing directories. * * @param baseContainer The base directory. User must have write permissions on this container * @param relFilePath The path relative to the base container. Must start with a '/'. To separate * sub directories use '/' * @return The resolved or created leaf or NULL if a problem happened */ public static VFSLeaf resolveOrCreateLeafFromPath( VFSContainer baseContainer, String relFilePath) { if (StringHelper.containsNonWhitespace(relFilePath)) { int lastSlash = relFilePath.lastIndexOf("/"); String relDirPath = relFilePath; String fileName = null; if (lastSlash == -1) { // relFilePath is the file name - no directories involved relDirPath = null; fileName = relFilePath; } else if (lastSlash == 0) { // Remove start slash from file name relDirPath = null; fileName = relFilePath.substring(1, relFilePath.length()); } else { relDirPath = relFilePath.substring(0, lastSlash); fileName = relFilePath.substring(lastSlash); } // Create missing directories and set parent dir for later file creation VFSContainer parent = baseContainer; if (StringHelper.containsNonWhitespace(relDirPath)) { parent = resolveOrCreateContainerFromPath(baseContainer, relDirPath); } // Now create file in that dir if (StringHelper.containsNonWhitespace(fileName)) { VFSLeaf leaf = null; VFSItem resolvedFile = parent.resolve(fileName); if (resolvedFile == null) { leaf = parent.createChildLeaf(fileName); if (leaf == null) { log.error( "Could not create leaf with relPath::" + relFilePath + " in base container::" + getRealPath(baseContainer), null); } } else { if (resolvedFile instanceof VFSLeaf) { leaf = (VFSLeaf) resolvedFile; } else { leaf = null; log.error( "Could not create relPath::" + relFilePath + ", a directory with this name exists (but not a file) in base container::" + getRealPath(baseContainer), null); } } return leaf; } } return null; }
public static VFSContainer getOrCreateContainer(VFSContainer parent, String name) { VFSItem item = parent.resolve(name); if (item instanceof VFSContainer) { return (VFSContainer) item; } else if (item != null) { return null; // problem } else { return parent.createChildContainer(name); } }
/** * Returns a similar but non existing file name in root based on the given name. * * @param root * @param name * @return A non existing name based on the given name in the root directory */ public static String similarButNonExistingName(VFSContainer root, String name) { VFSItem existingItem = null; String newName = name; existingItem = root.resolve(newName); int i = 1; while (existingItem != null) { newName = FileUtils.appendNumberAtTheEndOfFilename(name, i++); existingItem = root.resolve(newName); } return newName; }
/** * @param container * @param filename * @return */ public static String rename(VFSContainer container, String filename) { String newName = filename; VFSItem newFile = container.resolve(newName); for (int count = 0; newFile != null && count < 999; ) { count++; newName = FileUtils.appendNumberAtTheEndOfFilename(filename, count); newFile = container.resolve(newName); } if (newFile == null) { return newName; } return null; }
/** @see org.olat.core.util.vfs.VFSItem#resolveFile(java.lang.String) */ public static VFSItem resolveFile(VFSContainer rootContainer, String path) { path = VFSManager.sanitizePath(path); if (path.equals("/")) { // slash or empty path -> return this vfsitem return rootContainer; } // The following code block eliminates directory scans on file-systems, // which are done in the original code in the next block, which is left // there as a fall-back in case there are non-file-system implementations // of OLAT-VFS. // OLAT file-systems can be very large and directories can contain // quite numerous files. Scanning these can take a lot of time. // Just put together the paths of both arguments // and ask the file-system whether such an entry // exists. If yes, this entry must be exactly what is // to be returned as, the proper type of, VFSItem. if (rootContainer instanceof LocalFolderImpl) { String childName = extractChild(path); LocalFolderImpl l = (LocalFolderImpl) rootContainer; File t = new File(l.getBasefile().getAbsolutePath(), childName); if (t.exists()) { String bcroot = FolderConfig.getCanonicalRoot(); String fsPath = t.getAbsolutePath(); if (t.isDirectory()) { VFSContainer subContainer; if (fsPath.startsWith(bcroot)) { fsPath = fsPath.substring(bcroot.length(), fsPath.length()); subContainer = new OlatRootFolderImpl(fsPath, rootContainer); } else { subContainer = new LocalFolderImpl(t, rootContainer); } String subPath = path.substring(childName.length() + 1); return resolveFile(subContainer, subPath); } else { if (fsPath.startsWith(bcroot)) { fsPath = fsPath.replace(bcroot, ""); return new OlatRootFileImpl(fsPath, rootContainer); } else { return new LocalFileImpl(t, rootContainer); } } } else { return null; } } // leave original code block as fall-back for non-file-system-based implementations String childName = extractChild(path); List<VFSItem> children = rootContainer.getItems(); for (VFSItem child : children) { String curName = child.getName(); if (childName.equals(curName)) { // found , let child further resolve if needed return child.resolve(path.substring(childName.length() + 1)); } } return null; }
/** * Check wether this container has a quota assigned to itself. * * @param container * @return Quota if this container has a Quota assigned, null otherwise. */ public static Quota isTopLevelQuotaContainer(VFSContainer container) { VFSSecurityCallback callback = container.getLocalSecurityCallback(); if (callback != null && callback.getQuota() != null) return callback.getQuota(); // extract delegate if this is a NamedContainer instance... if (container instanceof NamedContainerImpl) container = ((NamedContainerImpl) container).getDelegate(); // check if this is a MergeSource with a root write container if (container instanceof MergeSource) { VFSContainer rwContainer = ((MergeSource) container).getRootWriteContainer(); if (rwContainer != null && rwContainer.getLocalSecurityCallback() != null && rwContainer.getLocalSecurityCallback().getQuota() != null) return rwContainer.getLocalSecurityCallback().getQuota(); } return null; }
/** * Copy the content of the source container to the target container. * * @param source * @param target * @return */ public static boolean copyContent(VFSContainer source, VFSContainer target) { if (!source.exists()) { return false; } if (isSelfOrParent(source, target)) { return false; } if (source instanceof NamedContainerImpl) { source = ((NamedContainerImpl) source).getDelegate(); } if (target instanceof NamedContainerImpl) { target = ((NamedContainerImpl) target).getDelegate(); } if (source instanceof LocalImpl && target instanceof LocalImpl) { LocalImpl localSource = (LocalImpl) source; LocalImpl localTarget = (LocalImpl) target; File localSourceFile = localSource.getBasefile(); File localTargetFile = localTarget.getBasefile(); return FileUtils.copyDirContentsToDir(localSourceFile, localTargetFile, false, "VFScopyDir"); } return false; }
/** @see org.olat.core.util.vfs.VFSItem#canRename() */ public VFSStatus canRename() { VFSContainer inheritingContainer = VFSManager.findInheritingSecurityCallbackContainer(this); if (inheritingContainer != null && !inheritingContainer.getLocalSecurityCallback().canWrite()) return VFSConstants.NO_SECURITY_DENIED; return VFSConstants.YES; }
private static ContainerAndFile findWritableRootFolderForRecursion( VFSContainer rootDir, String relFilePath, int recursionLevel) { recursionLevel++; if (recursionLevel > 20) { // Emergency exit condition: a directory hierarchy that has more than 20 // levels? Probably not.. log.warn( "Reached recursion level while finding writable root Folder - most likely a bug. rootDir::" + rootDir + " relFilePath::" + relFilePath); return null; } if (rootDir instanceof NamedContainerImpl) { rootDir = ((NamedContainerImpl) rootDir).getDelegate(); } if (rootDir instanceof MergeSource) { MergeSource mergedDir = (MergeSource) rootDir; // first check if the next level is not a second MergeSource int stop = relFilePath.indexOf("/", 1); if (stop > 0) { String nextLevel = extractChild(relFilePath); VFSItem item = mergedDir.resolve(nextLevel); if (item instanceof NamedContainerImpl) { item = ((NamedContainerImpl) item).getDelegate(); } if (item instanceof MergeSource) { rootDir = (MergeSource) item; relFilePath = relFilePath.substring(stop); return findWritableRootFolderForRecursion(rootDir, relFilePath, recursionLevel); } } VFSContainer rootWriteContainer = mergedDir.getRootWriteContainer(); if (rootWriteContainer == null) { // we have a merge source without a write container, try it one higher, // go through all children of this one and search the correct child in // the path List<VFSItem> children = rootDir.getItems(); if (children.isEmpty()) { // ups, a merge source without children, no good, return null return null; } String nextChildName = relFilePath.substring(1, relFilePath.indexOf("/", 1)); for (VFSItem child : children) { // look up for the next child in the path if (child.getName().equals(nextChildName)) { // use this child as new root and remove the child name from the rel // path if (child instanceof VFSContainer) { rootDir = (VFSContainer) child; relFilePath = relFilePath.substring(relFilePath.indexOf("/", 1)); break; } else { // ups, a merge source with a child that is not a VFSContainer - // no good, return null return null; } } } } else { // ok, we found a merge source with a write container rootDir = rootWriteContainer; } } if (rootDir != null && rootDir instanceof LocalFolderImpl) { // finished, we found a local folder we can use to write return new ContainerAndFile(rootDir, relFilePath); } else { // do recursion return findWritableRootFolderForRecursion(rootDir, relFilePath, recursionLevel); } }
/** * Get the path as string of the given item relative to the root container and the relative base * path * * @param item the item for which the relative path should be returned * @param rootContainer The root container for which the relative path should be calculated * @param relativeBasePath when NULL, the path will be calculated relative to the rootContainer; * when NOT NULL, the relativeBasePath must represent a relative path within the root * container that serves as the base. In this case, the calculated relative item path will * start from this relativeBasePath * @return */ public static String getRelativeItemPath( VFSItem item, VFSContainer rootContainer, String relativeBasePath) { // 1) Create path absolute to the root container if (item == null) return null; String absPath = ""; VFSItem tmpItem = item; // Check for merged containers to fix problems with named containers, see OLAT-3848 List<NamedContainerImpl> namedRootChilds = new ArrayList<NamedContainerImpl>(); for (VFSItem rootItem : rootContainer.getItems()) { if (rootItem instanceof NamedContainerImpl) { namedRootChilds.add((NamedContainerImpl) rootItem); } } // Check if root container is the same as the item and vice versa. It is // necessary to perform the check on both containers to catch all potential // cases with MergedSource and NamedContainer where the check in one // direction is not necessarily the same as the opposite check while (tmpItem != null && !rootContainer.isSame(tmpItem) && !tmpItem.isSame(rootContainer)) { String itemFileName = tmpItem.getName(); // fxdiff FXOLAT-125: virtual file system for CP if (tmpItem instanceof NamedLeaf) { itemFileName = ((NamedLeaf) tmpItem).getDelegate().getName(); } // Special case: check if this is a named container, see OLAT-3848 for (NamedContainerImpl namedRootChild : namedRootChilds) { if (namedRootChild.isSame(tmpItem)) { itemFileName = namedRootChild.getName(); } } absPath = "/" + itemFileName + absPath; tmpItem = tmpItem.getParentContainer(); if (tmpItem != null) { // test if this this is a merge source child container, see OLAT-5726 VFSContainer grandParent = tmpItem.getParentContainer(); if (grandParent instanceof MergeSource) { MergeSource mergeGrandParent = (MergeSource) grandParent; if (mergeGrandParent.isContainersChild((VFSContainer) tmpItem)) { // skip this parent container and use the merge grand-parent // instead, otherwise path contains the container twice tmpItem = mergeGrandParent; } } } } if (relativeBasePath == null) { return absPath; } // 2) Compute rel path to base dir of the current file // selpath = /a/irwas/subsub/nochsub/note.html 5 // filenam = /a/irwas/index.html 3 // --> subsub/nochsub/note.gif // or /a/irwas/bla/index.html // to /a/other/b/gugus.gif // --> ../../ other/b/gugus.gif // or /a/other/b/main.html // to /a/irwas/bla/goto.html // --> ../../ other/b/gugus.gif String base = relativeBasePath; // assume "/" is here if (!(base.indexOf("/") == 0)) { base = "/" + base; } String[] baseA = base.split("/"); String[] targetA = absPath.split("/"); int sp = 1; for (; sp < Math.min(baseA.length, targetA.length); sp++) { if (!baseA[sp].equals(targetA[sp])) { break; } } // special case: self-reference if (absPath.equals(base)) { sp = 1; } StringBuilder buffer = new StringBuilder(); for (int i = sp; i < baseA.length - 1; i++) { buffer.append("../"); } for (int i = sp; i < targetA.length; i++) { buffer.append(targetA[i] + "/"); } buffer.deleteCharAt(buffer.length() - 1); String path = buffer.toString(); String trimmed = path; // selectedPath.substring(1); return trimmed; }
/** * Resolves a directory path in the base container or creates this directory. The method creates * any missing directories. * * @param baseContainer The base directory. User must have write permissions on this container * @param relContainerPath The path relative to the base container. Must start with a '/'. To * separate sub directories use '/' * @return The resolved or created container or NULL if a problem happened */ public static VFSContainer resolveOrCreateContainerFromPath( VFSContainer baseContainer, String relContainerPath) { VFSContainer resultContainer = baseContainer; if (!VFSConstants.YES.equals(baseContainer.canWrite())) { log.error( "Could not create relPath::" + relContainerPath + ", base container::" + getRealPath(baseContainer) + " not writable", null); resultContainer = null; } else if (StringHelper.containsNonWhitespace(relContainerPath)) { // Try to resolve given rel path from current container VFSItem resolvedPath = baseContainer.resolve(relContainerPath.trim()); if (resolvedPath == null) { // Does not yet exist - create subdir String[] pathSegments = relContainerPath.split("/"); for (int i = 0; i < pathSegments.length; i++) { String segment = pathSegments[i].trim(); if (StringHelper.containsNonWhitespace(segment)) { resolvedPath = resultContainer.resolve(segment); if (resolvedPath == null) { resultContainer = resultContainer.createChildContainer(segment); if (resultContainer == null) { log.error( "Could not create container with name::" + segment + " in relPath::" + relContainerPath + " in base container::" + getRealPath(baseContainer), null); break; } } else { if (resolvedPath instanceof VFSContainer) { resultContainer = (VFSContainer) resolvedPath; } else { resultContainer = null; log.error( "Could not create container with name::" + segment + " in relPath::" + relContainerPath + ", a file with this name exists (but not a directory) in base container::" + getRealPath(baseContainer), null); break; } } } } } else { // Parent dir already exists, make sure this is really a container and not a file! if (resolvedPath instanceof VFSContainer) { resultContainer = (VFSContainer) resolvedPath; } else { resultContainer = null; log.error( "Could not create relPath::" + relContainerPath + ", a file with this name exists (but not a directory) in base container::" + getRealPath(baseContainer), null); } } } return resultContainer; }