Last active
February 11, 2016 14:21
-
-
Save kostix/63949f9ff6d8fc79c6d8 to your computer and use it in GitHub Desktop.
`tempfile` Go package mimicking mkstemp(3) and mkstemps(3) libc calls
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package tmpfile | |
import ( | |
"bytes" | |
"errors" | |
"os" | |
"sync/atomic" | |
"time" | |
) | |
func Mkstemp(tmpl string) (fd *os.File, err error) { | |
return create(tmpl, 0) | |
} | |
func Mkstemps(tmpl string, sfxlen int) (fd *os.File, err error) { | |
return create(tmpl, sfxlen) | |
} | |
var ErrExhaust = errors.New("Too many collisions when creating temporary file") | |
// These are the characters used in temporary filenames: | |
var letters = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") | |
var x6 = []byte{'X', 'X', 'X', 'X', 'X', 'X'} | |
// A shared volatile accumulator value. | |
// TODO ideally we should somehow preseed it with garbage. | |
var value uint64 | |
// __gen_tempname (char *tmpl, int sfxlen, int flags, int kind) | |
func create(tmpl string, sfxlen int) (fd *os.File, err error) { | |
const ( | |
// A lower bound on the number of temporary files to attempt to | |
// generate. The maximum total number of temporary file names that | |
// can exist for a given template is 62**6. It should never be | |
// necessary to try all these combinations. Instead if a reasonable | |
// number of names is tried (we define reasonable as 62**3) fail to | |
// give the system administrator the chance to remove the problems. | |
// Opengroups states that TMP_MAX must be at least 25, | |
// and that on OSI-compatible systems it should be ≥ 10000. | |
// See <http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/stdio.h.html> | |
ATTEMPTS_MIN = 62 * 62 * 62 | |
) | |
b := []byte(tmpl) | |
tlen := len(tmpl) | |
if tlen < 6+sfxlen { | |
panic("Template string too short") | |
} | |
xs := b[tlen-6-sfxlen : tlen-sfxlen] | |
if bytes.Compare(xs, x6) != 0 { | |
panic("Invalid suffix length value") | |
} | |
// Get some more or less random data. | |
tm := time.Now() | |
sec := uint64(tm.Unix()) | |
usec := uint64(tm.Nanosecond() / 1000) | |
random_time_bits := (usec << 16) ^ sec | |
v := atomic.AddUint64(&value, random_time_bits^uint64(os.Getpid())) | |
// The number of times to attempt to generate a temporary file. To | |
// conform to POSIX, this must be no smaller than TMP_MAX. | |
attempts := ATTEMPTS_MIN | |
for { | |
// Fill in the random bits: | |
xs[0] = letters[v%62] | |
v /= 62 | |
xs[1] = letters[v%62] | |
v /= 62 | |
xs[2] = letters[v%62] | |
v /= 62 | |
xs[3] = letters[v%62] | |
v /= 62 | |
xs[4] = letters[v%62] | |
v /= 62 | |
xs[5] = letters[v%62] | |
s := string(b) | |
fd, err = os.OpenFile(s, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) | |
if err == nil { | |
break | |
} | |
if pe := err.(*os.PathError); pe.Err != os.ErrExist { | |
return | |
} | |
attempts-- | |
if attempts == 0 { | |
err = &os.PathError{ | |
Op: "Temporary file name creation", | |
Path: s, | |
Err: ErrExhaust, | |
} | |
} | |
v = atomic.AddUint64(&value, 7777) | |
} | |
return | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment