unixmode

package module
v0.0.0-...-d14c1c1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 21, 2023 License: BSD-3-Clause Imports: 5 Imported by: 1

README

go-unixmode

This module is intended to help developers work directly with file permissions on Linux.

As of the time this module was built, there are no valid String() method in Go which correctly, in a POSIX manner, identifies sticky / set uid or gid bits in 9 character permission string. Hence the birth of this module.

One can go either way and preserve information:

um := unixmode.Parse("rwsr-sr-x")
fmt.Printf("%05o", um) // 06755

perm := unixmode.Mode(06755)
fmt.Println(perm.PermString()) // rwsr-sr-x

More documentation: https://pkg.go.dev/github.com/pschou/go-unixmode

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrorModeLength = errors.New("Invalid Mode Length")
	ErrorMode       = errors.New("Invalid Mode")
)

Functions

func Chmod

func Chmod(name string, m Mode) error

Functional call to os.Chmod with the ability to set permissions based on the Mode value.

func FileModePermString

func FileModePermString(m fs.FileMode) string

Return the lower 12 bits in a UNIX permission string format

Example
stat, _ := os.Lstat("/tmp")
m := stat.Mode()
fmt.Printf("mode: %q\n", unixmode.FileModePermString(m))
Output:

mode: "rwxrwxrwt"

func FileModeString

func FileModeString(m fs.FileMode) string

Return the Mode with the TypeLetter + PermString + " " The extra space is for compatibility with 4.4BSD strmode

Example
stat, _ := os.Lstat("/tmp")
m := stat.Mode()
fmt.Printf("mode: %q\n", unixmode.FileModeString(m))
Output:

mode: "drwxrwxrwt "
Example (Character)
if stat, err := os.Lstat("/dev/lp0"); err != nil {
	log.Fatal("Could not stat character device", err)
} else {
	m := stat.Mode()
	fmt.Printf("mode: %q\n", unixmode.FileModeString(m))
}
Output:

mode: "crw-rw---- "
Example (Socket)
if stat, err := os.Lstat("/dev/log"); err != nil {
	log.Fatal("Could not stat socket", err)
} else {
	m := stat.Mode()
	fmt.Printf("mode: %q\n", unixmode.FileModeString(m))
}
Output:

mode: "srw-rw-rw- "

func FileModeTypeLetter

func FileModeTypeLetter(m fs.FileMode) byte

Return the TypeLetter defined in the FileMode bits.

Example
if stat, err := os.Lstat("/dev/null"); err != nil {
	log.Fatal("Could not stat character device", err)
} else {
	m := stat.Mode()
	fmt.Printf("mode: %c\n", unixmode.FileModeTypeLetter(m))
}
Output:

mode: c

func ParseFileMode

func ParseFileMode(in string) (fs.FileMode, error)

A function to parse a fs.FileMode string into the standard fs.FileMode

Example
val := "augrwxr--r--"
fm, _ := unixmode.ParseFileMode(val)
fmt.Printf("File Mode: %o %s == %s\n", fm, val, fm)
Output:

File Mode: 10060000744 augrwxr--r-- == augrwxr--r--

Types

type Mode

type Mode uint16

A Mode represents a file's mode and permission bits. The bits are used in the POSIX definition.

