Setiap berkas di sistem operasi Unix membawa satu set aturan: siapa pemiliknya, siapa boleh membaca, siapa boleh menulis, dan siapa boleh mengeksekusinya. Aturan tersebut tertanam sebagai bit permission yang Go expose lewat tipe os.FileMode. Salah paham notasi ini berakibat nyata: berkas token yang terbaca semua pengguna, atau program yang gagal jalan karena bit eksekusi tidak disetel. Pada chapter ini kita membaca notasi oktal, menampilkan permission simbolik, lalu menyeting permission dengan os.Chmod sambil memperhitungkan efek umask.

Membaca Angka Oktal #

Permission klasik ditulis dengan tiga digit oktal seperti 0644. Setiap digit meringkas tiga bit — read (r), write (w), execute (x) — untuk satu kelas pengguna, dan posisi digit menentukan kelasnya:

AngkaBitSimbolHak yang diberikan
7111rwxbaca, tulis, eksekusi
6110rw-baca, tulis
5101r-xbaca, eksekusi
4100r--baca saja
0000---tidak ada

Digit pertama berlaku untuk pemilik, kedua untuk grup pemilik, ketiga untuk pengguna lain. Sehingga 0644 berarti pemilik baca-tulis (rw-), sedangkan grup dan lainnya hanya membaca (r--r--). Nol di depan hanya penanda basis delapan — 0644 dan 644 bernilai sama.

Notasi Simbolik dengan FileMode #

Method Mode() pada os.FileInfo mengembalikan os.FileMode yang mencampur bit tipe di bagian atas dan bit permission klasik pada sembilan bit bawah. Method String() merendernya dalam notasi simbolik, sedangkan Perm() memotong keluar sembilan bit permission saja:

go
info, _ := os.Stat("biasa.txt")
fmt.Println(info.Mode())     // -rw-r--r--
fmt.Println(info.Mode().Perm()) // -rw-r--r-- juga, tipe dibuang

Karakter pertama adalah bit tipe: - untuk berkas biasa, d untuk direktori, L untuk tautan simbolik. Konstanta io/fs menyediakan pemeriksaan programatiknya — info.Mode()&fs.ModeDir != 0 menguji direktori, dan fs.ModePerm (0777) adalah topeng sembilan bit permission penuh.

Umask Merampingkan Permintaan #

Permission yang program minta saat membuat berkas tidak selalu jadi permission akhir: kernel menerapkan umask proses. Permission aktual adalah permintaan yang dibersihkan dari bit umask — operasi AND NOT, dalam Go ditulis permintaan &^ umask:

Permintaanumask 0022 (lazim)umask 0002 (mesin demo)
064406440644
0666 (default os.Create)06440664
060006000600
075507550755

Fungsi os.Create selalu meminta 0666, sehingga hasil akhirnya bergantung umask: mesin ber-umask 0022 menghasilkan -rw-r--r--, mesin demo chapter ini (umask 0002) menghasilkan -rw-rw-r--. Permintaan yang tidak menyetel bit yang diblokir umask — 0644, 0600, 0755 — lolos tanpa perubahan.

Mengubah Permission dengan Chmod #

Fungsi os.Chmod(path, mode) menulis ulang permission berkas yang sudah ada. Bit tipe tidak boleh disetel lewat fungsi ini — argumen keduanya cukup sembilan bit permission:

go
if err := os.Chmod("biasa.txt", 0755); err != nil {
	fmt.Println("gagal mengubah permission:", err)
	return
}

Hasil 0755 menambahkan bit eksekusi untuk semua kelas (-rwxr-xr-x). Bit eksekusi adalah syarat sistem operasi menjalankan berkas sebagai program — binary hasil go build otomatis mendapatkannya; skrip harus Anda setel sendiri.

Program Utuh #

Berkas contoh.go membuat beberapa berkas dengan permission berbeda, mengubah salah satunya dengan os.Chmod, lalu melaporkan permission simbolik dan oktalnya lewat fungsi laporkan:

go
package main

import (
	"fmt"
	"io/fs"
	"os"
	"path/filepath"
)

