Understanding Permission Denied Errors

Permission denied errors in Go appear as:

bash
open /etc/shadow: permission denied
mkdir /var/log/myapp: permission denied
stat /root/config: permission denied
exec: /path/to/script: permission denied

The error is of type *os.PathError with Err being os.ErrPermission.

Common Scenarios and Solutions

Scenario 1: Writing to Protected Directory

Problem code: ``go func main() { err := os.WriteFile("/etc/myapp/config.json", []byte("{}"), 0644) if err != nil { log.Fatal(err) // open /etc/myapp/config.json: permission denied } }

Solution - Use appropriate directory: ```go func getConfigPath() string { // Check for custom config path from environment if path := os.Getenv("MYAPP_CONFIG"); path != "" { return path }

// Use user-writable locations home, err := os.UserHomeDir() if err == nil { return filepath.Join(home, ".myapp", "config.json") }

// Fallback to temp directory return filepath.Join(os.TempDir(), "myapp", "config.json") }

func main() { configPath := getConfigPath()

// Create directory if needed dir := filepath.Dir(configPath) if err := os.MkdirAll(dir, 0755); err != nil { log.Fatal(err) }

err := os.WriteFile(configPath, []byte("{}"), 0644) if err != nil { log.Fatal(err) } } ```

Scenario 2: Running as Wrong User

Problem code: ``go func main() { // Running as non-root but accessing root-owned file data, err := os.ReadFile("/var/log/syslog") if err != nil { log.Fatal(err) // permission denied } }

Solution - Check and escalate privileges: ```go func main() { // Check if running as root if os.Geteuid() != 0 { fmt.Println("This program requires root privileges") os.Exit(1) }

data, err := os.ReadFile("/var/log/syslog") if err != nil { log.Fatal(err) } fmt.Println(string(data)) } ```

Or use sudo for specific operations: ``go func readFileWithSudo(path string) ([]byte, error) { cmd := exec.Command("sudo", "cat", path) return cmd.Output() }

Scenario 3: File Permission Bits

Problem code: ```go func main() { // Created file with wrong permissions os.WriteFile("secret.txt", []byte("password"), 0777) // Too permissive!

// Now can't read it os.Chmod("secret.txt", 0000) // No permissions at all data, err := os.ReadFile("secret.txt") if err != nil { log.Fatal(err) // permission denied } } ```

Solution - Set correct permissions: ```go func main() { // Create with restricted permissions err := os.WriteFile("secret.txt", []byte("password"), 0600) if err != nil { log.Fatal(err) }

// Fix existing file permissions err = os.Chmod("secret.txt", 0600) // Owner read/write only if err != nil { log.Fatal(err) }

// Or change ownership (requires root) err = os.Chown("secret.txt", os.Getuid(), os.Getgid()) if err != nil { log.Fatal(err) } } ```

Permission reference: ``go // Common permission modes const ( DirPerms = 0755 // drwxr-xr-x: dirs with listing FilePerms = 0644 // -rw-r--r--: files readable by others SecretPerms = 0600 // -rw-------: sensitive files ExecPerms = 0755 // -rwxr-xr-x: executable files )

Scenario 4: Executable Permission

Error: `` exec: /path/to/script.sh: permission denied

Solution - Set executable bit: ```go func runScript(path string) error { // Make executable if err := os.Chmod(path, 0755); err != nil { return fmt.Errorf("chmod: %w", err) }

// Run the script cmd := exec.Command(path) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr return cmd.Run() }

// Or run shell script directly without exec bit func runShellScript(path string) error { cmd := exec.Command("sh", path) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr return cmd.Run() } ```

Scenario 5: Directory Traversal Prevention

Problem code: ``go func main() { // User input directly used in path filename := getUserInput() path := filepath.Join("/var/data", filename) data, err := os.ReadFile(path) // If filename is "../../../etc/passwd", reads system file! }

Solution - Validate and sanitize paths: ```go func safeReadFile(baseDir, filename string) ([]byte, error) { // Clean the filename filename = filepath.Clean(filename)

// Construct full path fullPath := filepath.Join(baseDir, filename)

// Resolve to absolute path absBase, err := filepath.Abs(baseDir) if err != nil { return nil, err }

absPath, err := filepath.Abs(fullPath) if err != nil { return nil, err }

// Verify path is within base directory if !strings.HasPrefix(absPath, absBase+string(filepath.Separator)) { return nil, os.ErrPermission // Prevent directory traversal }

// Check if it's actually a file (not directory) info, err := os.Stat(absPath) if err != nil { return nil, err } if info.IsDir() { return nil, fmt.Errorf("path is a directory, not a file") }

return os.ReadFile(absPath) } ```

Scenario 6: Windows-Specific Permissions

Error on Windows: `` open C:\Program Files\MyApp\config.txt: Access is denied.

Solution for Windows: ```go func getAppDataPath() string { if runtime.GOOS == "windows" { // Use AppData for user-writable storage appData := os.Getenv("APPDATA") if appData == "" { appData = os.Getenv("LOCALAPPDATA") } return filepath.Join(appData, "MyApp") }

home, _ := os.UserHomeDir() return filepath.Join(home, ".myapp") }

// Windows-specific ACL handling func setWindowsPermissions(path string) error { if runtime.GOOS != "windows" { return nil // Not needed on Unix }

// Use icacls command for Windows ACLs cmd := exec.Command("icacls", path, "/grant", "Users:(R,W)") return cmd.Run() } ```

Robust Permission Handling

```go type FileHandler struct { baseDir string umask os.FileMode }

func NewFileHandler(baseDir string) (*FileHandler, error) { absDir, err := filepath.Abs(baseDir) if err != nil { return nil, err }

// Create base directory with secure permissions if err := os.MkdirAll(absDir, 0755); err != nil { return nil, err }

return &FileHandler{ baseDir: absDir, umask: 0077, // Restrict to owner only }, nil }

func (h *FileHandler) SafeWrite(name string, data []byte) error { path := filepath.Join(h.baseDir, name)

// Validate path absPath, err := filepath.Abs(path) if err != nil { return err } if !strings.HasPrefix(absPath, h.baseDir) { return os.ErrPermission }

// Create parent directory dir := filepath.Dir(absPath) if err := os.MkdirAll(dir, 0755); err != nil { return err }

// Write to temp file first tmpPath := absPath + ".tmp" if err := os.WriteFile(tmpPath, data, 0600); err != nil { return err }

// Atomic rename return os.Rename(tmpPath, absPath) }

func (h *FileHandler) SafeRead(name string) ([]byte, error) { path := filepath.Join(h.baseDir, name)

// Validate path absPath, err := filepath.Abs(path) if err != nil { return nil, err } if !strings.HasPrefix(absPath, h.baseDir) { return nil, os.ErrPermission }

return os.ReadFile(absPath) } ```

Checking Permissions Before Operations

```go func checkWritePermission(path string) error { // Check if directory is writable dir := filepath.Dir(path)

// Create directory if it doesn't exist if _, err := os.Stat(dir); os.IsNotExist(err) { if err := os.MkdirAll(dir, 0755); err != nil { return fmt.Errorf("cannot create directory: %w", err) } }

// Try to create a temp file tmpPath := filepath.Join(dir, ".write_test") f, err := os.Create(tmpPath) if err != nil { return fmt.Errorf("write permission denied: %w", err) } f.Close() os.Remove(tmpPath)

return nil } ```

Verification

```bash # Check file permissions ls -la /path/to/file

# Check directory permissions ls -ld /path/to/directory

# Check current user whoami id

# Check file ownership stat /path/to/file

# Try with elevated privileges sudo ./myapp ```