CodeGym /Courses /JAVA 25 SELF /Permissions and access to the file system

Permissions and access to the file system

JAVA 25 SELF
Level 38 , Lesson 3
Available

1. Permissions in the OS

When you work with files and folders, it’s important to remember: the operating system (OS) protects them using an access control system. This means not every program (and not every user) can read, modify, or delete any file.

POSIX (Linux, macOS, etc.)

In POSIX systems (Unix, Linux, macOS), each file and directory has permissions for three categories:

  • Owner (user)
  • Group (group)
  • Others (others)

Each category has three types of permissions:

  • r — read
  • w — write
  • x — execute

Example:
-rw-r--r--
This means: the owner can read and write; others can only read.

Windows

In Windows, permissions are configured via ACL (Access Control List) — lists of permissions for users and groups. You can flexibly configure who can do what with a file or folder (read, write, modify, execute, etc.).

Important:
A Java program works with files within the rights of the user under which it is running. If the user has no permissions for a file, the program won’t be able to read or modify it either.

2. AccessDeniedException and its causes

When working with files in Java (especially via the NIO API), you may encounter the exception:

java.nio.file.AccessDeniedException

This exception is thrown if your program does not have permission to perform an operation on a file or directory.

Main causes:

  • No permission to read the file (for example, the file is protected from reading).
  • No permission to write to the file or directory (for example, you are trying to write to a system directory).
  • No permission to execute a file (relevant for running programs).
  • The folder or file is protected from modification (for example, read-only).
  • The file or directory is locked by another process (very common on Windows).

Example:

Path path = Paths.get("/etc/shadow"); // Linux system file
Files.readAllLines(path); // AccessDeniedException!

What to do?

  • Check the permissions on the file/directory.
  • Run the program as a user with the required rights.
  • Avoid writing to system directories unless necessary.

3. Checking permissions in Java: Files.isReadable(), isWritable(), isExecutable()

Java provides convenient methods to check permissions on a file or directory:

Path path = Paths.get("example.txt");

System.out.println(Files.isReadable(path));   // true if readable
System.out.println(Files.isWritable(path));   // true if writable
System.out.println(Files.isExecutable(path)); // true if executable

These methods show how the system sees your permissions at the moment.

But!
They do not guarantee that the operation will actually succeed. Reasons vary: the file may be locked by another program, permissions may change after the check, results on network shares depend on the server, and sometimes the OS reports one thing but applies other restrictions.

Therefore, in Java it’s better to first check permissions and then wrap the actual read or write in a try-catch — it’s more reliable.

The TOCTOU problem (Time Of Check To Time Of Use)

This is directly related to TOCTOU, when something changes between the permission check and the operation itself. For example:

  1. You checked that the file is writable (isWritable).
  2. At that moment another process or user changed the permissions — now the file is protected.
  3. You try to write data — you get AccessDeniedException.

Takeaway:
A permission check only hints at the current state, but does not guarantee a successful operation. Always handle exceptions when working with files.

4. The “safe write” principle (atomic write)

Why do you need a safe (atomic) way to write?

Sometimes a failure may occur while writing a file: the program crashed, the power went out, there wasn’t enough disk space... As a result, the file may end up corrupted or partially written. This is especially dangerous for important data (for example, settings, databases, documents).

Safe write is a way to guarantee that a file is either fully updated or left unchanged. This approach is called an atomic write (atomic write).

How to implement a safe write in Java?

Pattern:

  • Write data to a temporary file (usually in the same directory).
  • If the write succeeds — atomically move the temporary file into place, replacing the original.

Why does this work?
A file rename/move within the same file system is typically atomic: either the file is fully replaced or not changed at all. If something goes wrong, the original file remains untouched.

Code example: safe file write

import java.nio.file.*;

