Berkas yang tepat isinya bisa tetap berbahaya bila salah orang dapat membacanya — kunci API yang terbaca semua pengguna mesin, log transaksi yang bisa diubah proses lain, skrip yang dieksekusi tanpa sengaja. Sistem operasi mengatur hal ini lewat permission: bit penanda siapa boleh membaca, menulis, dan mengeksekusi setiap berkas. Java menawakan dua lapis API untuk urusan ini: pertanyaan sederhana Files.isReadable/isWritable/isExecutable yang bekerja di semua platform, dan model permission POSIX yang terperinci untuk Linux dan sistem UNIX. Pada Chapter ini kita memakai keduanya, membuktikan dampaknya pada pemeriksaan akses, lalu memetakan keterbatasannya di Windows.

Tiga Pertanyaan Akses #

Method Files.isReadable(path), isWritable(path), dan isExecutable(path) menjawab pertanyaan praktis yang berdiri dari sudut pandang pengguna yang menjalankan JVM: apakah proses kita boleh membaca, menulis, atau mengeksekusi berkas ini? Semua mengembalikan boolean tanpa exception — untuk berkas yang tidak ada pun jawabnya false, bukan error. Berbeda dari pertanyaan atribut pada Chapter File Attributes, ketiganya menguji akses nyata dengan memeriksa kepemilikan dan bit izin, sehingga hasilnya bisa berubah ketika permission diubah — bahkan tanpa berkasnya dicipta ulang.

Program berikut memanfaatkan fakta itu untuk memperagakan naik-turunnya akses tulis:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.PosixFilePermission;
import java.util.Set;

public class PeriksaAkses {
    public static void main(String[] args) throws IOException {
        Path berkas = Path.of("data.txt");
        Files.writeString(berkas, "data contoh\n");

        System.out.println("isReadable   : " + Files.isReadable(berkas));
        System.out.println("isWritable   : " + Files.isWritable(berkas));
        System.out.println("isExecutable : " + Files.isExecutable(berkas));
        System.out.println("isReadable hilang.txt : "
                + Files.isReadable(Path.of("hilang.txt")));

        // Turunkan izin: hanya boleh dibaca pemiliknya.
        Set<PosixFilePermission> hanyaBaca = Set.of(PosixFilePermission.OWNER_READ);
        Files.setPosixFilePermissions(berkas, hanyaBaca);
        System.out.println("hanya baca   : isWritable = " + Files.isWritable(berkas));

        // Pulihkan izin tulis pemilik.
        Set<PosixFilePermission> tulis = Set.of(
                PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE);
        Files.setPosixFilePermissions(berkas, tulis);
        System.out.println("pulih        : isWritable = " + Files.isWritable(berkas));

        Files.delete(berkas);
    }
}

Output yang Dihasilkan:

text
isReadable   : true
isWritable   : true
isExecutable : false
isReadable hilang.txt : false
hanya baca   : isWritable = false
pulih        : isWritable = true

Keluaran tersebut memperlihatkan empat perilaku yang penting:

  1. Baris 12-13: berkas teks baru dibuat dengan bit baca dan tulis untuk pemiliknya sehingga isReadable dan isWritable sama-sama true.
  2. Baris 14 (isExecutable): false — berkas data biasa tidak mendapat bit eksekusi, dan sistem operasi menolak menjalankan berkas tanpa bit tersebut.
  3. Baris 15-16 (hilang.txt): berkas yang tidak ada dijawab false, bukan NoSuchFileException; pertanyaan akses memang tidak melempar exception.
  4. Baris 19-25: setelah setPosixFilePermissions menyisakan OWNER_READ saja, isWritable turun menjadi false — bit izin sungguh berlaku pada pemeriksaan akses — lalu kembali true setelah bit tulis pemilik dipulihkan. Penghapusan di baris terakhir tetap berhasil karena menghapus berkas memerlukan izin tulis pada direktori, bukan pada berkasnya.

Permission POSIX dalam Sembilan Bit #

Model POSIX menyimpan izin dalam tiga kelompok — pemilik (owner), grup (group), dan lainnya (others) — masing-masing tiga bit: baca, tulis, eksekusi. Sembilan bit itu terwakili dalam enum PosixFilePermission bernilai OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, GROUP_READ, dan seterusnya, yang dikemas dalam Set<PosixFilePermission>. Utility PosixFilePermissions.fromString("rw-r-----") mengurai notasi klasik sembilan karakter menjadi Set, dan toString(set) bekerja arah sebaliknya — jembatan yang nyaman antara dunia chmod dan kode Java. Satu jebakan kecil: Files.getPosixFilePermissions mengembalikan HashSet yang urutan cetaknya tidak menentu, sehingga mencetaknya langsung menghasilkan keluaran yang berbeda-beda; membungkusnya dengan TreeSet menghasilkan urutan yang stabil.

Izin bawaan berkas baru tidak ditulis di kode — kernel memotong izin penuh dengan umask pengguna, sehingga hasilnya bergantung lingkungan. Program berikut karena itu mengatur izin secara eksplisit sebelum membacanya:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.PosixFileAttributeView;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.Set;
import java.util.TreeSet;

