github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/storage/temp_dir.go (about) 1 // Copyright 2017 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package storage 12 13 import ( 14 "bufio" 15 "context" 16 "io/ioutil" 17 "os" 18 "path/filepath" 19 20 "github.com/cockroachdb/cockroach/pkg/util/log" 21 "github.com/cockroachdb/cockroach/pkg/util/stop" 22 "github.com/cockroachdb/errors" 23 ) 24 25 const lockFilename = `TEMP_DIR.LOCK` 26 27 // CreateTempDir creates a temporary directory with a prefix under the given 28 // parentDir and returns the absolute path of the temporary directory. 29 // It is advised to invoke CleanupTempDirs before creating new temporary 30 // directories in cases where the disk is completely full. 31 func CreateTempDir(parentDir, prefix string, stopper *stop.Stopper) (string, error) { 32 // We generate a unique temporary directory with the specified prefix. 33 tempPath, err := ioutil.TempDir(parentDir, prefix) 34 if err != nil { 35 return "", err 36 } 37 38 // TempDir creates a directory with permissions 0700. Manually change the 39 // permissions to be 0755 like every other directory created by cockroach. 40 if err := os.Chmod(tempPath, 0755); err != nil { 41 return "", err 42 } 43 44 absPath, err := filepath.Abs(tempPath) 45 if err != nil { 46 return "", err 47 } 48 49 // Create a lock file. 50 flock, err := lockFile(filepath.Join(absPath, lockFilename)) 51 if err != nil { 52 return "", errors.Wrapf(err, "could not create lock on new temporary directory") 53 } 54 stopper.AddCloser(stop.CloserFn(func() { 55 if err := unlockFile(flock); err != nil { 56 log.Errorf(context.TODO(), "could not unlock file lock on temporary directory: %s", err.Error()) 57 } 58 })) 59 60 return absPath, nil 61 } 62 63 // RecordTempDir records tempPath to the record file specified by recordPath to 64 // facilitate cleanup of the temporary directory on subsequent startups. 65 func RecordTempDir(recordPath, tempPath string) error { 66 // If the file does not exist, create it, or append to the file. 67 f, err := os.OpenFile(recordPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) 68 if err != nil { 69 return err 70 } 71 defer f.Close() 72 73 // Record tempPath to the record file. 74 _, err = f.Write(append([]byte(tempPath), '\n')) 75 return err 76 } 77 78 // CleanupTempDirs removes all directories listed in the record file specified 79 // by recordPath. 80 // It should be invoked before creating any new temporary directories to clean 81 // up abandoned temporary directories. 82 // It should also be invoked when a newly created temporary directory is no 83 // longer needed and needs to be removed from the record file. 84 func CleanupTempDirs(recordPath string) error { 85 // Reading the entire file into memory shouldn't be a problem since 86 // it is extremely rare for this record file to contain more than a few 87 // entries. 88 f, err := os.OpenFile(recordPath, os.O_RDWR, 0644) 89 // There is no existing record file and thus nothing to clean up. 90 if os.IsNotExist(err) { 91 return nil 92 } 93 if err != nil { 94 return err 95 } 96 defer f.Close() 97 98 scanner := bufio.NewScanner(f) 99 // Iterate through each temporary directory path and remove the 100 // directory. 101 for scanner.Scan() { 102 path := scanner.Text() 103 if path == "" { 104 continue 105 } 106 107 // Check if the temporary directory exists; if it does not, skip over it. 108 if _, err := os.Stat(path); os.IsNotExist(err) { 109 log.Warningf(context.Background(), "could not locate previous temporary directory %s, might require manual cleanup, or might have already been cleaned up.", path) 110 continue 111 } 112 113 // Check if another Cockroach instance is using this temporary 114 // directory i.e. has a lock on the temp dir lock file. 115 flock, err := lockFile(filepath.Join(path, lockFilename)) 116 if err != nil { 117 return errors.Wrapf(err, "could not lock temporary directory %s, may still be in use", path) 118 } 119 // On Windows, file locks are mandatory, so we must remove our lock on the 120 // lock file before we can remove the temporary directory. This yields a 121 // race condition: another process could start using the now-unlocked 122 // directory before we can remove it. Luckily, this doesn't matter, because 123 // these temporary directories are never reused. Any other process trying to 124 // lock this temporary directory is just trying to clean it up, too. Only 125 // the original process wants the data in this directory, and we know that 126 // process is dead because we were able to acquire the lock in the first 127 // place. 128 if err := unlockFile(flock); err != nil { 129 log.Errorf(context.TODO(), "could not unlock file lock when removing temporary directory: %s", err.Error()) 130 } 131 132 // If path/directory does not exist, error is nil. 133 if err := os.RemoveAll(path); err != nil { 134 return err 135 } 136 } 137 138 // Clear out the record file now that we're done. 139 return f.Truncate(0) 140 }