public class SafeWriteDemo {
    public static void safeWrite(Path target, byte[] data) throws Exception {
        // 1. Create a temporary file in the same folder
        Path tempFile = Files.createTempFile(target.getParent(), "tmp_", ".tmp");

        try {
            // 2. Write data to the temp file
            Files.write(tempFile, data);

            // 3. Atomically move the temp file to the target
            Files.move(
                tempFile,
                target,
                StandardCopyOption.REPLACE_EXISTING,
                StandardCopyOption.ATOMIC_MOVE
            );
        } finally {
            // If something went wrong — delete the temp file
            Files.deleteIfExists(tempFile);
        }
    }

    public static void main(String[] args) throws Exception {
        Path file = Paths.get("important.txt");
        byte[] content = "Very important data".getBytes();

        safeWrite(file, content);
        System.out.println("File written safely!");
    }
}

Note:

  • Use Files.createTempFile() to create a temporary file.
  • For moving, use ATOMIC_MOVE — it guarantees atomicity (if supported by the OS and the file system).
  • If something goes wrong, the temporary file is deleted (Files.deleteIfExists).

When is this especially important?

  • When working with configuration files, databases, logs.
  • If the file can be read by another process at any moment.
  • If a write failure can lead to data loss or corruption.

5. Logging and handling access errors

How to handle access errors correctly?

When working with files, always use exception handling (try-catch). This allows you to:

  • Properly notify the user about the problem (for example, “No write permission for the folder”).
  • Write the error to a log for further analysis.
  • Keep the entire application from crashing because of one failed operation.

Example: handling AccessDeniedException

import java.nio.file.*;

public class FileAccessDemo {
    public static void main(String[] args) {
        Path file = Paths.get("/etc/shadow"); // example for Linux

        try {
            Files.readAllLines(file);
        } catch (AccessDeniedException ade) {
            System.err.println("Access error: no read permission for file " + file);
            // You can log it or prompt the user to choose another file
        } catch (Exception e) {
            System.err.println("Other error: " + e.getMessage());
        }
    }
}

Error logging

In real applications, use logging frameworks (for example, java.util.logging, Log4j, SLF4J). This allows you to:

  • Record errors with details (stack traces, time, user).
  • Analyze logs to find and fix issues.
  • Avoid showing “scary” messages to users and output them to logs instead.

Logging example:

import java.nio.file.*;
import java.util.logging.*;

public class FileLoggerDemo {
    private static final Logger logger = Logger.getLogger(FileLoggerDemo.class.getName());

    public static void main(String[] args) {
        Path file = Paths.get("data.txt");

        try {
            Files.readAllLines(file);
        } catch (AccessDeniedException ade) {
            logger.severe("No access to file: " + file);
        } catch (Exception e) {
            logger.log(Level.SEVERE, "Error while working with file", e);
        }
    }
}

6. Common mistakes

Error #1: Ignoring exceptions when working with files.
Never just write Files.write(path, data) without try-catch — if something goes wrong, the program will crash.

Error #2: Checking permissions without handling TOCTOU.
Don’t rely only on Files.isWritable() and similar methods. Even if they return “allowed,” the operation may still fail. Always handle exceptions (for example, AccessDeniedException).

Error #3: Writing over an existing file without a backup.
If the file is important, make a backup before writing or use an atomic write with StandardCopyOption.ATOMIC_MOVE.

Error #4: Not deleting temporary files after a failure.
If something goes wrong during an atomic write, a temporary file may remain. Use finally and Files.deleteIfExists().

Error #5: Not logging access errors.
If the program failed to write or read a file, the user should be informed, and you should see the details in the log. Use java.util.logging/SLF4J and record exceptions with a stack trace.

1
Task
JAVA 25 SELF, level 38, lesson 3
Locked
Access to secret blueprints: check read permission 👁️
Access to secret blueprints: check read permission 👁️
1
Task
JAVA 25 SELF, level 38, lesson 3
Locked
Preparing the warehouse for new deliveries: checking storage permission 📦
Preparing the warehouse for new deliveries: checking storage permission 📦
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION