How to properly instantiate os.FileMode

Will H picture Will H · Mar 10, 2015 · Viewed 23.5k times · Source

I have seen countless examples and tutorials that show how to create a file and all of them "cheat" by just setting the permission bits of the file. I would like to know / find out how to properly instantiate os.FileMode to provide to a writer during creation / updating of a file.

A crude example is this below:

func FileWrite(path string, r io.Reader, uid, gid int, perms string) (int64, error){
    w, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0664)
    if err != nil {
        if path == "" {
            w = os.Stdout
        } else {
            return 0, err
        }
    }
    defer w.Close()

    size, err := io.Copy(w, r)

    if err != nil {
        return 0, err
    }
    return size, err
}

In the basic function above permission bits 0664 is set and although this may make sense sometimes I prefer to have a proper way of setting the filemode correctly. As seen above a common example would be that the UID / GID is known and already provided as int values and the perms being octal digits that were previously gathered and inserted into a db as a string.

Answer

Chris Hopkins picture Chris Hopkins · Mar 10, 2017

My fix has been to define my own constants as I couldn't find any in os or syscall:

    const (
        OS_READ = 04
        OS_WRITE = 02
        OS_EX = 01
        OS_USER_SHIFT = 6
        OS_GROUP_SHIFT = 3
        OS_OTH_SHIFT = 0

        OS_USER_R = OS_READ<<OS_USER_SHIFT
        OS_USER_W = OS_WRITE<<OS_USER_SHIFT
        OS_USER_X = OS_EX<<OS_USER_SHIFT
        OS_USER_RW = OS_USER_R | OS_USER_W
        OS_USER_RWX = OS_USER_RW | OS_USER_X

        OS_GROUP_R = OS_READ<<OS_GROUP_SHIFT
        OS_GROUP_W = OS_WRITE<<OS_GROUP_SHIFT
        OS_GROUP_X = OS_EX<<OS_GROUP_SHIFT
        OS_GROUP_RW = OS_GROUP_R | OS_GROUP_W
        OS_GROUP_RWX = OS_GROUP_RW | OS_GROUP_X

        OS_OTH_R = OS_READ<<OS_OTH_SHIFT
        OS_OTH_W = OS_WRITE<<OS_OTH_SHIFT
        OS_OTH_X = OS_EX<<OS_OTH_SHIFT
        OS_OTH_RW = OS_OTH_R | OS_OTH_W
        OS_OTH_RWX = OS_OTH_RW | OS_OTH_X

        OS_ALL_R = OS_USER_R | OS_GROUP_R | OS_OTH_R
        OS_ALL_W = OS_USER_W | OS_GROUP_W | OS_OTH_W
        OS_ALL_X = OS_USER_X | OS_GROUP_X | OS_OTH_X
        OS_ALL_RW = OS_ALL_R | OS_ALL_W
        OS_ALL_RWX = OS_ALL_RW | OS_GROUP_X
)

This then allows me to specify my intent directly:

        // Create any directories needed to put this file in them
        var dir_file_mode os.FileMode
        dir_file_mode = os.ModeDir | (OS_USER_RWX | OS_ALL_R)
        os.MkdirAll(dir_str, dir_file_mode)

I'm sure this could be improved by use of iota, and some more combinations of permissions, but it works for me for now.