public class BacaIzinPosix {
    public static void main(String[] args) throws IOException {
        Path berkas = Path.of("arsip.txt");
        Files.writeString(berkas, "isi arsip\n");

        // Atur dulu agar hasil tidak bergantung pada umask lingkungan.
        Files.setPosixFilePermissions(berkas,
                PosixFilePermissions.fromString("rw-r-----"));

        Set<PosixFilePermission> izin = Files.getPosixFilePermissions(berkas);
        System.out.println("rwx string   : " + PosixFilePermissions.toString(izin));
        System.out.println("terurut      : " + new TreeSet<>(izin));

        // View POSIX membaca izin sekaligus kepemilikan.
        PosixFileAttributeView tampilan =
                Files.getFileAttributeView(berkas, PosixFileAttributeView.class);
        System.out.println("nama view    : " + tampilan.name());
        System.out.println("izin via view: "
                + PosixFilePermissions.toString(tampilan.readAttributes().permissions()));

        Files.delete(berkas);
    }
}

Output yang Dihasilkan:

text
rwx string   : rw-r-----
terurut      : [OWNER_READ, OWNER_WRITE, GROUP_READ]
nama view    : posix
izin via view: rw-r-----

Perhatikan tiga hal dari program tersebut:

  1. fromString("rw-r-----"): izin rw- untuk pemilik, r-- untuk grup, --- untuk lainnya — setara perintah chmod 640 — dipasang sebelum pembacaan sehingga keluaran pasti sama di lingkungan umask mana pun.
  2. new TreeSet<>(izin): enum diurutkan berdasarkan nama sehingga keluaran [GROUP_READ, OWNER_READ, OWNER_WRITE] selalu tertulis dengan urutan yang sama; urutan HashSet tidak diberi jaminan dan akan menggagalkan keluaran yang bisa diuji ulang.
  3. PosixFileAttributeView: mekanisme view membaca seluruh atribut POSIX — izin plus identitas pemilik dan grup — dalam satu panggilan; readAttributes().permissions() menghasilkan Set yang sama dengan Files.getPosixFilePermissions, dan view.name() mengembalikan "posix" sebagai penanda jenis view yang aktif.

Keterbatasan di platform Windows memang perlu dipahami sebelum menulis kode portabel. NTFS memakai Access Control List (ACL) — daftar aturan per-pengguna yang jauh lebih kaya daripada sembilan bit POSIX — yang diekspos Java melalui AclFileAttributeView; Java juga menyediakan DosFileAttributeView untuk atribut sederhana seperti read-only, hidden, dan archive. Kedua view tersebut tidak tersedia di Linux, cermin dari situasi POSIX di Windows. API lama File.setReadable/setWritable/setExecutable mencoba menjembatani dengan semantik terbaik-usaha di setiap platform, tetapi tanpa kekayaan POSIX. Praktik aman yang berlaku umum: andalkan tiga pertanyaan is* untuk logika program, gunakan API POSIX atau ACL khusus hanya untuk alat administrasi yang memang berjalan di platform targetnya.

Selama 15 chapter kita merakit satu per satu alat I/O Java — dari byte stream sampai permission. Chapter penutup Section ini merangkai semuanya menjadi satu alur kerja utuh: pemrosesan berkas yang tahan terhadap input buruk, charset yang eksplisit, penulisan transaksional, dan verifikasi hasil.

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

BacaIzinPosix.java

Unduh berkas
java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.PosixFileAttributeView;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.Set;
import java.util.TreeSet;

public class BacaIzinPosix {
    public static void main(String[] args) throws IOException {
        Path berkas = Path.of("arsip.txt");
        Files.writeString(berkas, "isi arsip\n");

        // Atur dulu agar hasil tidak bergantung pada umask lingkungan.
        Files.setPosixFilePermissions(berkas,
                PosixFilePermissions.fromString("rw-r-----"));

        Set<PosixFilePermission> izin = Files.getPosixFilePermissions(berkas);
        System.out.println("rwx string   : " + PosixFilePermissions.toString(izin));
        System.out.println("terurut      : " + new TreeSet<>(izin));

        // View POSIX membaca izin sekaligus kepemilikan.
        PosixFileAttributeView tampilan =
                Files.getFileAttributeView(berkas, PosixFileAttributeView.class);
        System.out.println("nama view    : " + tampilan.name());
        System.out.println("izin via view: "
                + PosixFilePermissions.toString(tampilan.readAttributes().permissions()));

        Files.delete(berkas);
    }
}

PeriksaAkses.java

Unduh berkas
java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.PosixFilePermission;
import java.util.Set;

public class PeriksaAkses {
    public static void main(String[] args) throws IOException {
        Path berkas = Path.of("data.txt");
        Files.writeString(berkas, "data contoh\n");

        System.out.println("isReadable   : " + Files.isReadable(berkas));
        System.out.println("isWritable   : " + Files.isWritable(berkas));
        System.out.println("isExecutable : " + Files.isExecutable(berkas));
        System.out.println("isReadable hilang.txt : "
                + Files.isReadable(Path.of("hilang.txt")));

        // Turunkan izin: hanya boleh dibaca pemiliknya.
        Set<PosixFilePermission> hanyaBaca = Set.of(PosixFilePermission.OWNER_READ);
        Files.setPosixFilePermissions(berkas, hanyaBaca);
        System.out.println("hanya baca   : isWritable = " + Files.isWritable(berkas));

        // Pulihkan izin tulis pemilik.
        Set<PosixFilePermission> tulis = Set.of(
                PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE);
        Files.setPosixFilePermissions(berkas, tulis);
        System.out.println("pulih        : isWritable = " + Files.isWritable(berkas));

        Files.delete(berkas);
    }
}