const (

	// Types (upper 4 bits)
	ModeTypeMask   Mode = 0170000 /* type of file mask */
	ModeNamedPipe  Mode = 0010000 /* named pipe (fifo) */
	ModeCharDevice Mode = 0020000 /* character special */
	ModeDir        Mode = 0040000 /* directory */
	ModeDevice     Mode = 0060000 /* block special */
	ModeRegular    Mode = 0100000 /* regular */
	ModeSymlink    Mode = 0120000 /* symbolic link */
	ModeSocket     Mode = 0140000 /* socket */

	// Set ID / Sticky (middle 3 bits)
	ModeSetuid Mode = 0004000 /* set-user-ID on execution */
	ModeSetgid Mode = 0002000 /* set-group-ID on execution */
	ModeSticky Mode = 0001000 /* save swapped text even after use */

	// Permissions (lower 9 bits)
	ModeUserMask   Mode = 0000700 /* RWX mask for owner */
	ModeReadUser   Mode = 0000400 /* R for owner */
	ModeWriteUser  Mode = 0000200 /* W for owner */
	ModeExecUser   Mode = 0000100 /* X for owner */
	ModeGroupMask  Mode = 0000070 /* RWX mask for group */
	ModeReadGroup  Mode = 0000040 /* R for group */
	ModeWriteGroup Mode = 0000020 /* W for group */
	ModeExecGroup  Mode = 0000010 /* X for group */
	ModeOtherMask  Mode = 0000007 /* RWX mask for other */
	ModeReadOther  Mode = 0000004 /* R for other */
	ModeWriteOther Mode = 0000002 /* W for other */
	ModeExecOther  Mode = 0000001 /* X for other */

)

The defined file mode bits are the most significant bits of the Mode. The values of these bits are defined by the Unix filemode standard and may be used in wire protocols or disk representations: they must not be changed, although new bits might be added.

func FileModePerm

func FileModePerm(m fs.FileMode) Mode

Perm returns the Unix permission bits in FileMode m. That is, it takes the GoLang fs.FileMode and changes the bit flags into the Mode permission format without maintaining the type.

This is useful for setting permissions on a linux system, like this:

Similarly, one can achieve the same result by doing:

m, err := os.Stat("/tmp")
myPerm := unixmode.FileModePerm(m.FileMode())
os.Chmod(name, myPerm)

Note: GoLang reserves bits 10-12 along with 3 additional random bit locations for sticky bit values. By keeping 10-12 clear and parsing down the higher order bits on top of these three bits, it has effectively disabled the lower 3 bits from being used. This methodology is to create "Go's portable mode bits"

This seems like a duplication of efforts for an extra 3 higher order bits to be assigned when the lower 3 bits are used when calling os.Chmod and conversely the syscallMode. This duplication of bit usage makes life easier for developers only interested in the lower 12, however creating an additional higher order bits just seems odd when the lower bits are still used. One may ask "why not re-use bits which already have a pretty stable definition?" With 32 bits in fs.FileMode and twice the declaration of what defines a sticky bit, they would have a valid point. This leaves one to wonder: when bits are duplicated, one can be left playing the game: "guess the bit which was used" when calling Chmod? Besides, what can go wrong when bits at two locations in a register could trigger SUID on a root owned executable?

Ref: https://cs.opensource.google/go/go/+/master:src/os/file_posix.go;l=62-75

Example (RawCharacter)
if stat, err := os.Lstat("/dev/lp0"); err != nil {
	log.Fatal("Could not stat character device", err)
} else {
	m := stat.Mode()
	fmt.Printf("mode: %c %04o\n", unixmode.FileModeTypeLetter(m), unixmode.FileModePerm(m))
}
Output:

mode: c 0660
Example (SUDO)
if stat, err := os.Lstat("/usr/bin/sudo"); err != nil {
	log.Fatal("Could not stat character device", err)
} else {
	m := stat.Mode()
	fmt.Printf("mode: %c %04o\n", unixmode.FileModeTypeLetter(m), unixmode.FileModePerm(m))
}
Output:

mode: - 4111

func New

func New(m fs.FileMode) Mode

Create a unixmode.Mode(uint16) from a FileMode(uint32) by cross referencing bits.

Example
fm := fs.FileMode(0777 | fs.ModeSetuid)
fmt.Printf("Full unix mode: %07o\n", unixmode.New(fm))
fmt.Printf("Unix perms: %05o\n", unixmode.New(fm).Perm())
Output:

Full unix mode: 0104777
Unix perms: 04777

func Parse

func Parse(in string) (Mode, error)

Parse will take three formats and convert them into a Mode with the bits set:

"rwsrwxrwx" - 9 bytes, Returns the lower 12 bits set

