Make a File Read-Only in Java: setReadOnly() and POSIX

Make a file read-only in Java with setReadOnly(), POSIX permissions or the DOS attribute, make it writable again and see what read-only does not stop.

Comparison of a read-only file on Linux with r--r--r-- permissions and on Windows with the read-only attribute, showing which operations still work

To make a file read-only in Java, we call file.setReadOnly() on a java.io.File, or, on Linux and macOS, remove the write bits with Files.setPosixFilePermissions(). On Windows, the same setReadOnly() call sets the DOS read-only attribute, which we can also set with Files.setAttribute(path, “dos:readonly”, true).

We mark files read-only to stop our own app or a careless script from changing them, for example an invoice PDF that was already sent to a customer, an exported audit log, a signed contract or a configuration file that only the deployment should change.

The following example makes a configuration file read-only, checks its permissions and makes it writable again.

Path dir = Files.createTempDirectory("config");
Path appYml = Files.writeString(dir.resolve("app.yml"), "port: 8080");
boolean done = appYml.toFile().setReadOnly();                        // true
String perms = PosixFilePermissions.toString(Files.getPosixFilePermissions(appYml)); // "r--r--r--"
boolean back = appYml.toFile().setWritable(true);                    // true, owner can write again

Notice that setReadOnly() returns a boolean and does not throw on failure, so we always check the result. We look at what read-only means on each operating system, the difference between setReadOnly() and setWritable(false), POSIX permissions, the DOS attribute, checking the state and the limits of read-only protection.

1. What Read-Only Means on Each Operating System

Linux and macOS store read, write and execute permissions for the owner, the group and everybody else, written as rw-r–r–. A file is read-only for a user when that user’s write bit is missing. Windows has no such bits for this purpose, and it keeps a single read-only attribute on the file instead.

Comparison of a read-only file on Linux with r--r--r-- permissions and on Windows with the read-only attribute, showing which operations still work
A read-only file blocks writes for normal users on both systems, but on Linux the folder’s permissions decide whether it can be deleted, and root ignores the file permissions

The two models behave differently in the cases that surprise people most.

Action on a read-only fileLinux and macOS (r–r–r–)Windows (read-only attribute)
Read the contentAllowedAllowed
Write or appendAccessDeniedExceptionAccessDeniedException
Delete the fileAllowed when the folder is writableAccessDeniedException
Write as root or administratorRoot writes anywayBlocked until the attribute is cleared
Change it backThe owner or root canAnyone with write access to the file’s attributes can

2. Make a File Read-Only With File.setReadOnly()

The method setReadOnly() removes write access for everybody, so on Linux it clears the write bit of the owner, the group and others. The method setWritable(false) clears only the owner’s write bit, and a group that had write access keeps it.

Path shared = Files.createTempDirectory("shared");
Path a = Files.writeString(shared.resolve("a.txt"), "x");
Path b = Files.writeString(shared.resolve("b.txt"), "x");
Path aPerms = Files.setPosixFilePermissions(a, PosixFilePermissions.fromString("rw-rw-r--")); // group can write
Path bPerms = Files.setPosixFilePermissions(b, PosixFilePermissions.fromString("rw-rw-r--")); // group can write
boolean aDone = a.toFile().setReadOnly();                            // true
boolean bDone = b.toFile().setWritable(false);                       // true
String aAfter = PosixFilePermissions.toString(Files.getPosixFilePermissions(a)); // "r--r--r--"
String bAfter = PosixFilePermissions.toString(Files.getPosixFilePermissions(b)); // "r--rw-r--"

Use setReadOnly() when the file must be read-only for everyone, because setWritable(false) leaves group and others write access in place. The overload setWritable(false, false) clears the write bit for all three classes, which gives the same result as setReadOnly() on Linux.

Both methods return false when they cannot change the file, for example when it does not exist or belongs to another user. They don’t throw, so a missing file goes unnoticed unless we check the result.

