utils/file.go (65 lines of code) (raw):

// Copyright 2024 Google LLC // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // https://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // OS file util for Google Guest Agent and Google Authorized Keys. package utils import ( "fmt" "io/fs" "os" "path/filepath" ) // Type is the type of file. type Type int const ( // TypeDir is the type of directory. TypeDir Type = iota // TypeFile is the type of file. TypeFile ) // SaferWriteFile writes to a temporary file and then replaces the expected output file. // This prevents other processes from reading partial content while the writer is still writing. func SaferWriteFile(content []byte, outputFile string, perm fs.FileMode) error { dir := filepath.Dir(outputFile) name := filepath.Base(outputFile) if err := os.MkdirAll(dir, perm); err != nil { return fmt.Errorf("unable to create required directories %q: %w", dir, err) } tmp, err := os.CreateTemp(dir, name+"*") if err != nil { return fmt.Errorf("unable to create temporary file under %q: %w", dir, err) } if err := os.Chmod(tmp.Name(), perm); err != nil { return fmt.Errorf("unable to set permissions on temporary file %q: %w", dir, err) } if err := tmp.Close(); err != nil { return fmt.Errorf("failed to close temporary file: %w", err) } if err := WriteFile(content, tmp.Name(), perm); err != nil { return fmt.Errorf("unable to write to a temporary file %q: %w", tmp.Name(), err) } return os.Rename(tmp.Name(), outputFile) } // CopyFile copies content from src to dst and sets permissions. func CopyFile(src, dst string, perm fs.FileMode) error { b, err := os.ReadFile(src) if err != nil { return fmt.Errorf("failed to read %q: %w", src, err) } if err := WriteFile(b, dst, perm); err != nil { return fmt.Errorf("failed to write %q: %w", dst, err) } if err := os.Chmod(dst, perm); err != nil { return fmt.Errorf("unable to set permissions on destination file %q: %w", dst, err) } return nil } // WriteFile creates parent directories if required and writes content to the output file. func WriteFile(content []byte, outputFile string, perm fs.FileMode) error { if err := os.MkdirAll(filepath.Dir(outputFile), perm); err != nil { return fmt.Errorf("unable to create required directories for %q: %w", outputFile, err) } return os.WriteFile(outputFile, content, perm) } // FileExists returns true if the file exists and match ftype. func FileExists(fpath string, ftype Type) bool { stat, err := os.Stat(fpath) if err != nil { return false } if ftype == TypeDir && stat.IsDir() { return true } if ftype == TypeFile && !stat.IsDir() { return true } return false }