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
Symbolic Links
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")
}
}
Hard Links
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
- Go os Package Documentation
- Go os/user Package Documentation
- Unix File Permissions
- Go File Operations
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