CodeGym /Corsi /JAVA 25 SELF /Permessi e accesso al file system

Permessi e accesso al file system

JAVA 25 SELF
Livello 38, Lezione 3
Disponibile

1. Permessi di accesso nel SO

Quando lavori con file e cartelle, è importante ricordare: il sistema operativo (SO) li protegge tramite un sistema di permessi. Questo significa che non ogni programma (e non ogni utente) può leggere, modificare o eliminare qualsiasi file.

POSIX (Linux, macOS, ecc.)

Nei sistemi POSIX (Unix, Linux, macOS) ogni file e cartella ha permessi per tre categorie:

  • Proprietario (user)
  • Gruppo (group)
  • Altri (others)

Per ciascuna categoria si definiscono tre tipi di permesso:

  • r — read (lettura)
  • w — write (scrittura)
  • x — execute (esecuzione)

Esempio:
-rw-r--r--
Significa: il proprietario può leggere e scrivere, gli altri — solo leggere.

Windows

In Windows i permessi si definiscono tramite il sistema ACL (Access Control List) — elenchi di autorizzazioni per utenti e gruppi. Qui si può configurare in modo flessibile chi può fare cosa con un file o una cartella (leggere, scrivere, modificare, eseguire, ecc.).

Importante:
Un programma Java lavora con i file entro i permessi dell’utente con cui è avviato. Se l’utente non ha i permessi sul file — il programma non potrà leggerlo o modificarlo.

2. Eccezione AccessDeniedException e cause

Quando lavori con i file in Java (soprattutto tramite le API NIO), potresti imbatterti nell’eccezione:

java.nio.file.AccessDeniedException

Questa eccezione viene lanciata se il tuo programma non ha i permessi per eseguire un’operazione su un file o una directory.

Cause principali:

  • Nessun permesso di lettura del file (ad esempio, il file è protetto dalla lettura).
  • Nessun permesso di scrittura nel file o nella cartella (ad esempio, stai tentando di scrivere in una cartella di sistema).
  • Nessun permesso di esecuzione del file (rilevante per l’avvio di programmi).
  • La cartella o il file sono protetti da modifiche (ad esempio, sola lettura).
  • Il file o la cartella sono in uso da un altro processo (soprattutto su Windows).

Esempio:

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

Cosa fare?

  • Verifica i permessi di accesso del file/cartella.
  • Esegui il programma come un utente con i permessi necessari.
  • Evita di scrivere nelle directory di sistema se non è necessario.

3. Verifica dei permessi in Java: metodi Files.isReadable(), isWritable(), isExecutable()

Java fornisce metodi comodi per verificare i permessi su un file o una cartella:

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

System.out.println(Files.isReadable(path));   // true se è possibile leggere
System.out.println(Files.isWritable(path));   // true se è possibile scrivere
System.out.println(Files.isExecutable(path)); // true se è possibile eseguire

Questi metodi mostrano come il sistema vede i tuoi permessi in questo momento.

Ma!
Non garantiscono che l’operazione andrà veramente a buon fine. Le ragioni possono essere varie: il file può essere bloccato da un altro programma, i permessi potrebbero essere cambiati dopo il controllo, sui dischi di rete i risultati dipendono dal server e talvolta il SO riferisce una cosa ma applica altre restrizioni.

Perciò in Java è meglio prima verificare i permessi e poi racchiudere l’operazione reale di lettura o scrittura in un blocco try-catch — è più affidabile.

Problema TOCTOU (Time Of Check To Time Of Use)

È direttamente collegata la situazione TOCTOU, quando tra il controllo del permesso e l’uso effettivo qualcosa cambia. Ad esempio:

  1. Hai verificato che il file è scrivibile (isWritable).
  2. Nel frattempo un altro processo o utente modifica i permessi — ora il file è protetto.
  3. Provi a scrivere i dati — ottieni AccessDeniedException.

Conclusione:
Il controllo dei permessi fornisce solo un’indicazione dello stato attuale, ma non garantisce il successo dell’operazione. Gestisci sempre le eccezioni quando lavori con i file.

4. Principio della «scrittura sicura» (atomic write)

Perché serve un metodo di scrittura sicuro (atomico)?

A volte durante la scrittura di un file può verificarsi un errore: il programma si arresta, salta la corrente, manca spazio sul disco... Di conseguenza il file può risultare corrotto o scritto solo parzialmente. Questo è particolarmente pericoloso per i dati importanti (ad esempio, configurazioni, database, documenti).

La scrittura sicura è un modo per garantire che il file sia completamente aggiornato oppure resti nello stato precedente. Questo approccio si chiama scrittura atomica (atomic write).

Come implementare una scrittura sicura in Java?

Pattern:

  • Scriviamo i dati in un file temporaneo (di solito nella stessa cartella).
  • Se la scrittura è andata a buon fine — spostiamo in modo atomico il file temporaneo al posto dell’originale (sostituendolo).