"-rwsrwxrwx" - 10 bytes, Lower 12 bits and includes setting the file ModeType

"-rwsrwxrwx " - 11 bytes, Compatibility with newer os's with ACLs and SELinux contexts

Example
if m, err := unixmode.Parse("rwxrwx---"); err != nil {
	log.Fatal(err)
} else {
	fmt.Printf("mode: %04o\n", m)
}
Output:

mode: 0770
Example (DirectoryMixedRaw)
if m, err := unixmode.Parse("d-w-r-S-wT"); err != nil {
	log.Fatal(err)
} else {
	fmt.Printf("mode: %04o\n", m)
}
Output:

mode: 43242
Example (Invalid)
if m, err := unixmode.Parse("drwSrwSrwS "); err != nil {
	fmt.Println("Err:", err)
} else {
	fmt.Printf("mode: %04o\n", m)
}
Output:

Err: Invalid 'S' at position 9
Example (Mixed)
if m, err := unixmode.Parse("-w-r-S-wT"); err != nil {
	log.Fatal(err)
} else {
	fmt.Printf("mode: %04o\n", m.Perm())
}
Output:

mode: 3242

func (Mode) FileMode

func (m Mode) FileMode() fs.FileMode

Create a FileMode(uint32) from a unixmode.Mode(uint16) by cross referencing bits.

Example
fm, _ := unixmode.Parse("dr-sr-srwx")

// This is the standard go FileMode output, basically only "rwx"
fmt.Printf("Unix mode: %q\n", fm.PermString())

// Note that Go FileMode does not show suid bits
fmt.Printf("Go FileMode: %q\n", fm.FileMode().Perm())
Output:

Unix mode: "r-sr-srwx"
Go FileMode: "-r-xr-xrwx"

func (Mode) IsDir

func (m Mode) IsDir() bool

IsDir reports whether m describes a directory. That is, it tests for the ModeDir bit being set in m.

Example
m, _ := unixmode.Parse("dr-s-wSr-T")
fmt.Printf("is dir: %v\n", m.IsDir())
Output:

is dir: true

func (Mode) IsRegular

func (m Mode) IsRegular() bool

IsRegular reports whether m describes a regular file. That is, it tests that the ModeRegular bit is the only type set.

Example
m, _ := unixmode.Parse("-r-s-wSr-T")
fmt.Printf("is regular: %v\n", m.IsRegular())
Output:

is regular: true

func (Mode) Perm

func (m Mode) Perm() Mode

Perm returns the Unix permission bits in m. That is, it returns the Mode & 07777, as the lower 12 bits describe the full permissions.

Example
m := unixmode.Mode(041755)
fmt.Printf("mode: %04o\n", m.Perm())
Output:

mode: 1755
Example (Parse)
m, _ := unixmode.Parse("r-s-wSr-T")
fmt.Printf("mode: %04o\n", m.Perm())
Output:

mode: 7524

func (Mode) PermString

func (m Mode) PermString() string

Return the lower 12 bits in a UNIX permission string format

Example
m := unixmode.Mode(0755)
fmt.Printf("mode: %q\n", m.PermString())
Output:

mode: "rwxr-xr-x"
Example (Directory)
m := unixmode.Mode(040755)
fmt.Printf("mode: %q\n", m.PermString())
Output:

mode: "rwxr-xr-x"
Example (SetUidBits)
m := unixmode.Mode(06755)
fmt.Printf("mode: %q\n", m.PermString())
Output:

mode: "rwsr-sr-x"

func (Mode) String

func (m Mode) String() string

Return the Mode with the TypeLetter + PermString + " " The extra space is for compatibility with 4.4BSD strmode

Example
m := unixmode.Mode(041755)
fmt.Printf("mode: %q\n", m.String())
Output:

mode: "drwxr-xr-t "

func (Mode) Type

func (m Mode) Type() Mode

Type returns type bits in m (m & ModeTypeMask).

func (Mode) TypeLetter

func (m Mode) TypeLetter() byte

Return the TypeLetter defined in the Mode bits.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL