diff --git a/platform/openide.filesystems/apichanges.xml b/platform/openide.filesystems/apichanges.xml index c21da31428ab..eb98834fa52e 100644 --- a/platform/openide.filesystems/apichanges.xml +++ b/platform/openide.filesystems/apichanges.xml @@ -25,6 +25,30 @@ Filesystems API + + + FileObject copy preserves source posix permissions. + + + + + + FileUtil.copyFile(...) now preserve ATTRIBUTES and POSIX permissions. + + + + + + FileUtil can convert FileObject to/from java.nio.file.Path. + + + + + + FileUtil has a toPath(FileObject fo) and toFileObject(Path path) utility methods. + + + Allow to filter or transform attribute values during copying. diff --git a/platform/openide.filesystems/manifest.mf b/platform/openide.filesystems/manifest.mf index 14c80d034d9b..201603422090 100644 --- a/platform/openide.filesystems/manifest.mf +++ b/platform/openide.filesystems/manifest.mf @@ -2,6 +2,6 @@ Manifest-Version: 1.0 OpenIDE-Module: org.openide.filesystems OpenIDE-Module-Localizing-Bundle: org/openide/filesystems/Bundle.properties OpenIDE-Module-Layer: org/openide/filesystems/resources/layer.xml -OpenIDE-Module-Specification-Version: 9.31 +OpenIDE-Module-Specification-Version: 9.32 diff --git a/platform/openide.filesystems/src/org/openide/filesystems/FileObject.java b/platform/openide.filesystems/src/org/openide/filesystems/FileObject.java index 66bc740c23a5..75876190398d 100644 --- a/platform/openide.filesystems/src/org/openide/filesystems/FileObject.java +++ b/platform/openide.filesystems/src/org/openide/filesystems/FileObject.java @@ -114,7 +114,8 @@ public abstract void rename(FileLock lock, String name, String ext) /** Copies this file. This allows the filesystem to perform any additional * operation associated with the copy. But the default implementation is simple - * copy of the file and its attributes + * copy of the file and its attributes Since version 9.32, the file POSIX + * permissions are copied as well. * * @param target target folder to move this file to * @param name new basename of file diff --git a/platform/openide.filesystems/src/org/openide/filesystems/FileUtil.java b/platform/openide.filesystems/src/org/openide/filesystems/FileUtil.java index 8aa969d5733e..59c85246900a 100644 --- a/platform/openide.filesystems/src/org/openide/filesystems/FileUtil.java +++ b/platform/openide.filesystems/src/org/openide/filesystems/FileUtil.java @@ -35,9 +35,11 @@ import java.net.URISyntaxException; import java.net.URL; import java.net.URLStreamHandler; +import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.attribute.PosixFilePermission; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -538,7 +540,8 @@ public static void copy(InputStream is, OutputStream os) } /** Copies file to the selected folder. - * This implementation simply copies the file by stream content. + * This implementation simply copies the file by stream content. Since version + * 9.32, the file POSIX permissions are copied as well. * @param source source file object * @param destFolder destination folder * @param newName file name (without extension) of destination file @@ -567,6 +570,7 @@ static FileObject copyFileImpl(FileObject source, FileObject destFolder, String } copy(bufIn, bufOut); + copyPosixPerms(source, dest); copyAttributes(source, dest); } finally { if (bufIn != null) { @@ -584,6 +588,17 @@ static FileObject copyFileImpl(FileObject source, FileObject destFolder, String return dest; } + + static void copyPosixPerms(FileObject source, FileObject dest) throws IOException { + Path src = toPath(source); + Path dst = toPath(dest); + if ((src != null) && (dst != null)) { + try { + Set perms = Files.getPosixFilePermissions(src); + Files.setPosixFilePermissions(dst, perms); + } catch (UnsupportedOperationException ex) {} + } + } // // public methods @@ -605,7 +620,9 @@ public static FileSystem createMemoryFileSystem() { } /** Copies file to the selected folder. - * This implementation simply copies the file by stream content. + * This implementation simply copies the file by stream content. Since version + * 9.32, the file POSIX permissions are copied as well. + * * @param source source file object * @param destFolder destination folder * @param newName file name (without extension) of destination file @@ -620,7 +637,8 @@ public static FileObject copyFile(FileObject source, FileObject destFolder, Stri } /** Copies file to the selected folder. - * This implementation simply copies the file by stream content. + * This implementation simply copies the file by stream content. Since version + * 9.32, the file POSIX permissions are copied as well. * Uses the extension of the source file. * @param source source file object * @param destFolder destination folder @@ -832,10 +850,22 @@ public static File toFile(FileObject fo) { assert assertNormalized(retVal, BaseUtilities.isMac()); // #240180 return retVal; } + + /** Finds appropriate java.nio.file.Path to FileObject if possible. + * If not possible then null is returned. + * This is the inverse operation of {@link #toFileObject}. + * @param fo FileObject whose corresponding Path will be looked for + * @return java.nio.file.Path or null if no corresponding File exists. + * @since 9.32 + */ + public static Path toPath(FileObject fo) { + File f = toFile(fo); + return f != null ? f.toPath() : null; + } /** * Converts a disk file to a matching file object. - * This is the inverse operation of {@link #toFile}. + * This is the inverse operation of {@link #toFile(org.openide.filesystems.FileObject) }. *

* If you are running with {@code org.netbeans.modules.masterfs} enabled, * this method should never return null for a file which exists on disk. @@ -896,7 +926,24 @@ public static FileObject toFileObject(File file) { } return retVal; } - + + /** + * Converts a Path to a FileObject if that is possible. It uses the + * {@link #toFileObject(java.io.File)} method with {@code path.toFile()}. + * if the conversion is not possible for some reason {@code null} is returned. + * + * @param path the {@link Path} to be converted + * @return the {@link FileObject} representing the {@code path} or {@code null} + * @since 9.32 + */ + public static FileObject toFileObject(Path path) { + try { + return toFileObject(path.toFile()); + } catch (UnsupportedOperationException ex) { + return null; + } + } + /** Finds appropriate FileObjects to java.io.File if possible. * If not possible then empty array is returned. More FileObjects may * correspond to one java.io.File that`s why array is returned. diff --git a/platform/openide.filesystems/test/unit/src/org/openide/filesystems/FileUtilTest.java b/platform/openide.filesystems/test/unit/src/org/openide/filesystems/FileUtilTest.java index 210b27f487fa..ff894afa57ec 100644 --- a/platform/openide.filesystems/test/unit/src/org/openide/filesystems/FileUtilTest.java +++ b/platform/openide.filesystems/test/unit/src/org/openide/filesystems/FileUtilTest.java @@ -24,18 +24,23 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URL; +import java.nio.file.Files; +import java.nio.file.attribute.PosixFilePermission; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; import junit.framework.Test; +import static org.junit.Assume.assumeFalse; import org.netbeans.junit.Log; import org.netbeans.junit.NbTestCase; import org.netbeans.junit.NbTestSuite; @@ -44,6 +49,7 @@ import org.openide.util.Lookup; import org.openide.util.RequestProcessor; import org.openide.util.BaseUtilities; +import org.openide.util.Utilities; import org.openide.util.test.MockLookup; /** @@ -704,6 +710,25 @@ public void close() throws SecurityException { assertTrue(result.toExternalForm().endsWith("/")); //NOI18N } + public void testCopyPosixPerms() throws Exception { + assumeFalse(Utilities.isWindows()); + + LocalFileSystem lfs = new LocalFileSystem(); + lfs.setRootDirectory(new File("/")); + FileObject workDir = lfs.findResource(getWorkDir().getAbsolutePath()); + clearWorkDir(); + + FileObject source = workDir.createData("original.file"); + Set perms = new HashSet<>(Files.getPosixFilePermissions(FileUtil.toPath(source))); + assertFalse(perms.contains(PosixFilePermission.OWNER_EXECUTE)); + perms.add(PosixFilePermission.OWNER_EXECUTE); + Files.setPosixFilePermissions(FileUtil.toPath(source), perms); + + FileObject dest = FileUtil.copyFile(source, workDir, "copied.file"); + perms = Files.getPosixFilePermissions(FileUtil.toPath(dest)); + assertTrue(perms.contains(PosixFilePermission.OWNER_EXECUTE)); + } + public void testCopyAttributes() throws Exception { FileObject testRoot = FileUtil.createMemoryFileSystem().getRoot(); FileObject aFile = testRoot.createData("a", "file");