github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/utils/file/sync.go (about) 1 // Copyright 2023 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package file 16 17 import ( 18 "io" 19 "os" 20 "path/filepath" 21 "runtime" 22 ) 23 24 func SyncDirectoryHandle(dir string) (err error) { 25 if runtime.GOOS == "windows" { 26 // directory sync not supported on Windows 27 return 28 } 29 var d *os.File 30 if d, err = os.Open(dir); err != nil { 31 return err 32 } 33 defer d.Close() 34 return d.Sync() 35 } 36 37 // WriteFileAtomically will attempt to write the contents of |rd| to a file named 38 // |name|, atomically. It uses POSIX filesystem semantics to attempt to 39 // accomplish this. It writes the entire contents of |rd| to a temporary file, 40 // fsync's the file, renames the file to its desired name, and then fsyncs the 41 // directory handle for the directory where these operations took place. 42 // 43 // Note: On non-Unix platforms, os.Rename is not an atomic operation. 44 func WriteFileAtomically(name string, rd io.Reader, mode os.FileMode) error { 45 name, err := filepath.Abs(name) 46 if err != nil { 47 return err 48 } 49 dir := filepath.Dir(name) 50 f, err := os.CreateTemp(dir, filepath.Base(name)+"-*") 51 if err != nil { 52 return err 53 } 54 _, err = io.Copy(f, rd) 55 if err != nil { 56 f.Close() 57 os.Remove(f.Name()) 58 return err 59 } 60 61 err = f.Sync() 62 if err != nil { 63 f.Close() 64 os.Remove(f.Name()) 65 return err 66 } 67 68 err = f.Close() 69 if err != nil { 70 os.Remove(f.Name()) 71 return err 72 } 73 74 err = os.Chmod(f.Name(), mode) 75 if err != nil { 76 os.Remove(f.Name()) 77 return err 78 } 79 80 err = os.Rename(f.Name(), name) 81 if err != nil { 82 os.Remove(f.Name()) 83 return err 84 } 85 86 return SyncDirectoryHandle(filepath.Dir(f.Name())) 87 }