Skip to content

Commit 975ccfc

Browse files
committed
[IO-751] When deleting symlinks, File/PathUtils.deleteDirectory()
changes file permissions of the target. POSIX: Deleting a file requires write and execute on the parent. Listing a file also requires reading. Looking for a fix to the Ubuntu failure in the GitHub builds.
1 parent e5cf469 commit 975ccfc

2 files changed

Lines changed: 57 additions & 76 deletions

File tree

src/main/java/org/apache/commons/io/file/PathUtils.java

Lines changed: 53 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@
5353
import java.time.Duration;
5454
import java.time.Instant;
5555
import java.time.chrono.ChronoZonedDateTime;
56-
import java.util.ArrayList;
5756
import java.util.Arrays;
5857
import java.util.Collection;
5958
import java.util.Collections;
@@ -68,7 +67,6 @@
6867

6968
import org.apache.commons.io.Charsets;
7069
import org.apache.commons.io.FileUtils;
71-
import org.apache.commons.io.IOExceptionList;
7270
import org.apache.commons.io.IOUtils;
7371
import org.apache.commons.io.UncheckedIOExceptions;
7472
import org.apache.commons.io.file.Counters.PathCounters;
@@ -512,7 +510,7 @@ public static PathCounters deleteFile(final Path file, final LinkOption[] linkOp
512510
pathCounts.getByteCounter().add(size);
513511
return pathCounts;
514512
}
515-
} catch (AccessDeniedException e) {
513+
} catch (final AccessDeniedException e) {
516514
// Ignore and try again below.
517515
}
518516
final Path parent = file.getParent();
@@ -538,7 +536,7 @@ public static PathCounters deleteFile(final Path file, final LinkOption[] linkOp
538536
}
539537

