From 2f6eceda913c36fa95f8c0e36f0f3b93c54935d2 Mon Sep 17 00:00:00 2001 From: Mostyn Bramley-Moore Date: Thu, 18 Jun 2020 20:36:04 +0200 Subject: [PATCH] add some customized tempfile code We are discussing allowing concurrent uploads for the same blob in #267. Part of the proposed new design involves using pseudo-randomly named temp files. Unfortunately, ioutil.TempFile creates files with more restrictive permissions than we want- 0600 before umask, whereas we want 0666 before umask. This code is an adapted version of the algorithm in ioutil.TempFile, refactored for our more narrow use case- in particular we skip the use of the pid in the seed, since our use case is single-process, and we don't bother re-seeding if we encounter lots of collisions (which is unlikely). (We do not use this code, yet.) --- utils/tempfile/tempfile.go | 55 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 utils/tempfile/tempfile.go diff --git a/utils/tempfile/tempfile.go b/utils/tempfile/tempfile.go new file mode 100644 index 000000000..ea2289214 --- /dev/null +++ b/utils/tempfile/tempfile.go @@ -0,0 +1,55 @@ +package tempfile + +import ( + "os" + "strconv" + "sync" + "time" +) + +type Creator struct { + mu sync.Mutex + idum uint32 // Psuedo-random number generator state. +} + +// NewCreator returns a new Creator, for creating temp files. +func NewCreator() *Creator { + return &Creator{idum: uint32(time.Now().UnixNano())} +} + +// Fast "quick and dirty" linear congruential (pseudo-random) number +// generator from Numerical Recipes. Excerpt here: +// https://www.unf.edu/~cwinton/html/cop4300/s09/class.notes/LCGinfo.pdf +// This is the same algorithm as used in the ioutil.TempFile go standard +// library function. +func (c *Creator) ranqd1() string { + c.mu.Lock() + c.idum = c.idum*1664525 + 1013904223 + r := c.idum + c.mu.Unlock() + return strconv.Itoa(int(1e9 + r%1e9))[1:] +} + +const flags = os.O_RDWR | os.O_CREATE | os.O_EXCL +const mode = 0666 // Before the umask is applied. + +// Create attempts to create a temp file for eventualFile, and returns +// the corresponding *os.File and an error if any occurred. The temp +// file is created in the same directory as eventualFile, with permissions +// 0666 before the umask is applied. +func (c *Creator) Create(eventualFile string) (*os.File, error) { + var err error + var f *os.File + + base := eventualFile + ".tmp" + + for i := 0; i < 10000; i++ { + name := base + c.ranqd1() + f, err = os.OpenFile(name, flags, mode) + if os.IsExist(err) { + continue + } + return f, err + } + return nil, err +}