8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ioutil.WriteFile のFileMode の挙動の意味がわからなかったので調べてみた

Posted at

ファイルを一括で記述したかったので、ioutil.WriteFile を使おうと思いました。この第三引数の部分がいまいち理解できていないので調べていました。

定義


func WriteFile(filename string, data []byte, perm os.FileMode) error

よくここにos.ModePrem とか書くけどあれなによ?というやつです。FileModeの定義も読んでみます。様々なデバイスファイルやモードが定義されています。単にファイルを書きたいときだと、os.ModePrem でオッケーで、Unix の0777のパーミッションがファイルに設定されるように見えます。

const (
    // The single letters are the abbreviations
    // used by the String method's formatting.
    ModeDir        FileMode = 1 << (32 - 1 - iota) // d: is a directory
    ModeAppend                                     // a: append-only
    ModeExclusive                                  // l: exclusive use
    ModeTemporary                                  // T: temporary file; Plan 9 only
    ModeSymlink                                    // L: symbolic link
    ModeDevice                                     // D: device file
    ModeNamedPipe                                  // p: named pipe (FIFO)
    ModeSocket                                     // S: Unix domain socket
    ModeSetuid                                     // u: setuid
    ModeSetgid                                     // g: setgid
    ModeCharDevice                                 // c: Unix character device, when ModeDevice is set
    ModeSticky                                     // t: sticky
    ModeIrregular                                  // ?: non-regular file; nothing else is known about this file

    // Mask for the type bits. For regular files, none will be set.
    ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice | ModeCharDevice | ModeIrregular

    ModePerm FileMode = 0777 // Unix permission bits
)

ファイルのパーミッションがうまく動作しない理由

それなら、普通に書きこむファイルのパーミッションを設定できるのでは?と思いしらべてみると、次のように書けます。

package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
)

func main() {
	ioutil.WriteFile("ModePerm.txt", []byte("ModePerm.txt"), os.ModePerm)
	fmt.Println(os.ModePerm.IsRegular())
	perm := "0777"
	perm32, _ := strconv.ParseUint(perm, 8, 32)
	ioutil.WriteFile("777.txt", []byte("some"), os.FileMode(perm32))
	fmt.Println(perm32)
	perm = "0644"
	perm32, _ = strconv.ParseUint(perm, 8, 32)
	ioutil.WriteFile("644.txt", []byte("some"), os.FileMode(perm32))
	fmt.Println(perm32)
	perm = "0666"
	perm32, _ = strconv.ParseUint(perm, 8, 32)
	ioutil.WriteFile("666.txt", []byte("some"), os.FileMode(perm32))
	// os.Chmod("./666.txt", os.FileMode(perm32))
}

これを実行すると、4つのファイルが生成されて、想定通りのパーミッションが設定されるといいのですが、そうなりません。

ls -l 
total 4
-rw-r--r-- 1 ushio ushio   4 Dec 21 08:45 644.txt
-rw-r--r-- 1 ushio ushio   4 Dec 21 08:45 666.txt
-rwxr-xr-x 1 ushio ushio   4 Dec 21 08:45 777.txt
-rwxr-xr-x 1 ushio ushio  12 Dec 21 08:45 ModePerm.txt
-rw-r--r-- 1 ushio ushio  47 Dec 21 08:11 go.mod
-rw-r--r-- 1 ushio ushio 809 Dec 21 08:44 main.go

なんでやねん!と思って私のGo師匠であるかえる師匠に聞いてみたのですが、こんなStackOverflowの記事を見つけてくれました。

これを読むと、これは、go の問題ではなく、OS で設定されている umaskの問題では?との答えがありました。

umask

umask() sets the calling process's file mode creation mask (umask) to mask & 0777 (i.e., only the file
permission bits of mask are used),and returns the previous value of the mask.

The umask is used by open(2), mkdir(2), and other system calls that create files to modify the permissions placed on newly created files or directories. Specifically, permissions in the umask are turned off from the mode argument to open(2) and mkdir(2).

Umaskは、openやmkdirのシステムコールで使われて、新しいファイルやディレクトリを作るときにパーミッションをモディファイするとあります。ここで、私が今使っているWSLのumaskを見てみましょう。

$ umask
022

022 ですね、これをマスクするということは、Unixのファイルパーミッション設定は、4(read) 2(write) 1(execute) なので、022は所有者以外の書き込み権限なので、それでマスクをすると、書き込み権限がなくなることになります。
実際の上の実行例を見てみると、644は正しく設定されているのに、666は、644に変更されています。ModePerm も元が777なのが、755に変更されています。

想定どおりのパーミッションを設定したい場合は?

では、想定通りのパーミッションを設定するにはどういう方法がよいでしょう?先ほどのStack Overflow からたどれるWikiで次のような記述があります。

In Unix-like systems, each file has a set of attributes that control who can read, write or execute it. When a program creates a file the file permissions are restricted by the mask. If the mask has a bit set to "1", then the corresponding initial file permission will be disabled. A bit set to "0" in the mask means that the corresponding permission will be determined by the program and the file system. In other words, the mask acts as a last-stage filter that strips away permissions as a file is created; each bit that is set to a "1" strips away its corresponding permission. Permissions may be changed later by users and programs using chmod.

Unixシステムでは、ファイルを作成した時にはマスクで、制限されてるとあります。パーミッションはchmodを使って後で変えられるかもしれないとあります。つまり、想定通りのパーミッション例えば、0666を設定したい場合は、

 os.Chmod("./666.txt", os.FileMode(perm32))

を追記すればいいことになります。そうすれば、666に無事設定されていることがわかると思います。

ls -l 
total 4
-rw-r--r-- 1 ushio ushio   4 Dec 21 08:49 644.txt
-rw-rw-rw- 1 ushio ushio   4 Dec 21 08:49 666.txt
-rwxr-xr-x 1 ushio ushio   4 Dec 21 08:49 777.txt
-rwxr-xr-x 1 ushio ushio  12 Dec 21 08:49 ModePerm.txt
-rw-r--r-- 1 ushio ushio  47 Dec 21 08:11 go.mod
-rw-r--r-- 1 ushio ushio 651 Dec 21 08:49 main.go
8
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?