Skip to content

Commit c5e0254

Browse files
author
Gary Gregory
committed
PathUtils.setReadOnly(Path, boolean, LinkOption...) readOnly argument is
always assumed true on POSIX.
1 parent ea11270 commit c5e0254

3 files changed

Lines changed: 77 additions & 15 deletions

File tree

src/changes/changes.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ The <action> type attribute can be add,update,fix,remove.
104104
<action issue="IO-638" dev="ggregory" type="fix" due-to="Gary Gregory">
105105
PathUtils.setReadOnly(Path, boolean, LinkOption...) should add READ_* file attributes when using POSIX.
106106
</action>
107+
<action issue="IO-638" dev="ggregory" type="fix" due-to="Gary Gregory">
108+
PathUtils.setReadOnly(Path, boolean, LinkOption...) readOnly argument is always assumed true on POSIX.
109+
</action>
107110
<!-- ADD -->
108111
<action dev="ggregory" type="add" due-to="Gary Gregory">
109112
Add BrokenReader.INSTANCE.

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

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,27 +1154,30 @@ public static Path setReadOnly(final Path path, final boolean readOnly, final Li
11541154
final List<Exception> causeList = new ArrayList<>(2);
11551155
final DosFileAttributeView fileAttributeView = Files.getFileAttributeView(path, DosFileAttributeView.class, linkOptions);
11561156
if (fileAttributeView != null) {
1157+
// Windows 10
11571158
try {
11581159
fileAttributeView.setReadOnly(readOnly);
11591160
return path;
11601161
} catch (final IOException e) {
1161-
// ignore for now, retry with PosixFileAttributeView
1162+
// Remember and retry with PosixFileAttributeView
11621163
causeList.add(e);
11631164
}
11641165
}
11651166
final PosixFileAttributeView posixFileAttributeView = Files.getFileAttributeView(path, PosixFileAttributeView.class, linkOptions);
11661167
if (posixFileAttributeView != null) {
1167-
// Works on Windows but not on Ubuntu:
1168-
// Files.setAttribute(path, "unix:readonly", readOnly, options);
1169-
// java.lang.IllegalArgumentException: 'unix:readonly' not recognized
1168+
// Not Windows 10
11701169
final PosixFileAttributes readAttributes = posixFileAttributeView.readAttributes();
11711170
final Set<PosixFilePermission> permissions = readAttributes.permissions();
11721171
permissions.add(PosixFilePermission.OWNER_READ);
11731172
permissions.add(PosixFilePermission.GROUP_READ);
11741173
permissions.add(PosixFilePermission.OTHERS_READ);
1175-
permissions.remove(PosixFilePermission.OWNER_WRITE);
1176-
permissions.remove(PosixFilePermission.GROUP_WRITE);
1177-
permissions.remove(PosixFilePermission.OTHERS_WRITE);
1174+
List<PosixFilePermission> writePermissions = Arrays.asList(PosixFilePermission.OWNER_WRITE, PosixFilePermission.GROUP_WRITE,
1175+
PosixFilePermission.OTHERS_WRITE);
1176+
if (readOnly) {
1177+
permissions.removeAll(writePermissions);
1178+
} else {
1179+
permissions.addAll(writePermissions);
1180+
}
11781181
try {
11791182
return Files.setPosixFilePermissions(path, permissions);
11801183
} catch (final IOException e) {

src/test/java/org/apache/commons/io/file/PathUtilsDeleteFileTest.java

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,23 @@
1818
package org.apache.commons.io.file;
1919

2020
import static org.apache.commons.io.file.CounterAssertions.assertCounts;
21+
import static org.junit.jupiter.api.Assertions.assertEquals;
2122
import static org.junit.jupiter.api.Assertions.assertFalse;
2223
import static org.junit.jupiter.api.Assertions.assertThrows;
2324
import static org.junit.jupiter.api.Assertions.assertTrue;
2425
import static org.junit.jupiter.api.Assumptions.assumeFalse;
2526

2627
import java.io.IOException;
28+
import java.nio.charset.StandardCharsets;
2729
import java.nio.file.Files;
2830
import java.nio.file.LinkOption;
2931
import java.nio.file.NoSuchFileException;
3032
import java.nio.file.Path;
3133
import java.nio.file.Paths;
34+
import java.nio.file.attribute.DosFileAttributeView;
35+
import java.nio.file.attribute.PosixFileAttributeView;
36+
import java.nio.file.attribute.PosixFilePermission;
37+
import java.util.Set;
3238

3339
import org.apache.commons.io.file.Counters.PathCounters;
3440
import org.apache.commons.lang3.SystemUtils;
@@ -79,8 +85,7 @@ public void testDeleteBrokenLink() throws IOException {
7985
@Test
8086
public void testDeleteFileDirectory1FileSize0() throws IOException {
8187
final String fileName = "file-size-0.bin";
82-
PathUtils.copyFileToDirectory(
83-
Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0/" + fileName), tempDir);
88+
PathUtils.copyFileToDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0/" + fileName), tempDir);
8489
assertCounts(0, 1, 0, PathUtils.deleteFile(tempDir.resolve(fileName)));
8590
// This will throw if not empty.
8691
Files.deleteIfExists(tempDir);
@@ -92,8 +97,7 @@ public void testDeleteFileDirectory1FileSize0() throws IOException {
9297
@Test
9398
public void testDeleteFileDirectory1FileSize1() throws IOException {
9499
final String fileName = "file-size-1.bin";
95-
PathUtils.copyFileToDirectory(
96-
Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1/" + fileName), tempDir);
100+
PathUtils.copyFileToDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1/" + fileName), tempDir);
97101
assertCounts(0, 1, 1, PathUtils.deleteFile(tempDir.resolve(fileName)));
98102
// This will throw if not empty.
99103
Files.deleteIfExists(tempDir);
@@ -129,8 +133,7 @@ public void testDeleteFileEmptyDirectory() throws IOException {
129133
@Test
130134
public void testDeleteReadOnlyFileDirectory1FileSize1() throws IOException {
131135
final String fileName = "file-size-1.bin";
132-
PathUtils.copyFileToDirectory(
133-
Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1/" + fileName), tempDir);
136+
PathUtils.copyFileToDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1/" + fileName), tempDir);
134137
final Path resolved = tempDir.resolve(fileName);
135138
PathUtils.setReadOnly(resolved, true);
136139
if (SystemUtils.IS_OS_WINDOWS) {
@@ -143,14 +146,67 @@ public void testDeleteReadOnlyFileDirectory1FileSize1() throws IOException {
143146
Files.deleteIfExists(tempDir);
144147
}
145148

149+
@Test
150+
public void testSetReadOnlyFile() throws IOException {
151+
final Path resolved = tempDir.resolve("testSetReadOnlyFile.txt");
152+
153+
// TEMP HACK
154+
assertTrue(Files.getFileAttributeView(resolved, DosFileAttributeView.class) != null);
155+
assertTrue(Files.getFileAttributeView(resolved, PosixFileAttributeView.class) == null);
156+
157+
PathUtils.writeString(resolved, "test", StandardCharsets.UTF_8);
158+
final boolean readable = Files.isReadable(resolved);
159+
final boolean writable = Files.isWritable(resolved);
160+
final boolean regularFile = Files.isRegularFile(resolved);
161+
final boolean executable = Files.isExecutable(resolved);
162+
final boolean hidden = Files.isHidden(resolved);
163+
final boolean directory = Files.isDirectory(resolved);
164+
final boolean symbolicLink = Files.isSymbolicLink(resolved);
165+
// Sanity checks
166+
assertTrue(readable);
167+
assertTrue(writable);
168+
// Test A
169+
PathUtils.setReadOnly(resolved, false);
170+
assertEquals(true, Files.isReadable(resolved));
171+
assertEquals(true, Files.isWritable(resolved));
172+
assertEquals(regularFile, Files.isReadable(resolved));
173+
assertEquals(executable, Files.isExecutable(resolved));
174+
assertEquals(hidden, Files.isHidden(resolved));
175+
assertEquals(directory, Files.isDirectory(resolved));
176+
assertEquals(symbolicLink, Files.isSymbolicLink(resolved));
177+
// Test B
178+
PathUtils.setReadOnly(resolved, true);
179+
assertEquals(true, Files.isReadable(resolved));
180+
assertEquals(false, Files.isWritable(resolved));
181+
final DosFileAttributeView dosFileAttributeView = Files.getFileAttributeView(resolved, DosFileAttributeView.class);
182+
if (dosFileAttributeView != null) {
183+
assertTrue(dosFileAttributeView.readAttributes().isReadOnly());
184+
}
185+
final PosixFileAttributeView posixFileAttributeView = Files.getFileAttributeView(resolved, PosixFileAttributeView.class);
186+
if (posixFileAttributeView != null) {
187+
// Not Windows
188+
final Set<PosixFilePermission> permissions = posixFileAttributeView.readAttributes().permissions();
189+
assertFalse(permissions.contains(PosixFilePermission.GROUP_WRITE), () -> permissions.toString());
190+
assertFalse(permissions.contains(PosixFilePermission.OTHERS_WRITE), () -> permissions.toString());
191+
assertFalse(permissions.contains(PosixFilePermission.OWNER_WRITE), () -> permissions.toString());
192+
}
193+
assertEquals(regularFile, Files.isReadable(resolved));
194+
assertEquals(executable, Files.isExecutable(resolved));
195+
assertEquals(hidden, Files.isHidden(resolved));
196+
assertEquals(directory, Files.isDirectory(resolved));
197+
assertEquals(symbolicLink, Files.isSymbolicLink(resolved));
198+
//
199+
PathUtils.setReadOnly(resolved, false);
200+
PathUtils.deleteFile(resolved);
201+
}
202+
146203
/**
147204
* Tests a directory with one file of size 1.
148205
*/
149206
@Test
150207
public void testSetReadOnlyFileDirectory1FileSize1() throws IOException {
151208
final String fileName = "file-size-1.bin";
152-
PathUtils.copyFileToDirectory(
153-
Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1/" + fileName), tempDir);
209+
PathUtils.copyFileToDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1/" + fileName), tempDir);
154210
final Path resolved = tempDir.resolve(fileName);
155211
PathUtils.setReadOnly(resolved, true);
156212
if (SystemUtils.IS_OS_WINDOWS) {

0 commit comments

Comments
 (0)