github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/osutil/fileutil.go (about) 1 // Copyright 2015 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package osutil 5 6 import ( 7 "bytes" 8 "fmt" 9 "io" 10 "io/fs" 11 "os" 12 "path/filepath" 13 ) 14 15 // CopyFile atomically copies oldFile to newFile preserving permissions and modification time. 16 func CopyFile(oldFile, newFile string) error { 17 oldf, err := os.Open(oldFile) 18 if err != nil { 19 return err 20 } 21 defer oldf.Close() 22 stat, err := oldf.Stat() 23 if err != nil { 24 return err 25 } 26 tmpFile := newFile + ".tmp" 27 newf, err := os.OpenFile(tmpFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, stat.Mode()&os.ModePerm) 28 if err != nil { 29 return err 30 } 31 defer newf.Close() 32 _, err = io.Copy(newf, oldf) 33 if err != nil { 34 return err 35 } 36 if err := newf.Close(); err != nil { 37 return err 38 } 39 if err := os.Chtimes(tmpFile, stat.ModTime(), stat.ModTime()); err != nil { 40 return err 41 } 42 return os.Rename(tmpFile, newFile) 43 } 44 45 // Rename is similar to os.Rename but handles cross-device renaming (by copying). 46 func Rename(oldFile, newFile string) error { 47 err := os.Rename(oldFile, newFile) 48 if err != nil { 49 // Can't use syscall.EXDEV because this is used in appengine app. 50 err = CopyFile(oldFile, newFile) 51 os.Remove(oldFile) 52 } 53 return err 54 } 55 56 // FillDirectory is used to fill in directory structure for tests. 57 func FillDirectory(dir string, fileContent map[string]string) error { 58 for path, content := range fileContent { 59 fullPath := filepath.Join(dir, path) 60 dirPath := filepath.Dir(fullPath) 61 if err := MkdirAll(dirPath); err != nil { 62 return fmt.Errorf("mkdir %q failed: %w", dirPath, err) 63 } 64 if err := WriteFile(fullPath, []byte(content)); err != nil { 65 return fmt.Errorf("write file failed: %w", err) 66 } 67 } 68 return nil 69 } 70 71 // WriteTempFile writes data to a temp file and returns its name. 72 func WriteTempFile(data []byte) (string, error) { 73 // Note: pkg/report knows about "syzkaller" prefix as it appears in crashes as process name. 74 f, err := os.CreateTemp("", "syzkaller") 75 if err != nil { 76 return "", fmt.Errorf("failed to create a temp file: %w", err) 77 } 78 if _, err := f.Write(data); err != nil { 79 f.Close() 80 os.Remove(f.Name()) 81 return "", fmt.Errorf("failed to write a temp file: %w", err) 82 } 83 f.Close() 84 return f.Name(), nil 85 } 86 87 // GrepFiles returns the list of files (relative to root) that include target. 88 // If ext is not empty, the files will be filtered by the extension. 89 // The function assumes that the files are not too big and may fit in memory. 90 func GrepFiles(root, ext string, target []byte) ([]string, error) { 91 var ret []string 92 err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { 93 if err != nil { 94 return err 95 } 96 if d.IsDir() || filepath.Ext(path) != ext { 97 return nil 98 } 99 content, err := os.ReadFile(path) 100 if err != nil { 101 return fmt.Errorf("failed to open %s: %w", path, err) 102 } 103 if bytes.Contains(content, target) { 104 rel, _ := filepath.Rel(root, path) 105 ret = append(ret, rel) 106 } 107 return nil 108 }) 109 return ret, err 110 }