11

How can I get a file inode in Go?

I already can print it like this:

file := "/tmp/system.log"
fileinfo, _ := os.Stat(file)
fmt.Println(fileinfo.Sys())
fmt.Println(fileinfo)

Looking at Go implementation it was obvious looking for some stat method, but I still did not manage to find the structure definition for a Unix system.

How can I get the inode value directly?

Which file/s in the source code define the structure of Sys()?

SystematicFrank
  • 16,555
  • 7
  • 56
  • 102

3 Answers3

22

You can use a type assertion to get the underlying syscall.Stat_t from the fileinfo like this

package main

import (
    "fmt"
    "os"
    "syscall"
)

func main() {
    file := "/etc/passwd"
    fileinfo, _ := os.Stat(file)
    fmt.Printf("fileinfo.Sys() = %#v\n", fileinfo.Sys())
    fmt.Printf("fileinfo = %#v\n", fileinfo)
    stat, ok := fileinfo.Sys().(*syscall.Stat_t)
    if !ok {
        fmt.Printf("Not a syscall.Stat_t")
        return
    }
    fmt.Printf("stat = %#v\n", stat)
    fmt.Printf("stat.Ino = %#v\n", stat.Ino)
}
Nick Craig-Wood
  • 52,955
  • 12
  • 126
  • 132
  • Ignoring the error when you call os.Stat(file) above is not a good idea. Particularly when the file being searched for might not even be present. You can get a runtime panic if you dereference fileinfo without checking. I know it's just an example, but people might trip over this if they use the above model. – Randy Howard Feb 25 '18 at 22:06
  • What does the line, `stat, ok := fileinfo.Sys().(*syscall.Stat_t)` actually do? `fileinfo.Sys()` returns a *syscall.Stat_t according to `fmt.Printf("%T", fileinfo.Sys())` Some clarity will be very helpful – Brettski May 25 '19 at 19:14
  • 1
    @Brettski: `fileinfo.Sys()` gets you an `interface{}` value—possibly `nil`—that captures whatever the `os.Stat()` call got from the underlying system call. The `stat, ok := fileinfo.Sys().(*syscall.Stat_t)` line is a *type assertion* that says *I think what's in this interface value is a `*syscall.Stat_t`. If so, let me have it, and set the variable `ok` to `true`.* If `ok` is true, `stat` now has the pointer you expected (if !ok, `stat` is nil) and you can now use `stat.Ino` and other such fields. – torek Feb 02 '20 at 00:39
  • @Brettski, to expand on what @torek said: Go must balance between two mutually-excusive requirements when it comes to interfacing with the OS: on the one hand the API it provides in its stdlib should strive to be 100% homogenous (cross-platform), and on the other hand, quite often a programmer needs to get their hold on the nitty-gritty platform-specific details. That's why `os.(*Fileinfo).Stat()` returns a value of "any type"—`interface{}`: the exact (dynamic) type of the returned value is OS-specific, and the programmer is required to _assert_ its real type to obtain a properly-typed value. – kostix Feb 02 '20 at 15:30
  • @Brettski, for instance, I beleive, on Windows `os.(*FileInfo).Sys()` returns `*syscall.Win32FileAttributeData`—you can read about it by running `$ GOOS=windows go doc syscall.Win32FileAttributeData`. – kostix Feb 02 '20 at 15:38
11

You can do the following:

file := "/tmp/system.log"
var stat syscall.Stat_t
if err := syscall.Stat(file, &stat); err != nil {
    panic(err)
}
fmt.Println(stat.Ino)

Where stat.Ino is the inode you are looking for.

serejja
  • 22,901
  • 6
  • 64
  • 72
-1

Package syscall is now deprecated. See https://pkg.go.dev/golang.org/x/sys instead.

user98761
  • 475
  • 3
  • 8