Path dir = Files.createTempDirectory("invoices");
File missing = dir.resolve("inv-7.pdf").toFile();
boolean changed = missing.setReadOnly();                             // false, no such file

3. POSIX Permissions With Files.setPosixFilePermissions()

On Linux and macOS, Files.setPosixFilePermissions() gives exact control and throws an IOException that names the problem. To make a file read-only without touching the read and execute bits, we read the current set, remove the three write permissions and write the set back.

static void removeWriteAccess(Path file) throws IOException {
    Set<PosixFilePermission> perms = new HashSet<>(Files.getPosixFilePermissions(file));
    perms.removeAll(Set.of(PosixFilePermission.OWNER_WRITE,
            PosixFilePermission.GROUP_WRITE, PosixFilePermission.OTHERS_WRITE));
    Files.setPosixFilePermissions(file, perms);
}
Path bin = Files.createTempDirectory("bin");
Path script = Files.writeString(bin.resolve("deploy.sh"), "echo ok");
Path exec = Files.setPosixFilePermissions(script, PosixFilePermissions.fromString("rwxr-xr-x")); // executable
removeWriteAccess(script);
String scriptPerms = PosixFilePermissions.toString(Files.getPosixFilePermissions(script)); // "r-xr-xr-x"

Say a release pipeline copies application.yml to a server. The app reads it at startup but should never edit it, so the pipeline sets r–r—–. The app user, in the owning group, can read the file, other users can’t see its content, and nobody except root can modify it by accident.

Path release = Files.createTempDirectory("release");
Path yml = Files.writeString(release.resolve("application.yml"), "db: prod");
Path locked = Files.setPosixFilePermissions(yml, PosixFilePermissions.fromString("r--r-----"));
String ymlPerms = PosixFilePermissions.toString(Files.getPosixFilePermissions(locked)); // "r--r-----"

We can also create a file read-only from the start by passing the permissions to Files.createFile(), as shown in creating a new file. On Windows, the POSIX methods throw UnsupportedOperationException.

4. The DOS Read-Only Attribute

On Windows, the read-only flag is part of the DosFileAttributeView. We set it by attribute name with Files.setAttribute() or through the view, and read it back with DosFileAttributes.isReadOnly().

Path docs = Files.createTempDirectory("docs");
Path contract = Files.writeString(docs.resolve("contract.pdf"), "x");
Path flagged = Files.setAttribute(contract, "dos:readonly", true);   // sets the flag
boolean readOnly = Files.readAttributes(contract, DosFileAttributes.class).isReadOnly(); // true

Linux also supports the dos view, but there the JDK stores the flag in an extended attribute of the file. The POSIX permissions stay as they were, so the file remains writable. Cross-platform code therefore picks the mechanism that the file system enforces.

static void makeReadOnly(Path file) throws IOException {
    if (file.getFileSystem().supportedFileAttributeViews().contains("posix")) {
        removeWriteAccess(file);
    } else {
        Files.setAttribute(file, "dos:readonly", true);
    }
}
Path out = Files.createTempDirectory("out");
Path audit = Files.writeString(out.resolve("audit.log"), "x");
makeReadOnly(audit);
String auditPerms = PosixFilePermissions.toString(Files.getPosixFilePermissions(audit)); // "r--r--r--"

5. Making a File Writable Again

Every method has a counterpart that restores write access. Code that updates a protected file, for example a nightly job that appends to a locked audit log, clears the flag, writes and sets it again in a finally block.

Path dir = Files.createTempDirectory("logs");
Path log = Files.writeString(dir.resolve("audit.log"), "x");
boolean locked = log.toFile().setReadOnly();                         // true
boolean ownerOnly = log.toFile().setWritable(true);                  // true, owner may write
String ownerPerms = PosixFilePermissions.toString(Files.getPosixFilePermissions(log)); // "rw-r--r--"
boolean everyone = log.toFile().setWritable(true, false);            // true, all may write
String allPerms = PosixFilePermissions.toString(Files.getPosixFilePermissions(log));   // "rw-rw-rw-"

With POSIX permissions, we add OWNER_WRITE back to the set, and with the DOS attribute we call Files.setAttribute(path, “dos:readonly”, false).

6. Checking Whether a File Is Read-Only

The methods File.canWrite() and Files.isWritable() answer whether the current process may write the file, which is not the same as whether the file is marked read-only. A process running as root, as many containers do, gets true even for a file with r–r–r–.

To check the file’s own setting, we look at the permissions or the DOS attribute.

Path dir = Files.createTempDirectory("check");
Path report = Files.writeString(dir.resolve("report.pdf"), "x");
boolean marked = report.toFile().setReadOnly();                      // true
Set<PosixFilePermission> perms = Files.getPosixFilePermissions(report);
boolean noWriteBits = !perms.contains(PosixFilePermission.OWNER_WRITE)
        && !perms.contains(PosixFilePermission.GROUP_WRITE)
        && !perms.contains(PosixFilePermission.OTHERS_WRITE);
boolean readOnlyForAll = noWriteBits;                                // true

For a normal user, Files.isWritable(report) returns false after setReadOnly(), and that is the right check before an optional write. The same caution as in checking if a file exists applies, so we still handle AccessDeniedException on the write itself.

7. What Read-Only Does Not Protect

Read-only is a guard against mistakes, not a security boundary. Root on Linux and any user with rights to change the attributes on Windows can write the file or restore write access. On Linux, deleting or renaming a file needs write access to the folder that contains it, so a read-only file in a writable folder can still be deleted.

Path tmp = Files.createTempDirectory("tmp");
Path note = Files.writeString(tmp.resolve("note.txt"), "keep me");
boolean ro = note.toFile().setReadOnly();                            // true
Files.delete(note);                                                  // works on Linux, folder is writable
boolean gone = Files.notExists(note);                                // true

To protect files against deletion on Linux, we remove write access from the folder as well, or move the file to storage the app cannot write, such as a separate volume mounted read-only. To delete a read-only file on Windows, we clear the attribute with Files.setAttribute(path, “dos:readonly”, false) first, and the same applies to every read-only file inside a recursive directory delete.

8. Read-Only File FAQs

A read-only flag behaves differently on folders, fails without an exception and is often confused with opening a file for reading, which is what most of these questions are about.

8.1. Can we make a directory read-only in Java?

Yes, the same methods work on folders. On Linux, a folder without write bits blocks creating, renaming and deleting entries inside it for normal users, while the files inside keep their own permissions. On Windows, the read-only attribute on a folder does not stop changes to its files.

8.2. Why does setReadOnly() return false?

The file does not exist, the process is not the owner, or the file system does not support the change. The method gives no reason, so we call Files.setPosixFilePermissions() or Files.setAttribute() when we need an exception that names the cause.

8.3. How do we open a file in read-only mode instead?

That is a different feature. Files.newInputStream(), Files.newBufferedReader() and Files.readString() open a file for reading only, so our code cannot write through that handle, but the file itself stays writable for others. See reading a file to a string.

8.4. How do we make a file read-only for other users only?

We remove only the group and others write bits and keep the owner’s bit.

Path dir = Files.createTempDirectory("team");
Path plan = Files.writeString(dir.resolve("plan.md"), "x");
Path set = Files.setPosixFilePermissions(plan, PosixFilePermissions.fromString("rw-r--r--")); // owner keeps write
String planPerms = PosixFilePermissions.toString(Files.getPosixFilePermissions(plan)); // "rw-r--r--"

9. Conclusion

File.setReadOnly() makes a file read-only for everyone on every platform, while setWritable(false) affects only the owner. On Linux and macOS, Files.setPosixFilePermissions() gives exact control and reports failures as exceptions, and on Windows the dos:readonly attribute is the flag the system enforces.

We check the return value of the File methods, check the permissions rather than canWrite() when the process may run as root, and remember that read-only does not stop root, nor deletion from a writable folder on Linux. For copying and moving files, start from the Java IO tutorial.

10. References

Happy Learning !!

Source Code on Github

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.