func main() {
	// Direktori kerja sementara agar program aman dijalankan berulang.
	dir, err := os.MkdirTemp("", "contoh-perm-*")
	if err != nil {
		fmt.Println("gagal membuat direktori:", err)
		return
	}
	defer os.RemoveAll(dir)

	// Berkas data biasa: pemilik baca-tulis, lainnya baca saja.
	biasa := filepath.Join(dir, "biasa.txt")
	if err := os.WriteFile(biasa, []byte("data\n"), 0644); err != nil {
		fmt.Println("gagal menulis berkas:", err)
		return
	}
	laporkan(biasa, "WriteFile 0644")

	// os.Create meminta 0666; umask merampingkan hasil akhirnya.
	buat := filepath.Join(dir, "buat.txt")
	f, err := os.Create(buat)
	if err != nil {
		fmt.Println("gagal membuat berkas:", err)
		return
	}
	if _, err := f.WriteString("data\n"); err != nil {
		fmt.Println("gagal menulis:", err)
		f.Close()
		return
	}
	if err := f.Close(); err != nil {
		fmt.Println("gagal menutup berkas:", err)
		return
	}
	laporkan(buat, "os.Create 0666")

	// Berkas rahasia: hanya pemilik yang boleh membaca dan menulis.
	rahasia := filepath.Join(dir, "rahasia.txt")
	if err := os.WriteFile(rahasia, []byte("token\n"), 0600); err != nil {
		fmt.Println("gagal menulis berkas:", err)
		return
	}
	laporkan(rahasia, "WriteFile 0600")

	// os.Chmod mengubah permission berkas yang sudah ada, tanpa pengaruh umask.
	if err := os.Chmod(biasa, 0755); err != nil {
		fmt.Println("gagal mengubah permission:", err)
		return
	}
	laporkan(biasa, "Chmod 0755")

	// Direktori: huruf d memimpin notasi simboliknya.
	folder := filepath.Join(dir, "folder")
	if err := os.Mkdir(folder, 0755); err != nil {
		fmt.Println("gagal membuat direktori:", err)
		return
	}
	info, err := os.Stat(folder)
	if err != nil {
		fmt.Println("gagal membaca metadata:", err)
		return
	}
	fmt.Printf("Mkdir 0755  : %s (%s) isDir=%v ModeDir=%v\n",
		info.Mode(), oktal(info), info.IsDir(), info.Mode()&fs.ModeDir != 0)

	// fs.ModePerm adalah topeng sembilan bit permission penuh.
	fmt.Printf("fs.ModePerm : %o\n", fs.ModePerm)
}

// laporkan mencetak permission simbolik dan oktal sebuah berkas.
func laporkan(path, label string) {
	info, err := os.Stat(path)
	if err != nil {
		fmt.Println("gagal membaca metadata:", err)
		return
	}
	fmt.Printf("%-16s %s (%s)\n", label, info.Mode(), oktal(info))
}

// oktal memformat sembilan bit permission sebagai tiga digit oktal.
func oktal(info os.FileInfo) string {
	return fmt.Sprintf("%03o", info.Mode().Perm())
}

Jalankan program dari direktori chapter dengan perintah berikut:

bash
go run contoh.go

Output yang Dihasilkan:

text
WriteFile 0644   -rw-r--r-- (644)
os.Create 0666   -rw-rw-r-- (664)
WriteFile 0600   -rw------- (600)
Chmod 0755       -rwxr-xr-x (755)
Mkdir 0755  : drwxr-xr-x (755) isDir=true ModeDir=true
fs.ModePerm : 777

Mari kita telaah baris demi baris program di atas:

  1. Baris 21-25 (WriteFile dengan 0644): permintaan 0644 lolos dari umask apa pun yang dibahas di sini; hasil cetakan -rw-r--r-- (644) menegaskan bit-nya utuh.
  2. Baris 29-43 (os.Create): fungsi ini meminta 0666, dan pada mesin ber-umask 0002 hasil akhirnya 0664 — bit write untuk pengguna lain dibersihkan umask, sedangkan bit write grup tetap utuh.
  3. Baris 47-51 (WriteFile dengan 0600): berkas token sengaja dibuat paling ketat; permission 600 berarti hanya pemilik yang bisa membacanya.
  4. Baris 54-58 (os.Chmod dengan 0755): berkas biasa.txt yang tadinya 644 berubah menjadi 755 persis seperti argumen — bukti Chmod tidak melewati umask.
  5. Baris 61-72 (os.Mkdir + os.Stat): direktori dengan 0755 tampil sebagai drwxr-xr-x; info.Mode()&fs.ModeDir terbukti true sama seperti info.IsDir().
  6. Baris 75 dan 85-89 (fs.ModePerm + oktal): konstanta fs.ModePerm mencetak 777 — topeng sembilan bit penuh; helper oktal memformat Perm() dengan %03o menjadi tiga digit oktal.

