Skip to main content
โšก Calmops

File Operations and Permissions in Go

File Operations and Permissions in Go

Working with file permissions and metadata is essential for system programming. Go provides comprehensive tools for managing file operations and permissions. This guide covers practical techniques.

File Metadata

Getting File Information

package main

import (
	"fmt"
	"log"
	"os"
)

func main() {
	// Get file info
	info, err := os.Stat("example.txt")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Name:", info.Name())
	fmt.Println("Size:", info.Size(), "bytes")
	fmt.Println("Mode:", info.Mode())
	fmt.Println("Modified:", info.ModTime())
	fmt.Println("Is directory:", info.IsDir())
}

File Mode and Permissions

package main

import (
	"fmt"
	"log"
	"os"
)

func main() {
	info, err := os.Stat("example.txt")
	if err != nil {
		log.Fatal(err)
	}

	mode := info.Mode()
	fmt.Printf("Mode: %v\n", mode)
	fmt.Printf("Permissions: %o\n", mode.Perm())

	// Check file type
	fmt.Println("Is regular file:", mode.IsRegular())
	fmt.Println("Is directory:", mode.IsDir())
	fmt.Println("Is symlink:", mode&os.ModeSymlink != 0)
}

Changing Permissions

Chmod Operations

package main

import (
	"log"
	"os"
)

func main() {
	// Change file permissions
	err := os.Chmod("example.txt", 0644)
	if err != nil {
		log.Fatal(err)
	}

	// Make executable
	err = os.Chmod("script.sh", 0755)
	if err != nil {
		log.Fatal(err)
	}

	// Read-only
	err = os.Chmod("readonly.txt", 0444)
	if err != nil {
		log.Fatal(err)
	}
}

Permission Constants

package main

import (
	"fmt"
	"os"
)

func main() {
	// Common permission modes
	modes := map[string]os.FileMode{
		"Owner read/write": 0600,
		"Owner rwx": 0700,
		"Owner rw, group r, other r": 0644,
		"Owner rwx, group rx, other rx": 0755,
		"Read-only": 0444,
		"Executable": 0755,
	}

	for name, mode := range modes {
		fmt.Printf("%s: %o\n", name, mode)
	}
}

File Ownership

Changing Owner

package main

import (
	"log"
	"os"
	"os/user"
	"strconv"
)

func main() {
	// Get current user
	currentUser, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Current user:", currentUser.Username)
	fmt.Println("UID:", currentUser.Uid)
	fmt.Println("GID:", currentUser.Gid)

	// Change file owner (requires appropriate permissions)
	uid, _ := strconv.Atoi(currentUser.Uid)
	gid, _ := strconv.Atoi(currentUser.Gid)

	err = os.Chown("example.txt", uid, gid)
	if err != nil {
		log.Fatal(err)
	}
}

Advanced File Operations

package main

import (
	"log"
	"os"
)

func main() {
	// Create symbolic link
	err := os.Symlink("original.txt", "link.txt")
	if err != nil {
		log.Fatal(err)
	}

	// Read symlink target
	target, err := os.Readlink("link.txt")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Link target:", target)

	// Check if file is symlink
	info, _ := os.Lstat("link.txt")
	if info.Mode()&os.ModeSymlink != 0 {
		fmt.Println("File is a symbolic link")
	}
}
package main

import (
	"log"
	"os"
)

func main() {
	// Create hard link
	err := os.Link("original.txt", "hardlink.txt")
	if err != nil {
		log.Fatal(err)
	}

	// Both files point to same inode
	info1, _ := os.Stat("original.txt")
	info2, _ := os.Stat("hardlink.txt")

	fmt.Println("Same file:", info1.Size() == info2.Size())
}

Renaming and Moving Files

package main

import (
	"log"
	"os"
)

func main() {
	// Rename file
	err := os.Rename("old_name.txt", "new_name.txt")
	if err != nil {
		log.Fatal(err)
	}

	// Move file to different directory
	err = os.Rename("file.txt", "/path/to/new/location/file.txt")
	if err != nil {
		log.Fatal(err)
	}
}

Practical Examples

File Backup with Permissions

package main

import (
	"io"
	"log"
	"os"
)

