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:
| Angka | Bit | Simbol | Hak yang diberikan |
|---|---|---|---|
| 7 | 111 | rwx | baca, tulis, eksekusi |
| 6 | 110 | rw- | baca, tulis |
| 5 | 101 | r-x | baca, eksekusi |
| 4 | 100 | r-- | baca saja |
| 0 | 000 | --- | 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:
info, _ := os.Stat("biasa.txt")
fmt.Println(info.Mode()) // -rw-r--r--
fmt.Println(info.Mode().Perm()) // -rw-r--r-- juga, tipe dibuangKarakter 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:
| Permintaan | umask 0022 (lazim) | umask 0002 (mesin demo) |
|---|---|---|
0644 | 0644 | 0644 |
0666 (default os.Create) | 0644 | 0664 |
0600 | 0600 | 0600 |
0755 | 0755 | 0755 |
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:
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:
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:
go run contoh.goOutput yang Dihasilkan:
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 : 777Mari kita telaah baris demi baris program di atas:
- Baris 21-25 (
WriteFiledengan0644): permintaan0644lolos dari umask apa pun yang dibahas di sini; hasil cetakan-rw-r--r-- (644)menegaskan bit-nya utuh. - Baris 29-43 (
os.Create): fungsi ini meminta0666, dan pada mesin ber-umask0002hasil akhirnya0664— bit write untuk pengguna lain dibersihkan umask, sedangkan bit write grup tetap utuh. - Baris 47-51 (
WriteFiledengan0600): berkas token sengaja dibuat paling ketat; permission600berarti hanya pemilik yang bisa membacanya. - Baris 54-58 (
os.Chmoddengan0755): berkasbiasa.txtyang tadinya644berubah menjadi755persis seperti argumen — buktiChmodtidak melewati umask. - Baris 61-72 (
os.Mkdir+os.Stat): direktori dengan0755tampil sebagaidrwxr-xr-x;info.Mode()&fs.ModeDirterbuktitruesama sepertiinfo.IsDir(). - Baris 75 dan 85-89 (
fs.ModePerm+oktal): konstantafs.ModePermmencetak777— topeng sembilan bit penuh; helperoktalmemformatPerm()dengan%03omenjadi 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 #
- Dokumentasi os: FileMode
— definisi bit tipe,
Perm, dan notasi simbolik. - Dokumentasi package io/fs
— konstanta
ModeDir,ModePerm, dan interfaceFileInfo. - Dokumentasi os: Create
— default permission
0666sebelum umask.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
contoh.go
Unduh berkaspackage 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())
}