Golang中检查全局可写文件的最有效方法
Golang中检查全局可写文件的最有效方法 你好。我是Go语言新手,这是我最初的几个应用程序之一。
目标是遍历整个文件系统,找出所有全局可写的文件。脚本应在5分钟内完成。这段代码可以工作,但我们有一些非常大的文件系统,遍历可能需要长达10分钟。
是否有更好的方法来检查文件模式?是否有最高效的方法来进行这些检查?使用并发会有任何好处吗?
// FilePathWalkDir接受一个根目录,然后返回一个
// 字符串数组,包含所有全局可写的文件。
func FilePathWalkDir() ([]string, error) {
var files []string
err := filepath.WalkDir("/", func(path string, d os.DirEntry, err error) error {
// 如果路径是目录并且是跳过的目录之一,则跳过它。
if d.IsDir() {
switch skipPath := path; skipPath {
case "/proc":
return filepath.SkipDir
case "/run":
return filepath.SkipDir
case "/sys/fs":
return filepath.SkipDir
case "/selinux":
return filepath.SkipDir
}
}
// 如果条目类型是常规文件,则获取其权限,如果它是全局可写的,
// 则将其添加到文件数组中。
if d.Type().IsRegular() {
info, _ := d.Info()
s := string(info.Mode().String()[8])
matched, _ := regexp.MatchString(`w`, s)
if matched {
files = append(files, path)
}
}
return nil
})
// 返回文件数组
return files, err
}
更多关于Golang中检查全局可写文件的最有效方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html
并发调用 FilePathWalkDir 可能会有所帮助。
更多关于Golang中检查全局可写文件的最有效方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
为什么你不使用位操作来检查呢?
我不太确定你100%的意思。我似乎只能得到 -rw-r--r--。有没有更简单的方法来获取八进制或二进制值,还是需要我自己进行转换?
info.Mode() 的最后9位代表权限,相关文档在此:https://pkg.go.dev/io/fs#FileMode
因此,info.Mode() & 0777 将只给你权限部分,info.Mode() & 0100 != 0 对于任何所有者可执行文件将为真,info.Mode() & 0004 != 0 对于所有全局可写文件为真。
在Go中检查全局可写文件时,可以通过并发和优化文件模式检查来提高性能。以下是改进后的实现:
package main
import (
"io/fs"
"os"
"path/filepath"
"sync"
)
func findWorldWritableFiles(root string, skipDirs []string) ([]string, error) {
var (
files []string
mu sync.Mutex
wg sync.WaitGroup
)
skipSet := make(map[string]struct{})
for _, dir := range skipDirs {
skipSet[dir] = struct{}{}
}
err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return nil
}
if d.IsDir() {
if _, skip := skipSet[path]; skip {
return filepath.SkipDir
}
return nil
}
if !d.Type().IsRegular() {
return nil
}
wg.Add(1)
go func(p string, entry fs.DirEntry) {
defer wg.Done()
info, err := entry.Info()
if err != nil {
return
}
mode := info.Mode()
if mode&0002 != 0 {
mu.Lock()
files = append(files, p)
mu.Unlock()
}
}(path, d)
return nil
})
wg.Wait()
return files, err
}
func main() {
skipDirs := []string{"/proc", "/run", "/sys/fs", "/selinux"}
files, err := findWorldWritableFiles("/", skipDirs)
if err != nil {
panic(err)
}
}
关键改进:
-
使用位运算检查权限:直接使用
mode&0002 != 0检查全局写权限,避免正则表达式和字符串操作的开销。 -
并发处理:对每个文件启动goroutine进行权限检查,利用多核CPU。
-
使用sync.Map优化并发写入(替代方案):
func findWorldWritableFilesConcurrent(root string) ([]string, error) {
var fileMap sync.Map
var wg sync.WaitGroup
err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
if err != nil || !d.Type().IsRegular() {
return nil
}
wg.Add(1)
go func(p string, entry fs.DirEntry) {
defer wg.Done()
info, err := entry.Info()
if err != nil {
return
}
if info.Mode()&0002 != 0 {
fileMap.Store(p, true)
}
}(path, d)
return nil
})
wg.Wait()
var files []string
fileMap.Range(func(key, value interface{}) bool {
files = append(files, key.(string))
return true
})
return files, err
}
- 使用工作池控制并发度:
func findWorldWritableFilesWithPool(root string, workers int) ([]string, error) {
paths := make(chan string, 1000)
results := make(chan string, 1000)
var wg sync.WaitGroup
for i := 0; i < workers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for path := range paths {
info, err := os.Stat(path)
if err == nil && info.Mode()&0002 != 0 {
results <- path
}
}
}()
}
go func() {
filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
if err == nil && d.Type().IsRegular() {
paths <- path
}
return nil
})
close(paths)
}()
go func() {
wg.Wait()
close(results)
}()
var files []string
for file := range results {
files = append(files, file)
}
return files, nil
}
这些改进通过并发处理和优化权限检查,可以显著减少在大文件系统上的扫描时间。

