Copy Files in Go While Preserving Permissions

Copy a file in Go with os.Open, io.Copy to os.Create, then os.Chmod from os.FileInfo.Mode; or use os.ReadFile and os.WriteFile with the source mode; os.Rename moves within a filesystem rather than copying.

Published

Updated

Read time 2 min read

Reviewed byDeepak Prasad

Copy Files in Go While Preserving Permissions

Copying a file in Go means reading bytes from a source path and writing them to a destination path, then matching permission bits (and optionally ownership, which requires OS-specific APIs not covered here). os.Stat on the source yields fs.FileInfo; use info.Mode() with os.Chmod on the destination after data is written. For remote copies, tools and articles on SSH/rsync complement in-process Go (copy over SSH).

Tested with Go 1.24 on Linux.


Copy with io.Copy and os.Chmod

go
package main

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

func main() {
	const srcPath = "source_file.txt"
	const dstPath = "/tmp/destination_file.txt"

	src, err := os.Open(srcPath)
	if err != nil {
		panic(err)
	}
	defer src.Close()

	info, err := src.Stat()
	if err != nil {
		panic(err)
	}

	dst, err := os.Create(dstPath)
	if err != nil {
		panic(err)
	}
	defer dst.Close()

	if _, err := io.Copy(dst, src); err != nil {
		panic(err)
	}
	dst.Close()

	if err := os.Chmod(dstPath, info.Mode()); err != nil {
		panic(err)
	}

	di, _ := os.Stat(dstPath)
	fmt.Println("dst mode:", di.Mode())
}

Run locally with a real source_file.txt. The critical call is io.Copy(dst, src) (writer first, reader second).


Copy with os.ReadFile / os.WriteFile

os.WriteFile(path, data, perm) sets mode on create; pass the source Mode().Perm() or full Mode() as appropriate:

go
package main

import (
	"os"
)

func main() {
	data, err := os.ReadFile("source_file.txt")
	if err != nil {
		panic(err)
	}
	info, err := os.Stat("source_file.txt")
	if err != nil {
		panic(err)
	}
	if err := os.WriteFile("/tmp/destination_file.txt", data, info.Mode().Perm()); err != nil {
		panic(err)
	}
}

About os.Rename

os.Rename moves a path within the same filesystem; it is not a general-purpose copy and will not duplicate bytes across devices. After a rename, metadata usually travels with the inode; still verify modes if you split copy and metadata steps.


Summary

To preserve permissions when copying a file in Go, copy bytes with io.Copy (destination writer first, source reader second: io.Copy(dst, src)) or use os.WriteFile with the source FileMode, then align the destination mode with os.Chmod if needed. Do not treat os.Rename as a portable copy across devices. Prefer os.ReadFile over deprecated ioutil helpers.


References


Frequently Asked Questions

1. How do I copy a file and keep permissions in Go?

Read the source os.FileInfo with os.Stat, copy bytes with io.Copy between *os.File values, then os.Chmod the destination to srcInfo.Mode(). Perm bits come from Mode().Perm() is a subset view; prefer assigning the full Mode for special bits when needed.

2. Why is io.Copy(dst, src) order important?

io.Copy writes to the first argument (destination writer) from the second argument (source reader). Swapping them breaks the copy.

3. Can I use os.Rename to copy across disks?

Rename only moves within the same filesystem. Cross-device moves fail; use copy-then-delete instead.

4. What replaced ioutil.ReadFile and WriteFile?

Use os.ReadFile and os.WriteFile since Go 1.16; pass the source FileMode as WriteFile’s perm argument to preserve permission bits when creating the destination.

5. Where can I read about chmod values?

See general chmod documentation on your platform; Go applies Unix permission bits through os.Chmod on supported systems.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive …