可写文件句柄在没有错误处理的情况下关闭¶
ID: go/unhandled-writable-file-close
Kind: path-problem
Security severity:
Severity: warning
Precision: high
Tags:
- maintainability
- correctness
- call
- defer
Query suites:
- go-security-and-quality.qls
写入文件句柄的数据可能不会被操作系统立即刷新到基础存储介质。通常情况下,数据可能会缓存在内存中,直到句柄关闭,或者在那之后的某个时间点。只有调用 os.File.Sync
才能合理地保证数据已刷新。因此,在调用 os.File.Close
或 os.File.Sync
之前,写入错误可能不会发生。如果调用了其中任何一个,并且它们返回的任何错误都被丢弃,那么程序可能不知道发生了数据丢失。
建议¶
始终检查 os.File.Close
是否返回错误,并进行适当处理。
示例¶
在第一个示例中,对 os.File.Close
进行了两次调用,目的是在所有文件操作完成后或写入文件失败时关闭文件。但是,虽然处理了调用 os.File.WriteString
期间可能出现的错误,但调用 os.File.Close
期间出现的任何错误都会被静默丢弃
package main
import (
"os"
)
func example() error {
f, err := os.OpenFile("file.txt", os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
return err
}
if _, err := f.WriteString("Hello"); err != nil {
f.Close()
return err
}
f.Close()
return nil
}
在第二个示例中,为了实现与第一个示例相同的行为,对 os.File.Close
的调用被延迟了。但是,虽然处理了调用 os.File.WriteString
期间可能出现的错误,但 os.File.Close
出现的任何错误再次被静默丢弃
package main
import (
"os"
)
func example() error {
f, err := os.OpenFile("file.txt", os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
return err
}
defer f.Close()
if _, err := f.WriteString("Hello"); err != nil {
return err
}
return nil
}
要解决此问题,请显式处理调用 os.File.Close
时出现的错误
package main
import (
"os"
)
func example() (exampleError error) {
f, err := os.OpenFile("file.txt", os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
return err
}
defer func() {
// try to close the file; if an error occurs, set the error returned by `example`
// to that error, but only if `WriteString` didn't already set it to something first
if err := f.Close(); exampleError == nil && err != nil {
exampleError = err
}
}()
if _, err := f.WriteString("Hello"); err != nil {
exampleError = err
}
return
}