clone only 'executable' permissions during copying to avoid read-only files in the output (IDEA-133984)

This commit is contained in:
nik
2015-01-19 18:04:30 +03:00
parent cda9dc2173
commit 90a236e3f3
2 changed files with 76 additions and 23 deletions
@@ -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;
@@ -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());
}
}