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

5 回复

并发调用 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)
	}
}

关键改进:

  1. 使用位运算检查权限:直接使用mode&0002 != 0检查全局写权限,避免正则表达式和字符串操作的开销。

  2. 并发处理:对每个文件启动goroutine进行权限检查,利用多核CPU。

  3. 使用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
}
  1. 使用工作池控制并发度
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
}

这些改进通过并发处理和优化权限检查,可以显著减少在大文件系统上的扫描时间。

回到顶部