golang.org/x/build@v0.0.0-20240506185731-218518f32b70/internal/gcsfs/osfs.go (about) 1 // Copyright 2022 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package gcsfs 6 7 import ( 8 "errors" 9 "fmt" 10 "io/fs" 11 "os" 12 "path" 13 "runtime" 14 "strings" 15 ) 16 17 var _ = fs.FS((*dirFS)(nil)) 18 var _ = CreateFS((*dirFS)(nil)) 19 20 // DirFS is a variant of os.DirFS that supports file creation and is a suitable 21 // test fake for the GCS FS. 22 func DirFS(dir string) fs.FS { 23 return dirFS(dir) 24 } 25 26 func containsAny(s, chars string) bool { 27 for i := 0; i < len(s); i++ { 28 for j := 0; j < len(chars); j++ { 29 if s[i] == chars[j] { 30 return true 31 } 32 } 33 } 34 return false 35 } 36 37 type dirFS string 38 39 func (dir dirFS) Open(name string) (fs.File, error) { 40 if !fs.ValidPath(name) || runtime.GOOS == "windows" && containsAny(name, `\:`) { 41 return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrInvalid} 42 } 43 f, err := os.Open(string(dir) + "/" + name) 44 if err != nil { 45 return nil, err // nil fs.File 46 } 47 return &atomicWriteFile{f, nil}, nil 48 } 49 50 func (dir dirFS) Stat(name string) (fs.FileInfo, error) { 51 if !fs.ValidPath(name) || runtime.GOOS == "windows" && containsAny(name, `\:`) { 52 return nil, &fs.PathError{Op: "stat", Path: name, Err: fs.ErrInvalid} 53 } 54 f, err := os.Stat(string(dir) + "/" + name) 55 if err != nil { 56 return nil, err 57 } 58 return f, nil 59 } 60 61 func (dir dirFS) Create(name string) (WriterFile, error) { 62 if !fs.ValidPath(name) || runtime.GOOS == "windows" && containsAny(name, `\:`) { 63 return nil, &fs.PathError{Op: "create", Path: name, Err: fs.ErrInvalid} 64 } 65 fullName := path.Join(string(dir), name) 66 if err := os.MkdirAll(path.Dir(fullName), 0700); err != nil { 67 return nil, err 68 } 69 70 // GCS doesn't let you see a file until you're done writing it. Write 71 // to a temp file, which will be renamed to the expected name on Close. 72 temp, err := os.CreateTemp(path.Dir(fullName), "."+path.Base(fullName)+".writing-*") 73 if err != nil { 74 return nil, err 75 } 76 finalize := func() error { 77 if _, err := os.Stat(fullName); !errors.Is(err, fs.ErrNotExist) { 78 return fmt.Errorf("file exists and cannot be overwritten: %v", name) 79 } 80 return os.Rename(temp.Name(), fullName) 81 } 82 return &atomicWriteFile{temp, finalize}, nil 83 } 84 85 type atomicWriteFile struct { 86 *os.File 87 finalize func() error 88 } 89 90 func (wf *atomicWriteFile) ReadDir(n int) ([]fs.DirEntry, error) { 91 unfiltered, err := wf.File.ReadDir(n) 92 var result []fs.DirEntry 93 for _, de := range unfiltered { 94 if !strings.HasPrefix(de.Name(), ".") { 95 result = append(result, de) 96 } 97 } 98 return result, err 99 } 100 101 func (wf *atomicWriteFile) Close() error { 102 if err := wf.File.Close(); err != nil { 103 return err 104 } 105 if wf.finalize != nil { 106 if err := wf.finalize(); err != nil { 107 return err 108 } 109 wf.finalize = nil 110 } 111 return nil 112 } 113 114 func (dir dirFS) Sub(subDir string) (fs.FS, error) { 115 return dirFS(path.Join(string(dir), subDir)), nil 116 }