Hack_Hut
Hack_Hut

Reputation: 184

Is there a function for finding the full file permissions?

I need to use Go to get the file permissions of a given file (specifically the SUID bit). Here are the permissions of an example file.

$ touch example_file.test
$ chmod 7777 example_file.test
$ ls -ltra example_file.test
    -rwsrwsrwt 1 luke users 0 Feb 25 21:53 example_file.test

$ stat -c "%a %n" example_file.test
     7777 example_file.test

Here is a tiny program illustrating the issue.

func main() {
    info, _ := os.Stat("example_file.test")
        fmt.Println(info.Mode().String())                        // ugtrwxrwxrwx
        fmt.Println(info.Mode().Perm().String())                 // -rwxrwxrwx
        fmt.Printf("permissions: %#o\n", info.Mode().Perm())     // permissions: 0777
}

The Go results are inconsistent because ugtrwxrwxrwx != 0777.

The following quote from the documentation indicates that this might be a cross-platform compatibility issue.

A FileMode represents a file's mode and permission bits. The bits have the same definition on all systems, so that information about files can be moved from one system to another portably. Not all bits apply to all systems. The only required bit is ModeDir for directories.

type FileMode uint32 The defined file mode bits are the most significant bits of the FileMode. The nine least-significant bits are the standard Unix rwxrwxrwx permissions. The values of these bits should be considered part of the public API and may be used in wire protocols or disk representations: they must not be changed, although new bits might be added.

Is this a limitation of Go?

Upvotes: 4

Views: 1734

Answers (2)

chuckx
chuckx

Reputation: 6877

It's worth noting that in addition to the octal representations not matching, neither do the string representations:

"-rwsrwsrwt" != "ugtrwxrwxrwx"`

This is not due to a limitation of the Go implementation, but is instead a consequence of it being implemented in a system agnostic manner.

From the FileMode documentation (emphasis mine):

A FileMode represents a file's mode and permission bits. The bits have the same definition on all systems, so that information about files can be moved from one system to another portably. Not all bits apply to all systems.

Because the implementation isn't meant to mimic the behavior of a specific system, the output isn't guaranteed to match the native tools in a given environment. However, all of the relevant data is there to work with.

If you wanted to mimic the output of stat you can just author some logic to do so.

Here's a quick example that tackles the octal representation:

package main

import (
    "fmt"
    "os"
)

func UnixPerm(m os.FileMode) (p uint32) {
    p = uint32(m.Perm())
    if m & os.ModeSetuid != 0 {
        p |= 04000
    }
    if m & os.ModeSetgid != 0 {
        p |= 02000
    }
    if m & os.ModeSticky != 0 {
        p |= 01000
    }
    return p
}

func main() {
    info, _ := os.Stat("example_file")
    fmt.Printf("FileMode.Perm(): %04o\n", info.Mode().Perm())
    fmt.Printf("UnixPerm(): %04o\n", UnixPerm(info.Mode()))
}

Output:

$ go run fileperm.go
FileMode.Perm(): 0777
UnixPerm(): 7777

$ stat --printf "Permissions: %a\n" example_file
Permissions: 7777

Upvotes: 4

Shang Jian Ding
Shang Jian Ding

Reputation: 2126

I need to use golang to get the file permissions of a given file (specifically the SUID bit)

Ok, add this: fmt.Printf("File has SUID bit set: %t\n", info.Mode()&os.ModeSetuid != 0)

The golang results are inconsistant because ugtrwxrwxrwx != 0777

info.Mode() and info.Mode().Perm() are different things, though related.

info.Mode().Perm() contains a subset of info.Mode(). info.Mode().Perm() is simply a little convenience function to do a bit-wise AND with a constant mask 0777.

Is this a limitation of go?

No

Upvotes: 1

Related Questions