github.com/Cloud-Foundations/Dominator@v0.3.4/imagebuilder/builder/calibrateCtime.go (about)

     1  package builder
     2  
     3  import (
     4  	"errors"
     5  	"io/ioutil"
     6  	"os"
     7  	"sync"
     8  	"syscall"
     9  	"time"
    10  )
    11  
    12  var (
    13  	calibrateOnce    sync.Once
    14  	calibrationError error
    15  	_ctimeResolution time.Duration
    16  )
    17  
    18  // calibrateCtime will calibrate the resolution of inode change times for
    19  // temporary files. It will return the minimum resolution or an error.
    20  func calibrateCtime() (time.Duration, error) {
    21  	buffer := []byte("data\n")
    22  	file, err := ioutil.TempFile("", "ctimeCalibration")
    23  	if err != nil {
    24  		return 0, err
    25  	}
    26  	defer file.Close()
    27  	defer os.Remove(file.Name())
    28  	fd := int(file.Fd()) // Bypass os.File overheads.
    29  	if _, err := syscall.Write(fd, buffer); err != nil {
    30  		return 0, err
    31  	}
    32  	var firstStat syscall.Stat_t
    33  	if err := syscall.Stat(file.Name(), &firstStat); err != nil {
    34  		return 0, err
    35  	}
    36  	interval := time.Nanosecond
    37  	startTime := time.Now()
    38  	for ; time.Since(startTime) < time.Second; interval *= 10 {
    39  		if _, err := syscall.Write(fd, buffer); err != nil {
    40  			return 0, err
    41  		}
    42  		var newStat syscall.Stat_t
    43  		if err := syscall.Stat(file.Name(), &newStat); err != nil {
    44  			return 0, err
    45  		}
    46  		if newStat.Ctim != firstStat.Ctim {
    47  			return time.Since(startTime), nil
    48  		}
    49  		time.Sleep(interval)
    50  	}
    51  	return 0, errors.New("timed out calibrating Ctime changes")
    52  }
    53  
    54  func getCtimeResolution() (time.Duration, error) {
    55  	calibrateOnce.Do(func() {
    56  		_ctimeResolution, calibrationError = calibrateCtime()
    57  	})
    58  	return _ctimeResolution, calibrationError
    59  }