Perché funziona?
L’operazione di spostamento del file (rename/move) all’interno dello stesso file system è in genere atomica: o il file viene sostituito completamente, oppure no. Se qualcosa va storto — il file originale rimane intatto.

Esempio di codice: scrittura sicura del file

import java.nio.file.*;

public class SafeWriteDemo {
    public static void safeWrite(Path target, byte[] data) throws Exception {
        // 1. Creiamo un file temporaneo nella stessa cartella
        Path tempFile = Files.createTempFile(target.getParent(), "tmp_", ".tmp");

        try {
            // 2. Scriviamo i dati nel file temporaneo
            Files.write(tempFile, data);

            // 3. Spostiamo in modo atomico il file temporaneo al posto dell'originale
            Files.move(
                tempFile,
                target,
                StandardCopyOption.REPLACE_EXISTING,
                StandardCopyOption.ATOMIC_MOVE
            );
        } finally {
            // Se qualcosa è andato storto — eliminiamo il file temporaneo
            Files.deleteIfExists(tempFile);
        }
    }

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

        safeWrite(file, content);
        System.out.println("File scritto in modo sicuro!");
    }
}

Da notare:

  • Usiamo Files.createTempFile() per creare il file temporaneo.
  • Per lo spostamento usiamo l’opzione ATOMIC_MOVE — garantisce l’atomicità (se supportata dal SO e dal file system).
  • Se qualcosa va storto — il file temporaneo viene eliminato (Files.deleteIfExists).

Quando è particolarmente importante?

  • Nella gestione di file di configurazione, database, log.
  • Se il file può essere letto da un altro processo in qualsiasi momento.
  • Se un errore in scrittura può portare a perdita o corruzione dei dati.

5. Logging e gestione degli errori di accesso

Come gestire correttamente gli errori di accesso?

Quando lavori con i file usa sempre la gestione delle eccezioni (try-catch). Questo permette di:

  • Informare correttamente l’utente del problema (ad esempio, «Nessun permesso di scrittura nella cartella»).
  • Registrare l’errore nel log per un’analisi successiva.
  • Evitare di far terminare l’intera applicazione a causa di un’unica operazione non riuscita.

Esempio: gestione di AccessDeniedException

import java.nio.file.*;

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

        try {
            Files.readAllLines(file);
        } catch (AccessDeniedException ade) {
            System.err.println("Errore di accesso: nessun permesso di lettura per il file " + file);
            // Si può scrivere nel log o proporre all'utente di scegliere un altro file
        } catch (Exception e) {
            System.err.println("Altro errore: " + e.getMessage());
        }
    }
}

Logging degli errori

Nelle applicazioni reali usa sistemi di logging (ad esempio, java.util.logging, Log4j, SLF4J). Questo permette di:

  • Registrare gli errori con i dettagli (stack trace, ora, utente).
  • Analizzare i log per trovare e correggere i problemi.
  • Non mostrare all’utente messaggi «spaventosi», ma inviarli solo al log.

Esempio con logging:

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("Nessun accesso al file: " + file);
        } catch (Exception e) {
            logger.log(Level.SEVERE, "Errore durante l'operazione sul file", e);
        }
    }
}

6. Errori tipici

Errore n. 1: ignorare le eccezioni durante il lavoro con i file.
Non scrivere mai semplicemente Files.write(path, data) senza try-catch — se qualcosa va storto, l’applicazione si arresterà.

Errore n. 2: controllare i permessi senza considerare il TOCTOU.
Non fare affidamento solo su Files.isWritable() e metodi simili. Anche se restituiscono “si può”, l’operazione potrebbe non riuscire. Gestisci sempre le eccezioni (ad esempio, AccessDeniedException).

Errore n. 3: sovrascrivere un file esistente senza backup.
Se il file è importante — crea un backup prima di scrivere oppure usa la scrittura atomica con StandardCopyOption.ATOMIC_MOVE.

Errore n. 4: non eliminare i file temporanei dopo un errore.
Se durante la scrittura atomica qualcosa va storto — il file temporaneo può rimanere. Usa finally e Files.deleteIfExists().

Errore n. 5: non registrare nel log gli errori di accesso.
Se l’applicazione non è riuscita a scrivere o leggere un file — l’utente deve saperlo e tu devi vedere i dettagli nel log. Usa java.util.logging/SLF4J e registra le eccezioni con lo stack trace.

1
Compito
JAVA 25 SELF, livello 38, lezione 3
Bloccato
Accesso ai disegni segreti: verifica dell'autorizzazione alla visualizzazione 👁️
Accesso ai disegni segreti: verifica dell'autorizzazione alla visualizzazione 👁️
1
Compito
JAVA 25 SELF, livello 38, lezione 3
Bloccato
Preparazione del magazzino per nuove forniture: controllo del permesso di scrittura 📦
Preparazione del magazzino per nuove forniture: controllo del permesso di scrittura 📦
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION