Golang中如何解决对权限为600的目录写入问题

Golang中如何解决对权限为600的目录写入问题 我正在尝试将应用部署到 Heroku,它会在 /tmp 目录中临时存储上传的文件。

ioutil.TempDir 可以在 /tmp 目录内正常创建目录。因为是 Heroku,权限被设置为 0600,但所有者与运行应用的用户相同。

问题出现在我调用 os.Create 时,最终会抛出权限错误:

2019-12-12T12:09:21.681565+00:00 app[web.1]: 2019/12/12 12:09:21 open /tmp/stream-hot146966131/stream/1080p_001.ts: permission denied
2019-12-12T12:09:21.681571+00:00 app[web.1]: 2019/12/12 12:09:21 drw-------

我添加了日志记录来在日志中显示错误,并显示 /tmp/stream-hot146966131/stream 的权限,正如你所见,它们设置是正确的。 如果 stream 目录不存在,则会创建它(该名称可能不同,它基于请求的 URL)

当然,当我在我的系统(Windows)上运行时,一切正常。

代码:

var tempDir string

func streamHandler(w http.ResponseWriter, r *http.Request) {
	var rex = regexp.MustCompile("^\\/([a-zA-Z0-9]*)\\/([a-zA-Z0-9_]*\\.(?:m3u8|ts))$")
	url := rex.FindStringSubmatch(r.URL.String())
	if r.Method == "PUT" {
		if url == nil {
			http.Error(w, "URL has to be a path to HLS fragment/playlist.", http.StatusBadRequest)
			return
		}
		stat, err := os.Stat(filepath.Join(tempDir, url[1]))
		if os.IsNotExist(err) {
			os.MkdirAll(filepath.Join(tempDir, url[1]), 0666)
		}
		f, err := os.Create(filepath.Join(tempDir, url[1], url[2]))
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			log.Println(err.Error())
			if stat != nil {
				log.Println(stat.Mode())
			}
			return
		}
		defer f.Close()
		_, err = io.Copy(f, r.Body)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		w.WriteHeader(200)
		w.Write([]byte("OK"))
		return
	}
}

func registerHandlers() {
	var err error
	tempDir, err = ioutil.TempDir("", "stream-hot")
	if err != nil {
		panic(err)
	}
	http.HandleFunc("/", streamHandler)
}

更多关于Golang中如何解决对权限为600的目录写入问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

4 回复

Unix。

更多关于Golang中如何解决对权限为600的目录写入问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


谢谢,这个方法确实有效!

有个小问题,这是 Go 语言本身的特性,还是 Unix/Linux 系统的一个特殊之处?

目录需要执行权限才能允许“进入”,因此 0600 对于目录来说不是一个非常有用的权限设置。你至少应该使用 0700。在你的 os.MkdirAll 中使用 0777(或者更好的 0755)应该可以工作。

在Heroku环境中,/tmp目录的权限限制确实会导致写入问题。问题在于你创建的目录权限是0666,但父目录/tmp的权限是0600,这会限制子目录的实际访问权限。

以下是解决方案:

// 修改目录创建权限为0700
os.MkdirAll(filepath.Join(tempDir, url[1]), 0700)

// 或者更好的做法:在创建临时目录时就设置正确的权限
tempDir, err = ioutil.TempDir("", "stream-hot")
if err != nil {
    panic(err)
}
// 确保临时目录有执行权限
os.Chmod(tempDir, 0700)

更完整的修复方案:

func streamHandler(w http.ResponseWriter, r *http.Request) {
    var rex = regexp.MustCompile("^\\/([a-zA-Z0-9]*)\\/([a-zA-Z0-9_]*\\.(?:m3u8|ts))$")
    url := rex.FindStringSubmatch(r.URL.String())
    if r.Method == "PUT" {
        if url == nil {
            http.Error(w, "URL has to be a path to HLS fragment/playlist.", http.StatusBadRequest)
            return
        }
        
        dirPath := filepath.Join(tempDir, url[1])
        stat, err := os.Stat(dirPath)
        if os.IsNotExist(err) {
            // 使用0700权限创建目录
            err = os.MkdirAll(dirPath, 0700)
            if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
            }
        } else if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        
        // 确保目录有正确的权限
        if stat != nil && stat.Mode().Perm() != 0700 {
            os.Chmod(dirPath, 0700)
        }
        
        filePath := filepath.Join(dirPath, url[2])
        f, err := os.Create(filePath)
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            log.Printf("Create error: %v, path: %s", err, filePath)
            return
        }
        defer f.Close()
        
        _, err = io.Copy(f, r.Body)
        if err != nil {
            http.Error(w, err.Error(), http.StatusBadRequest)
            return
        }
        w.WriteHeader(200)
        w.Write([]byte("OK"))
        return
    }
}

func registerHandlers() {
    var err error
    tempDir, err = ioutil.TempDir("", "stream-hot")
    if err != nil {
        panic(err)
    }
    // 设置临时目录权限
    os.Chmod(tempDir, 0700)
    http.HandleFunc("/", streamHandler)
}

关键点:

  1. 目录需要执行权限(x)才能在其中创建文件
  2. 0700权限允许所有者读、写、执行
  3. Heroku的/tmp目录权限限制需要子目录有足够的权限

另一种替代方案是使用os.MkdirTemp(Go 1.16+):

tempDir, err = os.MkdirTemp("", "stream-hot")
if err != nil {
    panic(err)
}
// 新创建的目录默认权限就是0700
回到顶部