Permission kini terbaca dan terkelola, termasuk untuk berkas sensitif. Masalah berikutnya berkaitan langsung dengan berkas yang sifatnya sekali pakai: data perantara, hasil parsial, dan penulisan atomik membutuhkan berkas sementara yang namanya tidak bentrok. Chapter Temporary File membahas os.CreateTemp dan pola rename atomik.

Referensi #

Berkas Sumber

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

contoh.go

Unduh berkas
go
package main

import (
	"fmt"
	"io/fs"
	"os"
	"path/filepath"
)

func main() {
	// Direktori kerja sementara agar program aman dijalankan berulang.
	dir, err := os.MkdirTemp("", "contoh-perm-*")
	if err != nil {
		fmt.Println("gagal membuat direktori:", err)
		return
	}
	defer os.RemoveAll(dir)

	// Berkas data biasa: pemilik baca-tulis, lainnya baca saja.
	biasa := filepath.Join(dir, "biasa.txt")
	if err := os.WriteFile(biasa, []byte("data\n"), 0644); err != nil {
		fmt.Println("gagal menulis berkas:", err)
		return
	}
	laporkan(biasa, "WriteFile 0644")

	// os.Create meminta 0666; umask merampingkan hasil akhirnya.
	buat := filepath.Join(dir, "buat.txt")
	f, err := os.Create(buat)
	if err != nil {
		fmt.Println("gagal membuat berkas:", err)
		return
	}
	if _, err := f.WriteString("data\n"); err != nil {
		fmt.Println("gagal menulis:", err)
		f.Close()
		return
	}
	if err := f.Close(); err != nil {
		fmt.Println("gagal menutup berkas:", err)
		return
	}
	laporkan(buat, "os.Create 0666")

	// Berkas rahasia: hanya pemilik yang boleh membaca dan menulis.
	rahasia := filepath.Join(dir, "rahasia.txt")
	if err := os.WriteFile(rahasia, []byte("token\n"), 0600); err != nil {
		fmt.Println("gagal menulis berkas:", err)
		return
	}
	laporkan(rahasia, "WriteFile 0600")

	// os.Chmod mengubah permission berkas yang sudah ada, tanpa pengaruh umask.
	if err := os.Chmod(biasa, 0755); err != nil {
		fmt.Println("gagal mengubah permission:", err)
		return
	}
	laporkan(biasa, "Chmod 0755")

	// Direktori: huruf d memimpin notasi simboliknya.
	folder := filepath.Join(dir, "folder")
	if err := os.Mkdir(folder, 0755); err != nil {
		fmt.Println("gagal membuat direktori:", err)
		return
	}
	info, err := os.Stat(folder)
	if err != nil {
		fmt.Println("gagal membaca metadata:", err)
		return
	}
	fmt.Printf("Mkdir 0755  : %s (%s) isDir=%v ModeDir=%v\n",
		info.Mode(), oktal(info), info.IsDir(), info.Mode()&fs.ModeDir != 0)

	// fs.ModePerm adalah topeng sembilan bit permission penuh.
	fmt.Printf("fs.ModePerm : %o\n", fs.ModePerm)
}

// laporkan mencetak permission simbolik dan oktal sebuah berkas.
func laporkan(path, label string) {
	info, err := os.Stat(path)
	if err != nil {
		fmt.Println("gagal membaca metadata:", err)
		return
	}
	fmt.Printf("%-16s %s (%s)\n", label, info.Mode(), oktal(info))
}

// oktal memformat sembilan bit permission sebagai tiga digit oktal.
func oktal(info os.FileInfo) string {
	return fmt.Sprintf("%03o", info.Mode().Perm())
}