func backupFile(source string) error {
	// Get source file info
	info, err := os.Stat(source)
	if err != nil {
		return err
	}

	// Create backup
	backup := source + ".backup"
	srcFile, err := os.Open(source)
	if err != nil {
		return err
	}
	defer srcFile.Close()

	dstFile, err := os.Create(backup)
	if err != nil {
		return err
	}
	defer dstFile.Close()

	// Copy content
	_, err = io.Copy(dstFile, srcFile)
	if err != nil {
		return err
	}

	// Preserve permissions
	err = os.Chmod(backup, info.Mode().Perm())
	if err != nil {
		return err
	}

	return nil
}

func main() {
	err := backupFile("important.txt")
	if err != nil {
		log.Fatal(err)
	}
}

Directory Listing with Permissions

package main

import (
	"fmt"
	"log"
	"os"
)

func listDirectory(path string) error {
	entries, err := os.ReadDir(path)
	if err != nil {
		return err
	}

	for _, entry := range entries {
		info, _ := entry.Info()
		mode := info.Mode()

		// Format: permissions type name size
		typeChar := "-"
		if mode.IsDir() {
			typeChar = "d"
		}

		fmt.Printf("%s%o %s %d\n", typeChar, mode.Perm(), entry.Name(), info.Size())
	}

	return nil
}

func main() {
	err := listDirectory(".")
	if err != nil {
		log.Fatal(err)
	}
}

Secure File Creation

package main

import (
	"log"
	"os"
)

func createSecureFile(path string, content []byte) error {
	// Create file with restricted permissions
	file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}
	defer file.Close()

	// Write content
	_, err = file.Write(content)
	if err != nil {
		return err
	}

	return nil
}

func main() {
	err := createSecureFile("secret.txt", []byte("Secret data"))
	if err != nil {
		log.Fatal(err)
	}

	// Verify permissions
	info, _ := os.Stat("secret.txt")
	fmt.Printf("Permissions: %o\n", info.Mode().Perm())
}

Temporary Files

package main

import (
	"fmt"
	"io"
	"log"
	"os"
)

func main() {
	// Create temporary file
	tmpFile, err := os.CreateTemp("", "temp-*.txt")
	if err != nil {
		log.Fatal(err)
	}
	defer os.Remove(tmpFile.Name())

	fmt.Println("Temp file:", tmpFile.Name())

	// Write to temp file
	_, err = io.WriteString(tmpFile, "Temporary data")
	if err != nil {
		log.Fatal(err)
	}

	tmpFile.Close()

	// Create temporary directory
	tmpDir, err := os.MkdirTemp("", "temp-dir-*")
	if err != nil {
		log.Fatal(err)
	}
	defer os.RemoveAll(tmpDir)

	fmt.Println("Temp directory:", tmpDir)
}

File Locking Pattern

package main

import (
	"log"
	"os"
	"time"
)

func lockFile(path string) error {
	lockFile := path + ".lock"

	// Check if lock exists
	if _, err := os.Stat(lockFile); err == nil {
		return fmt.Errorf("file is locked")
	}

	// Create lock file
	file, err := os.Create(lockFile)
	if err != nil {
		return err
	}
	file.Close()

	// Defer removal of lock
	defer os.Remove(lockFile)

	// Do work
	time.Sleep(1 * time.Second)

	return nil
}

func main() {
	err := lockFile("important.txt")
	if err != nil {
		log.Fatal(err)
	}
}

Best Practices

โœ… Good Practices

// Check file existence before operations
if _, err := os.Stat(path); err == nil {
	// File exists
}

// Use appropriate permissions
os.Chmod(path, 0644) // Regular file
os.Chmod(path, 0755) // Executable

// Preserve permissions when copying
info, _ := os.Stat(src)
os.Chmod(dst, info.Mode().Perm())

// Use defer for cleanup
file, _ := os.Open(path)
defer file.Close()

// Handle permission errors
if os.IsPermission(err) {
	// Handle permission denied
}

โŒ Anti-Patterns

// Don't ignore permission errors
os.Chmod(path, 0644) // Should check error

// Don't use overly permissive modes
os.Chmod(path, 0777) // Too permissive

// Don't assume file exists
// Always check before operations

// Don't forget to close files
file, _ := os.Open(path)
// Missing defer file.Close()

Resources

Summary

File operations and permissions in Go:

  • Use os.Stat() for file metadata
  • Use os.Chmod() for permission changes
  • Use appropriate permission modes
  • Preserve permissions when copying
  • Handle permission errors properly
  • Use symbolic and hard links appropriately
  • Create temporary files securely

With these techniques, you can effectively manage file operations and permissions in Go applications.

Comments