| // 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 |
| } |