540538
private static <R> R withPosixFileAttributes(final Path path, final LinkOption[] linkOptions, final boolean overrideReadOnly,
541-
IOFunction<PosixFileAttributes, R> function) throws IOException {
539+
final IOFunction<PosixFileAttributes, R> function) throws IOException {
542540
final PosixFileAttributes posixFileAttributes = overrideReadOnly ? readPosixFileAttributes(path, linkOptions) : null;
543541
try {
544542
return function.apply(posixFileAttributes);
@@ -1305,67 +1303,53 @@ public static void setLastModifiedTime(final Path sourceFile, final Path targetF
13051303
* @since 2.8.0
13061304
*/
13071305
public static Path setReadOnly(final Path path, final boolean readOnly, final LinkOption... linkOptions) throws IOException {
1308-
final List<Exception> causeList = new ArrayList<>(3);
13091306
try {
13101307
// Windows is simplest
13111308
if (setDosReadOnly(path, readOnly, linkOptions)) {
13121309
return path;
13131310
}
13141311
} catch (final IOException e) {
1315-
// Remember and retry with POSIX
1316-
causeList.add(e);
1312+
// Retry with POSIX below.
13171313
}
13181314
// POSIX
13191315
if (readOnly) {
13201316
// RO
13211317
// File, then parent dir (if any).
1322-
final boolean set = setPosixReadOnlyFile(path, readOnly, linkOptions);
1318+
setPosixReadOnlyFile(path, readOnly, linkOptions);
13231319
setPosixDeletePermissions(path.getParent(), false, linkOptions);
1324-
if (set) {
1325-
return path;
1326-
}
13271320
} else {
13281321
// RE
13291322
// Parent dir (if any), then file.
1330-
if (setPosixDeletePermissions(path.getParent(), true, linkOptions)) {
1331-
return path;
1332-
}
1323+
setPosixDeletePermissions(path.getParent(), true, linkOptions);
13331324
}
1334-
IOExceptionList.checkEmpty(causeList, path);
1335-
throw new IOException(String.format("No DosFileAttributeView or PosixFileAttributeView for '%s' (linkOptions=%s)", path, Arrays.toString(linkOptions)));
1336-
}
1337-
1338-
private static boolean setPosixReadOnlyFile(final Path path, final boolean readOnly, final LinkOption... linkOptions) throws IOException {
1339-
final PosixFileAttributeView posixFileAttributeView = getPosixFileAttributeView(path, linkOptions);
1340-
if (posixFileAttributeView != null) {
1341-
// Not Windows 10
1342-
final PosixFileAttributes readAttributes = posixFileAttributeView.readAttributes();
1343-
final Set<PosixFilePermission> permissions = readAttributes.permissions();
1344-
// @formatter:off
1345-
final List<PosixFilePermission> readPermissions = Arrays.asList(
1346-
PosixFilePermission.OWNER_READ
1347-
//PosixFilePermission.GROUP_READ,
1348-
//PosixFilePermission.OTHERS_READ
1349-
);
1350-
final List<PosixFilePermission> writePermissions = Arrays.asList(
1351-
PosixFilePermission.OWNER_WRITE
1352-
//PosixFilePermission.GROUP_WRITE,
1353-
//PosixFilePermission.OTHERS_WRITE
1354-
);
1355-
// @formatter:on
1356-
if (readOnly) {
1357-
// RO: We can read, we cannot write.
1358-
permissions.addAll(readPermissions);
1359-
permissions.removeAll(writePermissions);
1360-
} else {
1361-
// Not RO: We can read, we can write.
1362-
permissions.addAll(readPermissions);
1363-
permissions.addAll(writePermissions);
1364-
}
1365-
Files.setPosixFilePermissions(path, permissions);
1366-
return true;
1325+
return path;
1326+
}
1327+
1328+
private static void setPosixReadOnlyFile(final Path path, final boolean readOnly, final LinkOption... linkOptions) throws IOException {
1329+
// Not Windows 10
1330+
final Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(path, linkOptions);
1331+
// @formatter:off
1332+
final List<PosixFilePermission> readPermissions = Arrays.asList(
1333+
PosixFilePermission.OWNER_READ
1334+
//PosixFilePermission.GROUP_READ,
1335+
//PosixFilePermission.OTHERS_READ
1336+
);
1337+
final List<PosixFilePermission> writePermissions = Arrays.asList(
1338+
PosixFilePermission.OWNER_WRITE
1339+
//PosixFilePermission.GROUP_WRITE,
1340+
//PosixFilePermission.OTHERS_WRITE
1341+
);
1342+
// @formatter:on
1343+
if (readOnly) {
1344+
// RO: We can read, we cannot write.
1345+
permissions.addAll(readPermissions);
1346+
permissions.removeAll(writePermissions);
1347+
} else {
1348+
// Not RO: We can read, we can write.
1349+
permissions.addAll(readPermissions);
1350+
permissions.addAll(writePermissions);
13671351
}
1368-
return false;
1352+
Files.setPosixFilePermissions(path, permissions);
13691353
}
13701354

13711355
private static boolean setDosReadOnly(final Path path, final boolean readOnly, final LinkOption... linkOptions) throws IOException {
@@ -1381,26 +1365,24 @@ private static boolean setDosReadOnly(final Path path, final boolean readOnly, f
13811365
* To delete a file in POSIX, you need Write and Execute permissions on its parent directory.
13821366
*
13831367
* @param parent The parent path for a file element to delete which needs RW permissions.
1384-
* @param enableDelete true to set permissions to delete.
1368+
* @param enableDeleteChildren true to set permissions to delete.
13851369
* @param linkOptions options indicating how handle symbolic links.
13861370
* @return true if the operation was attempted and succeeded, false if parent is null.
13871371
* @throws IOException if an I/O error occurs.
13881372
*/
1389-
private static boolean setPosixDeletePermissions(final Path parent, final boolean enableDelete, final LinkOption... linkOptions) throws IOException {
1390-
if (parent != null) {
1391-
// To delete a file in POSIX, you need write and execute permissions on its parent directory.
1392-
// @formatter:off
1393-
return setPosixPermissions(parent, enableDelete, Arrays.asList(
1394-
PosixFilePermission.OWNER_WRITE,
1395-
//PosixFilePermission.GROUP_WRITE,
1396-
//PosixFilePermission.OTHERS_WRITE,
1397-
PosixFilePermission.OWNER_EXECUTE
1398-
//PosixFilePermission.GROUP_EXECUTE,
1399-
//PosixFilePermission.OTHERS_EXECUTE
1400-
), linkOptions);
1401-
// @formatter:on
1402-
}
1403-
return false;
1373+
private static boolean setPosixDeletePermissions(final Path parent, final boolean enableDeleteChildren, final LinkOption... linkOptions)
1374+
throws IOException {
1375+
// To delete a file in POSIX, you need write and execute permissions on its parent directory.
1376+
// @formatter:off
1377+
return setPosixPermissions(parent, enableDeleteChildren, Arrays.asList(
1378+
PosixFilePermission.OWNER_WRITE,
1379+
//PosixFilePermission.GROUP_WRITE,
1380+
//PosixFilePermission.OTHERS_WRITE,
1381+
PosixFilePermission.OWNER_EXECUTE
1382+
//PosixFilePermission.GROUP_EXECUTE,
1383+
//PosixFilePermission.OTHERS_EXECUTE
1384+
), linkOptions);
1385+
// @formatter:on
14041386
}
14051387

14061388
/**
@@ -1416,17 +1398,14 @@ private static boolean setPosixDeletePermissions(final Path parent, final boolea
14161398
private static boolean setPosixPermissions(final Path path, final boolean addPermissions, final List<PosixFilePermission> updatePermissions,
14171399
final LinkOption... linkOptions) throws IOException {
14181400
if (path != null) {
1419-
final PosixFileAttributeView posixFileAttributeView = getPosixFileAttributeView(path, linkOptions);
1420-
if (posixFileAttributeView != null) {
1421-
final Set<PosixFilePermission> permissions = posixFileAttributeView.readAttributes().permissions();
1422-
if (addPermissions) {
1423-
permissions.addAll(updatePermissions);
1424-
} else {
1425-
permissions.removeAll(updatePermissions);
1426-
}
1427-
Files.setPosixFilePermissions(path, permissions);
1428-
return true;
1401+
final Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(path, linkOptions);
1402+
if (addPermissions) {
1403+
permissions.addAll(updatePermissions);
1404+
} else {
1405+
permissions.removeAll(updatePermissions);
14291406
}
1407+
Files.setPosixFilePermissions(path, permissions);
1408+
return true;
14301409
}
14311410
return false;
14321411
}

src/test/java/org/apache/commons/io/DeleteDirectoryTest.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,10 @@ public void testDeleteFileCheckParentAccess() throws IOException {
100100
// A file is RO in POSIX if the parent is not W and not E.
101101
PathUtils.setReadOnly(file, true);
102102
final Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(testDir);
103-
assertFalse(Files.isWritable(testDir), () -> String.format("Directory '%s' should NOT be Writable, permissions are %s ", testDir, permissions));
104-
assertFalse(Files.isExecutable(testDir), () -> String.format("Directory '%s' should NOT be Executable, permissions are %s ", testDir, permissions));
103+
assertFalse(Files.isWritable(testDir),
104+
() -> String.format("Parent directory '%s' of '%s' should NOT be Writable, permissions are %s ", testDir, file, permissions));
105+
assertFalse(Files.isExecutable(testDir),
106+
() -> String.format("Parent directory '%s' of '%s' should NOT be Executable, permissions are %s ", testDir, file, permissions));
105107

106108
assertThrows(IOException.class, () -> PathUtils.delete(file));
107109
// Nothing happened, we're not even allowed to test attributes, so the file seems deleted, but it is not.

0 commit comments

Comments
 (0)