github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/ioutil/write_serialized_file.go (about) 1 // Copyright 2017 Keybase Inc. All rights reserved. 2 // Use of this source code is governed by a BSD 3 // license that can be found in the LICENSE file. 4 5 package ioutil 6 7 import ( 8 "io" 9 "os" 10 11 "github.com/pkg/errors" 12 ) 13 14 // WriteSerializedFile writes (or overwrites) `data` into `filename`. 15 // If `filename` doesn't exist, it's created with `perm` permissions. 16 // If `filename` does exist, the data is first overwritten to the 17 // file, and then the file is truncated to the length of the data. If 18 // the data represents a serialized data structure where the length is 19 // explicitly stated in, or implicitly calculated from, the data 20 // itself on a read, then this is approximately an atomic write 21 // (without the performance overhead of writing to a temp file and 22 // renaming it). NOTE: it's technically possible a partial OS write 23 // could lead to a corrupted file, though in practice this seems much 24 // more rare than os.WriteFile() leaving behind an empty file. 25 func WriteSerializedFile( 26 filename string, data []byte, perm os.FileMode) (err error) { 27 // Don't use os.WriteFile because it truncates the file first, 28 // and if there's a crash it will leave the file in an unknown 29 // state. 30 f, err := OpenFile(filename, os.O_WRONLY|os.O_CREATE, 0600) 31 if err != nil { 32 return err 33 } 34 defer func() { 35 closeErr := f.Close() 36 if err == nil { 37 err = errors.WithStack(closeErr) 38 } 39 }() 40 // Overwrite whatever data is there and then truncate. 41 n, err := f.Write(data) 42 if err != nil { 43 return errors.WithStack(err) 44 } else if n < len(data) { 45 return errors.WithStack(io.ErrShortWrite) 46 } 47 48 return f.Truncate(int64(len(data))) 49 }