Golang Functions os.Stat() vs os.Lstat()

Chao Geng
4 min readApr 23, 2024

--

This tutorial will compare two functions of Golang os.Stat() and os.Lstat() and give the scenarios of them.

os.Stat() and os.Lstat() are both functions provided by the package os for retrieving information about files or directories. However, they behave slightly differently, especially when dealing with symbolic links.

os.Stat()

The function os.Stat returns an implementation of the FileInfo interface for the specified path. If the path points to a symbolic link, os.Stat will return the information of the file or directory that the symbolic link refers to. That is, it follows the symbolic link and returns the file information of the link target.

os.Lstat()

The function os.Lstat also returns an implementation of the FileInfo interface for the specified path. However, when the path points to a symbolic link, it returns the information of the symbolic link itself, rather than the file or directory it refers to.

Scenarios

Both functions return an implementation of the FileInfo interface for the specified path. We can call methods such as Name, IsDir, Size, Mode on the returned FileInfo object to get detailed information about the file.

Let us create new files and directory like:

# mkdir /tmp/test
# cd /tmp/test/
# echo 1 > file
# ln -s /tmp/test link_to_tmp
]# ls -l
total 4
lrwxrwxrwx 1 root root 9 Apr 23 13:57 link_to_tmp -> /tmp/test
-rw-r--r-- 1 root root 2 Apr 23 13:57 file
  • mkdir /tmp/test: Creates a new directory named test inside the tmp directory.
  • cd /tmp/test/: Changes the current working directory to /tmp/test/.
  • echo 1 > file: Creates a new file named file inside the /tmp/test/directory and writes the character "1" into it.
  • ln -s /tmp/test link_to_tmp: Creates a symbolic link named link_to_tmp in the current directory that points to the /tmp/test/directory. The "-s" flag indicates that it should create a symbolic link rather than a hard link.
  • ls -l: Lists the contents of the current directory.

And then write two functions to print detailed information about the file, such as file name, file mode, size and so on.

package main

import (
"fmt"
"os"
)

// With os.Stat
func WithStat(path string) error {
info, err := os.Stat(path)
if err != nil {
return err
}

fmt.Printf("File %s from funcstion os.Stat:\n", path)
fmt.Printf("Name: %v\n", info.Name())
fmt.Printf("Size: %v\n", info.Size())
fmt.Printf("Mode: %v\n", info.Mode())
fmt.Printf("ModeTime: %v\n", info.ModTime())
fmt.Printf("IsDir : %v\n", info.IsDir())

return nil
}

// With os.Lstat
func WithLstat(path string) error {
info, err := os.Lstat(path)
if err != nil {
return err
}

fmt.Printf("File %s from funcstion os.Lstat:\n", path)
fmt.Printf("Name: %v\n", info.Name())
fmt.Printf("Size: %v\n", info.Size())
fmt.Printf("Mode: %v\n", info.Mode())
fmt.Printf("ModeTime: %v\n", info.ModTime())
fmt.Printf("IsDir : %v\n", info.IsDir())

return nil
}

First, let us run with the regular file

func main() {
WithStat("/tmp/test/file")
WithLstat("/tmp/test/file")
}

Output:

File /tmp/test/file from funcstion os.Stat:
Name: file
Size: 2
Mode: -rw-r--r--
ModeTime: 2024-04-23 13:57:08.403387436 +0800 CST
IsDir : false


File /tmp/test/file from funcstion os.Lstat:
Name: file
Size: 2
Mode: -rw-r--r--
ModeTime: 2024-04-23 13:57:08.403387436 +0800 CST
IsDir : false

We can see that they are all same. That means there is not any difference between os.Stat() and os.Lstat() for regular files or directories.

Second, let us run with the soft link file link_to_tmp

func main() {
WithStat("/tmp/test/link_to_tmp")
WithLstat("/tmp/test/link_to_tmp")
}

Output:

File /tmp/test/link_to_tmp from funcstion os.Stat:
Name: link_to_tmp
Size: 37
Mode: drwxr-xr-x
ModeTime: 2024-04-23 14:12:41.424154018 +0800 CST
IsDir : true


File /tmp/test/link_to_tmp from funcstion os.Lstat:
Name: link_to_tmp
Size: 9
Mode: Lrwxrwxrwx
ModeTime: 2024-04-23 13:57:27.078422722 +0800 CST
IsDir : false

We can see that they are not same except for file name. That means there are differences between os.Stat() and os.Lstat() for link file. os.Stat()follows the symbolic link and returns the file information of the link target. However, os.Lstat() returns only the symbolic link itself.

Last, how about hard link file

A hard link is actually a pointer to the inode of the source file. The system does not reallocate inode for it. Hard links do not create new inode. No matter how many hard links there are, they all point to the same inode node. Creating a new hard link only increases the number of connections to the node. As long as the number of connections to the node is not 0, the file will always exist, regardless of whether you delete the source file or the linked file.
As long as one exists, the file exists, which is actually the concept of reference counting. When you modify any of the source files or connection files, the other files will be modified synchronously.

Run with hard link file:

func main() {
WithStat("/tmp/hard_link")
WithLstat("/tmp/hard_link")
}

Output:

File /tmp/hard_link from funcstion os.Stat:
Name: hard_link
Size: 13
Mode: -rw-r--r--
ModeTime: 2024-05-06 09:22:07.681437649 +0800 CST
IsDir : false


File /tmp/hard_link from funcstion os.Lstat:
Name: hard_link
Size: 13
Mode: -rw-r--r--
ModeTime: 2024-05-06 09:22:07.681437649 +0800 CST
IsDir : false

We can see that they are all same. That means there is not any difference between os.Stat() and os.Lstat() for hard link file.

In summary, when working with the file system, os.Stat() is used when we want information about the target of a symbolic link, while os.Lstat() is used when we want information about the symbolic link itself.

--

--