Go语言 ioutil.TempFile和umask

yjghlzjz  于 2023-05-20  发布在  Go
关注(0)|答案(3)|浏览(119)

在我的Go应用程序中,我不想直接写文件,而是想写一个临时文件,当所有事情都完成时,它会被重命名为最终文件。这是为了避免在应用程序崩溃时将部分写入的内容留在文件中。
目前我使用ioutil.TempFile,但问题是它创建的文件具有0600权限,而不是0666。因此,使用典型的umask值,可以获得0600权限,而不是预期的0644或0660。这不是一个问题,因为目标文件已经存在,因为我可以将临时文件上的权限修复为现有的权限,但如果文件不存在,那么我需要以某种方式推断当前的umask。
我想我可以复制ioutil.TempFile实现,将0666传递到os.OpenFile,但这听起来不太好。所以问题是有更好的方法吗?

1dkrff03

1dkrff031#

我不太明白你的问题。
临时文件 * 必须 * 以尽可能严格的权限创建,因为拥有它们的整个想法是为您的应用程序提供临时存储数据的 * 安全 * 方法,这些数据太大而无法容纳在内存中(或将生成的文件交给另一个进程)。(注意,在POSIX系统中,打开的文件被视为对其的实时引用,甚至习惯上在打开文件时立即删除文件,以便除了从创建它的进程写入之外,没有办法修改其数据。
所以在我看来,你试图用一个错误的解决方案来解决你的问题。
所以在你这样的情况下我会做的是:
1.创建一个与旧文件同名的文件,但附加了“.temp”后缀。
1.在那里写数据。
1.关闭,将其重命名为旧的。
如果您觉得使用固定的后缀是蹩脚的,那么您可以从ioutil.TempFile()中“窃取”选择一个唯一的非冲突文件名的实现。但我认为这是过度的。

eit6fx6z

eit6fx6z2#

您可以使用ioutil.TempDir获取存储临时文件的文件夹,然后使用正确的权限自己创建文件。

ecr0jaav

ecr0jaav3#

与问题的要求完全匹配,应该是:

package main

import (
    "bytes"
    "fmt"
    "io"
    "io/fs"
    "os"
    "path/filepath"
    "syscall"
)

func getCurrentUmask() int {
    // Get current umask
    currentUmask := syscall.Umask(0)
    // Restore it to previous value
    syscall.Umask(currentUmask)
    // Return the value
    return currentUmask
}

func atomicWriteFile(filename string, data []byte, perm os.FileMode) error {
    reader := bytes.NewBuffer(data)
    dataSize := int64(len(data))

    tmpFile, err := os.CreateTemp(filepath.Dir(filename), ".tmp-"+filepath.Base(filename))
    if err != nil {
        return err
    }

    err = os.Chmod(tmpFile.Name(), perm)
    if err != nil {
        tmpFile.Close()

        return err
    }

    n, err := io.Copy(tmpFile, reader)
    if err == nil && n < dataSize {
        tmpFile.Close()

        return io.ErrShortWrite
    }

    if err != nil {
        tmpFile.Close()

        return err
    }

    if err := tmpFile.Sync(); err != nil {
        tmpFile.Close()

        return err
    }

    if err := tmpFile.Close(); err != nil {
        return err
    }

    return os.Rename(tmpFile.Name(), filename)
}

func main() {
    // Whatever permission you would normally set on your file
    targetPermissionForFile := 0o666

    // How you would normally write a file
    os.WriteFile("using_os.txt", []byte("barbar"), fs.FileMode(targetPermissionForFile))

    // Retrieving the current umask
    mask := getCurrentUmask()

    // Writing it with the atomic method
    atomicWriteFile("using_atomic.txt", []byte("foofoo"), fs.FileMode((^mask)&targetPermissionForFile))

    // Comparing resulting permissions
    info, _ := os.Stat("using_os.txt")
    fmt.Printf("File written using os.WriteFile has permissions: %#o\n", info.Mode())

    info, _ = os.Stat("using_atomic.txt")
    fmt.Printf("File written using atomicWrite has permissions: %#o\n", info.Mode())

}

相关问题