tempfile.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. // Copyright 2010 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package os
  5. import (
  6. "errors"
  7. "internal/itoa"
  8. )
  9. // fastrand provided by runtime.
  10. // We generate random temporary file names so that there's a good
  11. // chance the file doesn't exist yet - keeps the number of tries in
  12. // TempFile to a minimum.
  13. func fastrand() uint32
  14. func nextRandom() string {
  15. return itoa.Uitoa(uint(fastrand()))
  16. }
  17. // CreateTemp creates a new temporary file in the directory dir,
  18. // opens the file for reading and writing, and returns the resulting file.
  19. // The filename is generated by taking pattern and adding a random string to the end.
  20. // If pattern includes a "*", the random string replaces the last "*".
  21. // If dir is the empty string, CreateTemp uses the default directory for temporary files, as returned by TempDir.
  22. // Multiple programs or goroutines calling CreateTemp simultaneously will not choose the same file.
  23. // The caller can use the file's Name method to find the pathname of the file.
  24. // It is the caller's responsibility to remove the file when it is no longer needed.
  25. func CreateTemp(dir, pattern string) (*File, error) {
  26. if dir == "" {
  27. dir = TempDir()
  28. }
  29. prefix, suffix, err := prefixAndSuffix(pattern)
  30. if err != nil {
  31. return nil, &PathError{Op: "createtemp", Path: pattern, Err: err}
  32. }
  33. prefix = joinPath(dir, prefix)
  34. try := 0
  35. for {
  36. name := prefix + nextRandom() + suffix
  37. f, err := OpenFile(name, O_RDWR|O_CREATE|O_EXCL, 0600)
  38. if IsExist(err) {
  39. if try++; try < 10000 {
  40. continue
  41. }
  42. return nil, &PathError{Op: "createtemp", Path: prefix + "*" + suffix, Err: ErrExist}
  43. }
  44. return f, err
  45. }
  46. }
  47. var errPatternHasSeparator = errors.New("pattern contains path separator")
  48. // prefixAndSuffix splits pattern by the last wildcard "*", if applicable,
  49. // returning prefix as the part before "*" and suffix as the part after "*".
  50. func prefixAndSuffix(pattern string) (prefix, suffix string, err error) {
  51. for i := 0; i < len(pattern); i++ {
  52. if IsPathSeparator(pattern[i]) {
  53. return "", "", errPatternHasSeparator
  54. }
  55. }
  56. if pos := lastIndex(pattern, '*'); pos != -1 {
  57. prefix, suffix = pattern[:pos], pattern[pos+1:]
  58. } else {
  59. prefix = pattern
  60. }
  61. return prefix, suffix, nil
  62. }
  63. // MkdirTemp creates a new temporary directory in the directory dir
  64. // and returns the pathname of the new directory.
  65. // The new directory's name is generated by adding a random string to the end of pattern.
  66. // If pattern includes a "*", the random string replaces the last "*" instead.
  67. // If dir is the empty string, MkdirTemp uses the default directory for temporary files, as returned by TempDir.
  68. // Multiple programs or goroutines calling MkdirTemp simultaneously will not choose the same directory.
  69. // It is the caller's responsibility to remove the directory when it is no longer needed.
  70. func MkdirTemp(dir, pattern string) (string, error) {
  71. if dir == "" {
  72. dir = TempDir()
  73. }
  74. prefix, suffix, err := prefixAndSuffix(pattern)
  75. if err != nil {
  76. return "", &PathError{Op: "mkdirtemp", Path: pattern, Err: err}
  77. }
  78. prefix = joinPath(dir, prefix)
  79. try := 0
  80. for {
  81. name := prefix + nextRandom() + suffix
  82. err := Mkdir(name, 0700)
  83. if err == nil {
  84. return name, nil
  85. }
  86. if IsExist(err) {
  87. if try++; try < 10000 {
  88. continue
  89. }
  90. return "", &PathError{Op: "mkdirtemp", Path: dir + string(PathSeparator) + prefix + "*" + suffix, Err: ErrExist}
  91. }
  92. if IsNotExist(err) {
  93. if _, err := Stat(dir); IsNotExist(err) {
  94. return "", err
  95. }
  96. }
  97. return "", err
  98. }
  99. }
  100. func joinPath(dir, name string) string {
  101. if len(dir) > 0 && IsPathSeparator(dir[len(dir)-1]) {
  102. return dir + name
  103. }
  104. return dir + string(PathSeparator) + name
  105. }
  106. // LastIndexByte from the strings package.
  107. func lastIndex(s string, sep byte) int {
  108. for i := len(s) - 1; i >= 0; i-- {
  109. if s[i] == sep {
  110. return i
  111. }
  112. }
  113. return -1
  114. }