diff --git a/platform/util/src/com/intellij/openapi/util/io/FileSystemUtil.java b/platform/util/src/com/intellij/openapi/util/io/FileSystemUtil.java index cbcf77d90fe6..20e41ed39aad 100644 --- a/platform/util/src/com/intellij/openapi/util/io/FileSystemUtil.java +++ b/platform/util/src/com/intellij/openapi/util/io/FileSystemUtil.java @@ -56,7 +56,7 @@ public class FileSystemUtil { @Nullable protected abstract String resolveSymLink(@NotNull String path) throws Exception; - protected boolean clonePermissions(@NotNull String source, @NotNull String target) throws Exception { return false; } + protected boolean clonePermissions(@NotNull String source, @NotNull String target, boolean onlyPermissionsToExecute) throws Exception { return false; } @NotNull private String getName() { return getClass().getSimpleName().replace("MediatorImpl", ""); } @@ -196,7 +196,21 @@ public class FileSystemUtil { */ public static boolean clonePermissions(@NotNull String source, @NotNull String target) { try { - return ourMediator.clonePermissions(source, target); + return ourMediator.clonePermissions(source, target, false); + } + catch (Exception e) { + LOG.warn(e); + return false; + } + } + + /** + * Gives the second file permissions to execute of the first one if possible; returns true if succeed. + * Will do nothing on Windows. + */ + public static boolean clonePermissionsToExecute(@NotNull String source, @NotNull String target) { + try { + return ourMediator.clonePermissions(source, target, true); } catch (Exception e) { LOG.warn(e); @@ -303,22 +317,44 @@ public class FileSystemUtil { } @Override - protected boolean clonePermissions(@NotNull String source, @NotNull String target) throws Exception { + protected boolean clonePermissions(@NotNull String source, @NotNull String target, boolean onlyPermissionsToExecute) throws Exception { if (SystemInfo.isUnix) { Object sourcePath = myGetPath.invoke(myDefaultFileSystem, source, ArrayUtil.EMPTY_STRING_ARRAY); Object targetPath = myGetPath.invoke(myDefaultFileSystem, target, ArrayUtil.EMPTY_STRING_ARRAY); - Map attributes = (Map)myReadAttributes.invoke(null, sourcePath, "posix:permissions", myLinkOptions); - if (attributes != null) { - Object permissions = attributes.get("permissions"); - if (permissions instanceof Collection) { - mySetAttribute.invoke(null, targetPath, "posix:permissions", permissions, myLinkOptions); - return true; + Collection sourcePermissions = getPermissions(sourcePath); + if (sourcePermissions != null) { + Collection permissionsToSet; + if (onlyPermissionsToExecute) { + Collection targetPermissions = getPermissions(targetPath); + permissionsToSet = new HashSet(); + for (Object permission : targetPermissions) { + if (!permission.toString().endsWith("_EXECUTE")) { + permissionsToSet.add(permission); + } + } + for (Object permission : sourcePermissions) { + if (permission.toString().endsWith("_EXECUTE")) { + permissionsToSet.add(permission); + } + } } + else { + permissionsToSet = sourcePermissions; + } + mySetAttribute.invoke(null, targetPath, "posix:permissions", permissionsToSet, myLinkOptions); + return true; } } return false; } + + private Collection getPermissions(Object sourcePath) throws IllegalAccessException, InvocationTargetException { + Map attributes = (Map)myReadAttributes.invoke(null, sourcePath, "posix:permissions", myLinkOptions); + if (attributes == null) return null; + Object permissions = attributes.get("permissions"); + return permissions instanceof Collection ? (Collection)permissions : null; + } } @@ -347,6 +383,7 @@ public class FileSystemUtil { int S_IFREG = 0100000; // regular file int S_IFDIR = 0040000; // directory int PERM_MASK = 0777; + int EXECUTE_MASK = 0111; int WRITE_MASK = 0222; int W_OK = 2; // write permission flag for access(2) @@ -397,14 +434,13 @@ public class FileSystemUtil { int res = SystemInfo.isLinux ? myLibC.__lxstat64(0, path, buffer) : myLibC.lstat(path, buffer); if (res != 0) return null; - int mode = (SystemInfo.isLinux ? buffer.getInt(myOffsets[OFF_MODE]) : buffer.getShort(myOffsets[OFF_MODE])) & LibC.S_MASK; + int mode = getModeFlags(buffer) & LibC.S_MASK; boolean isSymlink = (mode & LibC.S_IFLNK) == LibC.S_IFLNK; if (isSymlink) { - res = SystemInfo.isLinux ? myLibC.__xstat64(0, path, buffer) : myLibC.stat(path, buffer); - if (res != 0) { + if (!loadFileStatus(path, buffer)) { return FileAttributes.BROKEN_SYMLINK; } - mode = (SystemInfo.isLinux ? buffer.getInt(myOffsets[OFF_MODE]) : buffer.getShort(myOffsets[OFF_MODE])) & LibC.S_MASK; + mode = getModeFlags(buffer) & LibC.S_MASK; } boolean isDirectory = (mode & LibC.S_IFDIR) == LibC.S_IFDIR; @@ -419,6 +455,10 @@ public class FileSystemUtil { return new FileAttributes(isDirectory, isSpecial, isSymlink, false, size, mTime, writable); } + private boolean loadFileStatus(@NotNull String path, Memory buffer) { + return (SystemInfo.isLinux ? myLibC.__xstat64(0, path, buffer) : myLibC.stat(path, buffer)) == 0; + } + @Override protected String resolveSymLink(@NotNull final String path) throws Exception { try { @@ -435,15 +475,25 @@ public class FileSystemUtil { } @Override - protected boolean clonePermissions(@NotNull String source, @NotNull String target) throws Exception { + protected boolean clonePermissions(@NotNull String source, @NotNull String target, boolean onlyPermissionsToExecute) throws Exception { Memory buffer = new Memory(256); - int res = SystemInfo.isLinux ? myLibC.__xstat64(0, source, buffer) : myLibC.stat(source, buffer); - if (res == 0) { - int permissions = (SystemInfo.isLinux ? buffer.getInt(myOffsets[OFF_MODE]) : buffer.getShort(myOffsets[OFF_MODE])) & LibC.PERM_MASK; - return myLibC.chmod(target, permissions) == 0; - } + if (!loadFileStatus(source, buffer)) return false; - return false; + int permissions; + int sourcePermissions = getModeFlags(buffer) & LibC.PERM_MASK; + if (onlyPermissionsToExecute) { + if (!loadFileStatus(target, buffer)) return false; + int targetPermissions = getModeFlags(buffer) & LibC.PERM_MASK; + permissions = targetPermissions & ~LibC.EXECUTE_MASK | sourcePermissions & LibC.EXECUTE_MASK; + } + else { + permissions = sourcePermissions; + } + return myLibC.chmod(target, permissions) == 0; + } + + private int getModeFlags(Memory buffer) { + return SystemInfo.isLinux ? buffer.getInt(myOffsets[OFF_MODE]) : buffer.getShort(myOffsets[OFF_MODE]); } private boolean ownFile(Memory buffer) { @@ -510,11 +560,14 @@ public class FileSystemUtil { } @Override - protected boolean clonePermissions(@NotNull String source, @NotNull String target) throws Exception { + protected boolean clonePermissions(@NotNull String source, @NotNull String target, boolean onlyPermissionsToExecute) throws Exception { if (SystemInfo.isUnix) { File srcFile = new File(source); File dstFile = new File(target); - return dstFile.setWritable(srcFile.canWrite(), true) && dstFile.setExecutable(srcFile.canExecute(), true); + if (!onlyPermissionsToExecute) { + if (!dstFile.setWritable(srcFile.canWrite(), true)) return false; + } + return dstFile.setExecutable(srcFile.canExecute(), true); } return false; diff --git a/platform/util/src/com/intellij/openapi/util/io/FileUtil.java b/platform/util/src/com/intellij/openapi/util/io/FileUtil.java index a747727cfbc3..419c1bbfe14c 100644 --- a/platform/util/src/com/intellij/openapi/util/io/FileUtil.java +++ b/platform/util/src/com/intellij/openapi/util/io/FileUtil.java @@ -497,7 +497,7 @@ public class FileUtil extends FileUtilRt { } if (SystemInfo.isUnix && fromFile.canExecute()) { - FileSystemUtil.clonePermissions(fromFile.getPath(), toFile.getPath()); + FileSystemUtil.clonePermissionsToExecute(fromFile.getPath(), toFile.getPath()); } }