diff --git a/go.mod b/go.mod index 9ffff9b..9d9aff1 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/boyter/hashit go 1.22 require ( + github.com/djherbis/times v1.6.0 github.com/gosuri/uiprogress v0.0.1 github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 github.com/spf13/cobra v1.8.1 @@ -11,7 +12,6 @@ require ( ) require ( - github.com/djherbis/times v1.6.0 // indirect github.com/gosuri/uilive v0.0.4 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/klauspost/cpuid/v2 v2.2.8 // indirect diff --git a/main.go b/main.go index 805e574..69cc51a 100644 --- a/main.go +++ b/main.go @@ -103,6 +103,12 @@ func main() { runtime.NumCPU(), "number of threads processing files, by default the number of CPU cores", ) + flags.BoolVar( + &processor.MTime, + "mtime", + false, + "enable mtime output", + ) if err := rootCmd.Execute(); err != nil { os.Exit(1) diff --git a/processor/processor.go b/processor/processor.go index 7bc10e1..6e3f4e6 100644 --- a/processor/processor.go +++ b/processor/processor.go @@ -23,6 +23,9 @@ var Debug = false // Trace enables trace logging output which is extremely verbose var Trace = false +// MTime enable mtime calculation and output +var MTime = false + // Progress uses ui bar to display the progress of files var Progress = false diff --git a/processor/structs.go b/processor/structs.go index 547c1b6..af841ee 100644 --- a/processor/structs.go +++ b/processor/structs.go @@ -1,5 +1,7 @@ package processor +import "time" + // Holds the result after processing the hashes for the file type Result struct { File string @@ -16,4 +18,5 @@ type Result struct { Sha3384 string Sha3512 string Bytes int64 + MTime *time.Time } diff --git a/processor/workers.go b/processor/workers.go index 0c65b15..fc7cd1b 100644 --- a/processor/workers.go +++ b/processor/workers.go @@ -9,6 +9,7 @@ import ( "crypto/sha512" "encoding/hex" "fmt" + "github.com/djherbis/times" "github.com/gosuri/uiprogress" "github.com/minio/blake2b-simd" "github.com/zeebo/blake3" @@ -19,6 +20,7 @@ import ( "os" "strings" "sync" + "time" ) const ( @@ -44,14 +46,23 @@ func fileProcessorWorker(input chan string, output chan Result) { // Open the file and determine if we should read it from disk or memory map // based on how large it is reported as being file, err := os.OpenFile(res, os.O_RDONLY, 0644) - if err != nil { printError(fmt.Sprintf("Unable to process file %s with error %s", res, err.Error())) continue } - fi, err := file.Stat() + var mtime time.Time + if MTime { + stat, err := times.Stat(res) + if err != nil { + printError(fmt.Sprintf("Unable to read mtime file %s with error %s", res, err.Error())) + return + } + mtime = stat.ModTime() + //fmt.Println(stat.ModTime().Format("2006-01-02 15:04:05")) + } + fi, err := file.Stat() if err != nil { printError(fmt.Sprintf("Unable to get file info for file %s with error %s", res, err.Error())) continue @@ -81,6 +92,7 @@ func fileProcessorWorker(input chan string, output chan Result) { if err == nil { r.File = res r.Bytes = fsize + r.MTime = &mtime output <- r } @@ -117,6 +129,7 @@ func fileProcessorWorker(input chan string, output chan Result) { if err == nil { r.File = res r.Bytes = fsize + r.MTime = &mtime output <- r } } diff --git a/vendor/github.com/djherbis/times/LICENSE b/vendor/github.com/djherbis/times/LICENSE new file mode 100644 index 0000000..1e7b7cc --- /dev/null +++ b/vendor/github.com/djherbis/times/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Dustin H + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/vendor/github.com/djherbis/times/README.md b/vendor/github.com/djherbis/times/README.md new file mode 100644 index 0000000..7c88890 --- /dev/null +++ b/vendor/github.com/djherbis/times/README.md @@ -0,0 +1,67 @@ +times +========== + +[![GoDoc](https://godoc.org/github.com/djherbis/times?status.svg)](https://godoc.org/github.com/djherbis/times) +[![Release](https://img.shields.io/github/release/djherbis/times.svg)](https://github.com/djherbis/times/releases/latest) +[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE.txt) +[![go test](https://github.com/djherbis/times/actions/workflows/go-test.yml/badge.svg)](https://github.com/djherbis/times/actions/workflows/go-test.yml) +[![Coverage Status](https://coveralls.io/repos/djherbis/times/badge.svg?branch=master)](https://coveralls.io/r/djherbis/times?branch=master) +[![Go Report Card](https://goreportcard.com/badge/github.com/djherbis/times)](https://goreportcard.com/report/github.com/djherbis/times) +[![Sourcegraph](https://sourcegraph.com/github.com/djherbis/times/-/badge.svg)](https://sourcegraph.com/github.com/djherbis/times?badge) + +Usage +------------ +File Times for #golang + +Go has a hidden time functions for most platforms, this repo makes them accessible. + +```go +package main + +import ( + "log" + + "github.com/djherbis/times" +) + +func main() { + t, err := times.Stat("myfile") + if err != nil { + log.Fatal(err.Error()) + } + + log.Println(t.AccessTime()) + log.Println(t.ModTime()) + + if t.HasChangeTime() { + log.Println(t.ChangeTime()) + } + + if t.HasBirthTime() { + log.Println(t.BirthTime()) + } +} +``` + +Supported Times +------------ +| | windows | linux | solaris | dragonfly | nacl | freebsd | darwin | netbsd | openbsd | plan9 | js | aix | +|:-----:|:-------:|:-----:|:-------:|:---------:|:------:|:-------:|:----:|:------:|:-------:|:-----:|:-----:|:-----:| +| atime | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| mtime | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| ctime | ✓* | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | ✓ | ✓ | +| btime | ✓ | ✓* | | | | ✓ | ✓| ✓ | | | + +* Linux btime requires kernel 4.11 and filesystem support, so HasBirthTime = false. +Use Timespec.HasBirthTime() to check if file has birth time. +Get(FileInfo) never returns btime. +* Windows XP does not have ChangeTime so HasChangeTime = false, +however Vista onward does have ChangeTime so Timespec.HasChangeTime() will +only return false on those platforms when the syscall used to obtain them fails. +* Also note, Get(FileInfo) will now only return values available in FileInfo.Sys(), this means Stat() is required to get ChangeTime on Windows + +Installation +------------ +```sh +go get -u github.com/djherbis/times +``` diff --git a/vendor/github.com/djherbis/times/ctime_windows.go b/vendor/github.com/djherbis/times/ctime_windows.go new file mode 100644 index 0000000..f2a5c8b --- /dev/null +++ b/vendor/github.com/djherbis/times/ctime_windows.go @@ -0,0 +1,149 @@ +package times + +import ( + "os" + "syscall" + "time" + "unsafe" +) + +// Stat returns the Timespec for the given filename. +func Stat(name string) (Timespec, error) { + ts, err := platformSpecficStat(name) + if err == nil { + return ts, err + } + + return stat(name, os.Stat) +} + +// Lstat returns the Timespec for the given filename, and does not follow Symlinks. +func Lstat(name string) (Timespec, error) { + ts, err := platformSpecficLstat(name) + if err == nil { + return ts, err + } + + return stat(name, os.Lstat) +} + +type timespecEx struct { + atime + mtime + ctime + btime +} + +// StatFile finds a Windows Timespec with ChangeTime. +func StatFile(file *os.File) (Timespec, error) { + return statFile(syscall.Handle(file.Fd())) +} + +func statFile(h syscall.Handle) (Timespec, error) { + var fileInfo fileBasicInfo + if err := getFileInformationByHandleEx(h, &fileInfo); err != nil { + return nil, err + } + + var t timespecEx + t.atime.v = time.Unix(0, fileInfo.LastAccessTime.Nanoseconds()) + t.mtime.v = time.Unix(0, fileInfo.LastWriteTime.Nanoseconds()) + t.ctime.v = time.Unix(0, fileInfo.ChangeTime.Nanoseconds()) + t.btime.v = time.Unix(0, fileInfo.CreationTime.Nanoseconds()) + return t, nil +} + +func platformSpecficLstat(name string) (Timespec, error) { + if findProcErr != nil { + return nil, findProcErr + } + + isSym, err := isSymlink(name) + if err != nil { + return nil, err + } + + var attrs = uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS) + if isSym { + attrs |= syscall.FILE_FLAG_OPEN_REPARSE_POINT + } + + return openHandleAndStat(name, attrs) +} + +func isSymlink(name string) (bool, error) { + fi, err := os.Lstat(name) + if err != nil { + return false, err + } + return fi.Mode()&os.ModeSymlink != 0, nil +} + +func platformSpecficStat(name string) (Timespec, error) { + if findProcErr != nil { + return nil, findProcErr + } + + return openHandleAndStat(name, syscall.FILE_FLAG_BACKUP_SEMANTICS) +} + +func openHandleAndStat(name string, attrs uint32) (Timespec, error) { + pathp, e := syscall.UTF16PtrFromString(name) + if e != nil { + return nil, e + } + h, e := syscall.CreateFile(pathp, + syscall.FILE_WRITE_ATTRIBUTES, syscall.FILE_SHARE_WRITE, nil, + syscall.OPEN_EXISTING, attrs, 0) + if e != nil { + return nil, e + } + defer syscall.Close(h) + + return statFile(h) +} + +var ( + findProcErr error + procGetFileInformationByHandleEx *syscall.Proc +) + +func init() { + var modkernel32 *syscall.DLL + if modkernel32, findProcErr = syscall.LoadDLL("kernel32.dll"); findProcErr == nil { + procGetFileInformationByHandleEx, findProcErr = modkernel32.FindProc("GetFileInformationByHandleEx") + } +} + +// fileBasicInfo holds the C++ data for FileTimes. +// +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa364217(v=vs.85).aspx +type fileBasicInfo struct { + CreationTime syscall.Filetime + LastAccessTime syscall.Filetime + LastWriteTime syscall.Filetime + ChangeTime syscall.Filetime + FileAttributes uint32 + _ uint32 // padding +} + +type fileInformationClass int + +const ( + fileBasicInfoClass fileInformationClass = iota +) + +func getFileInformationByHandleEx(handle syscall.Handle, data *fileBasicInfo) (err error) { + if findProcErr != nil { + return findProcErr + } + + r1, _, e1 := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), 4, uintptr(handle), uintptr(fileBasicInfoClass), uintptr(unsafe.Pointer(data)), unsafe.Sizeof(*data), 0, 0) + if r1 == 0 { + err = syscall.EINVAL + if e1 != 0 { + err = error(e1) + } + } + return +} diff --git a/vendor/github.com/djherbis/times/js.cover.dockerfile b/vendor/github.com/djherbis/times/js.cover.dockerfile new file mode 100644 index 0000000..f522a5e --- /dev/null +++ b/vendor/github.com/djherbis/times/js.cover.dockerfile @@ -0,0 +1,9 @@ +FROM golang:1.17 + +RUN curl -sL https://deb.nodesource.com/setup_17.x | bash +RUN apt-get install --yes nodejs + +WORKDIR /go/src/github.com/djherbis/times +COPY . . + +RUN GO111MODULE=auto GOOS=js GOARCH=wasm go test -covermode=count -coverprofile=profile.cov -exec="$(go env GOROOT)/misc/wasm/go_js_wasm_exec" diff --git a/vendor/github.com/djherbis/times/js.cover.sh b/vendor/github.com/djherbis/times/js.cover.sh new file mode 100644 index 0000000..1506104 --- /dev/null +++ b/vendor/github.com/djherbis/times/js.cover.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -e + +docker build -f js.cover.dockerfile -t js.cover.djherbis.times . +docker create --name js.cover.djherbis.times js.cover.djherbis.times +docker cp js.cover.djherbis.times:/go/src/github.com/djherbis/times/profile.cov . +docker rm -v js.cover.djherbis.times \ No newline at end of file diff --git a/vendor/github.com/djherbis/times/linux.cover.dockerfile b/vendor/github.com/djherbis/times/linux.cover.dockerfile new file mode 100644 index 0000000..e1a7959 --- /dev/null +++ b/vendor/github.com/djherbis/times/linux.cover.dockerfile @@ -0,0 +1,6 @@ +FROM golang:1.17 + +WORKDIR /go/src/github.com/djherbis/times +COPY . . + +RUN GO111MODULE=auto go test -covermode=count -coverprofile=profile.cov diff --git a/vendor/github.com/djherbis/times/linux.cover.sh b/vendor/github.com/djherbis/times/linux.cover.sh new file mode 100644 index 0000000..83f9743 --- /dev/null +++ b/vendor/github.com/djherbis/times/linux.cover.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -e + +docker build -f linux.cover.dockerfile -t linux.cover.djherbis.times . +docker create --name linux.cover.djherbis.times linux.cover.djherbis.times +docker cp linux.cover.djherbis.times:/go/src/github.com/djherbis/times/profile.cov . +docker rm -v linux.cover.djherbis.times \ No newline at end of file diff --git a/vendor/github.com/djherbis/times/times.go b/vendor/github.com/djherbis/times/times.go new file mode 100644 index 0000000..1902698 --- /dev/null +++ b/vendor/github.com/djherbis/times/times.go @@ -0,0 +1,74 @@ +// Package times provides a platform-independent way to get atime, mtime, ctime and btime for files. +package times + +import ( + "os" + "time" +) + +// Get returns the Timespec for the given FileInfo +func Get(fi os.FileInfo) Timespec { + return getTimespec(fi) +} + +type statFunc func(string) (os.FileInfo, error) + +func stat(name string, sf statFunc) (Timespec, error) { + fi, err := sf(name) + if err != nil { + return nil, err + } + return getTimespec(fi), nil +} + +// Timespec provides access to file times. +// ChangeTime() panics unless HasChangeTime() is true and +// BirthTime() panics unless HasBirthTime() is true. +type Timespec interface { + ModTime() time.Time + AccessTime() time.Time + ChangeTime() time.Time + BirthTime() time.Time + HasChangeTime() bool + HasBirthTime() bool +} + +type atime struct { + v time.Time +} + +func (a atime) AccessTime() time.Time { return a.v } + +type ctime struct { + v time.Time +} + +func (ctime) HasChangeTime() bool { return true } + +func (c ctime) ChangeTime() time.Time { return c.v } + +type mtime struct { + v time.Time +} + +func (m mtime) ModTime() time.Time { return m.v } + +type btime struct { + v time.Time +} + +func (btime) HasBirthTime() bool { return true } + +func (b btime) BirthTime() time.Time { return b.v } + +type noctime struct{} + +func (noctime) HasChangeTime() bool { return false } + +func (noctime) ChangeTime() time.Time { panic("ctime not available") } + +type nobtime struct{} + +func (nobtime) HasBirthTime() bool { return false } + +func (nobtime) BirthTime() time.Time { panic("birthtime not available") } diff --git a/vendor/github.com/djherbis/times/times_aix.go b/vendor/github.com/djherbis/times/times_aix.go new file mode 100644 index 0000000..5cbcbec --- /dev/null +++ b/vendor/github.com/djherbis/times/times_aix.go @@ -0,0 +1,39 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// https://golang.org/src/os/stat_aix.go + +package times + +import ( + "os" + "syscall" + "time" +) + +// HasChangeTime and HasBirthTime are true if and only if +// the target OS supports them. +const ( + HasChangeTime = true + HasBirthTime = false +) + +type timespec struct { + atime + mtime + ctime + nobtime +} + +func timespecToTime(ts syscall.StTimespec_t) time.Time { + return time.Unix(int64(ts.Sec), int64(ts.Nsec)) +} + +func getTimespec(fi os.FileInfo) (t timespec) { + stat := fi.Sys().(*syscall.Stat_t) + t.atime.v = timespecToTime(stat.Atim) + t.mtime.v = timespecToTime(stat.Mtim) + t.ctime.v = timespecToTime(stat.Ctim) + return t +} diff --git a/vendor/github.com/djherbis/times/times_darwin.go b/vendor/github.com/djherbis/times/times_darwin.go new file mode 100644 index 0000000..94c5e4c --- /dev/null +++ b/vendor/github.com/djherbis/times/times_darwin.go @@ -0,0 +1,40 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// http://golang.org/src/os/stat_darwin.go + +package times + +import ( + "os" + "syscall" + "time" +) + +// HasChangeTime and HasBirthTime are true if and only if +// the target OS supports them. +const ( + HasChangeTime = true + HasBirthTime = true +) + +type timespec struct { + atime + mtime + ctime + btime +} + +func timespecToTime(ts syscall.Timespec) time.Time { + return time.Unix(int64(ts.Sec), int64(ts.Nsec)) +} + +func getTimespec(fi os.FileInfo) (t timespec) { + stat := fi.Sys().(*syscall.Stat_t) + t.atime.v = timespecToTime(stat.Atimespec) + t.mtime.v = timespecToTime(stat.Mtimespec) + t.ctime.v = timespecToTime(stat.Ctimespec) + t.btime.v = timespecToTime(stat.Birthtimespec) + return t +} diff --git a/vendor/github.com/djherbis/times/times_dragonfly.go b/vendor/github.com/djherbis/times/times_dragonfly.go new file mode 100644 index 0000000..e11610c --- /dev/null +++ b/vendor/github.com/djherbis/times/times_dragonfly.go @@ -0,0 +1,39 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// http://golang.org/src/os/stat_dragonfly.go + +package times + +import ( + "os" + "syscall" + "time" +) + +// HasChangeTime and HasBirthTime are true if and only if +// the target OS supports them. +const ( + HasChangeTime = true + HasBirthTime = false +) + +type timespec struct { + atime + mtime + ctime + nobtime +} + +func timespecToTime(ts syscall.Timespec) time.Time { + return time.Unix(int64(ts.Sec), int64(ts.Nsec)) +} + +func getTimespec(fi os.FileInfo) (t timespec) { + stat := fi.Sys().(*syscall.Stat_t) + t.atime.v = timespecToTime(stat.Atim) + t.mtime.v = timespecToTime(stat.Mtim) + t.ctime.v = timespecToTime(stat.Ctim) + return t +} diff --git a/vendor/github.com/djherbis/times/times_freebsd.go b/vendor/github.com/djherbis/times/times_freebsd.go new file mode 100644 index 0000000..59d4335 --- /dev/null +++ b/vendor/github.com/djherbis/times/times_freebsd.go @@ -0,0 +1,40 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// http://golang.org/src/os/stat_freebsd.go + +package times + +import ( + "os" + "syscall" + "time" +) + +// HasChangeTime and HasBirthTime are true if and only if +// the target OS supports them. +const ( + HasChangeTime = true + HasBirthTime = true +) + +type timespec struct { + atime + mtime + ctime + btime +} + +func timespecToTime(ts syscall.Timespec) time.Time { + return time.Unix(int64(ts.Sec), int64(ts.Nsec)) +} + +func getTimespec(fi os.FileInfo) (t timespec) { + stat := fi.Sys().(*syscall.Stat_t) + t.atime.v = timespecToTime(stat.Atimespec) + t.mtime.v = timespecToTime(stat.Mtimespec) + t.ctime.v = timespecToTime(stat.Ctimespec) + t.btime.v = timespecToTime(stat.Birthtimespec) + return t +} diff --git a/vendor/github.com/djherbis/times/times_js.go b/vendor/github.com/djherbis/times/times_js.go new file mode 100644 index 0000000..40fbd60 --- /dev/null +++ b/vendor/github.com/djherbis/times/times_js.go @@ -0,0 +1,41 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// https://golang.org/src/os/stat_nacljs.go + +// +build js,wasm + +package times + +import ( + "os" + "syscall" + "time" +) + +// HasChangeTime and HasBirthTime are true if and only if +// the target OS supports them. +const ( + HasChangeTime = true + HasBirthTime = false +) + +type timespec struct { + atime + mtime + ctime + nobtime +} + +func timespecToTime(sec, nsec int64) time.Time { + return time.Unix(sec, nsec) +} + +func getTimespec(fi os.FileInfo) (t timespec) { + stat := fi.Sys().(*syscall.Stat_t) + t.atime.v = timespecToTime(stat.Atime, stat.AtimeNsec) + t.mtime.v = timespecToTime(stat.Mtime, stat.MtimeNsec) + t.ctime.v = timespecToTime(stat.Ctime, stat.CtimeNsec) + return t +} diff --git a/vendor/github.com/djherbis/times/times_linux.go b/vendor/github.com/djherbis/times/times_linux.go new file mode 100644 index 0000000..85f87db --- /dev/null +++ b/vendor/github.com/djherbis/times/times_linux.go @@ -0,0 +1,185 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// http://golang.org/src/os/stat_linux.go + +package times + +import ( + "errors" + "os" + "sync/atomic" + "syscall" + "time" + + "golang.org/x/sys/unix" +) + +// HasChangeTime and HasBirthTime are true if and only if +// the target OS supports them. +const ( + HasChangeTime = true + HasBirthTime = false +) + +type timespec struct { + atime + mtime + ctime + nobtime +} + +type timespecBtime struct { + atime + mtime + ctime + btime +} + +var ( + supportsStatx int32 = 1 + statxFunc = unix.Statx +) + +func isStatXSupported() bool { + return atomic.LoadInt32(&supportsStatx) == 1 +} + +func isStatXUnsupported(err error) bool { + // linux 4.10 and earlier does not support Statx syscall + if err != nil && errors.Is(err, unix.ENOSYS) { + atomic.StoreInt32(&supportsStatx, 0) + return true + } + return false +} + +// Stat returns the Timespec for the given filename. +func Stat(name string) (Timespec, error) { + if isStatXSupported() { + ts, err := statX(name) + if err == nil { + return ts, nil + } + if !isStatXUnsupported(err) { + return nil, err + } + // Fallback. + } + return stat(name, os.Stat) +} + +func statX(name string) (Timespec, error) { + // https://man7.org/linux/man-pages/man2/statx.2.html + var statx unix.Statx_t + err := statxFunc(unix.AT_FDCWD, name, unix.AT_STATX_SYNC_AS_STAT, unix.STATX_ATIME|unix.STATX_MTIME|unix.STATX_CTIME|unix.STATX_BTIME, &statx) + if err != nil { + return nil, err + } + return extractTimes(&statx), nil +} + +// Lstat returns the Timespec for the given filename, and does not follow Symlinks. +func Lstat(name string) (Timespec, error) { + if isStatXSupported() { + ts, err := lstatx(name) + if err == nil { + return ts, nil + } + if !isStatXUnsupported(err) { + return nil, err + } + // Fallback. + } + return stat(name, os.Lstat) +} + +func lstatx(name string) (Timespec, error) { + // https://man7.org/linux/man-pages/man2/statx.2.html + var statX unix.Statx_t + err := statxFunc(unix.AT_FDCWD, name, unix.AT_STATX_SYNC_AS_STAT|unix.AT_SYMLINK_NOFOLLOW, unix.STATX_ATIME|unix.STATX_MTIME|unix.STATX_CTIME|unix.STATX_BTIME, &statX) + if err != nil { + return nil, err + } + return extractTimes(&statX), nil +} + +func statXFile(file *os.File) (Timespec, error) { + sc, err := file.SyscallConn() + if err != nil { + return nil, err + } + + var statx unix.Statx_t + var statxErr error + err = sc.Control(func(fd uintptr) { + // https://man7.org/linux/man-pages/man2/statx.2.html + statxErr = statxFunc(int(fd), "", unix.AT_EMPTY_PATH|unix.AT_STATX_SYNC_AS_STAT, unix.STATX_ATIME|unix.STATX_MTIME|unix.STATX_CTIME|unix.STATX_BTIME, &statx) + }) + if err != nil { + return nil, err + } + + if statxErr != nil { + return nil, statxErr + } + + return extractTimes(&statx), nil +} + +// StatFile returns the Timespec for the given *os.File. +func StatFile(file *os.File) (Timespec, error) { + if isStatXSupported() { + ts, err := statXFile(file) + if err == nil { + return ts, nil + } + if !isStatXUnsupported(err) { + return nil, err + } + // Fallback. + } + return statFile(file) +} + +func statFile(file *os.File) (Timespec, error) { + fi, err := file.Stat() + if err != nil { + return nil, err + } + return getTimespec(fi), nil +} + +func statxTimestampToTime(ts unix.StatxTimestamp) time.Time { + return time.Unix(ts.Sec, int64(ts.Nsec)) +} + +func extractTimes(statx *unix.Statx_t) Timespec { + if statx.Mask&unix.STATX_BTIME == unix.STATX_BTIME { + var t timespecBtime + t.atime.v = statxTimestampToTime(statx.Atime) + t.mtime.v = statxTimestampToTime(statx.Mtime) + t.ctime.v = statxTimestampToTime(statx.Ctime) + t.btime.v = statxTimestampToTime(statx.Btime) + return t + } + + var t timespec + t.atime.v = statxTimestampToTime(statx.Atime) + t.mtime.v = statxTimestampToTime(statx.Mtime) + t.ctime.v = statxTimestampToTime(statx.Ctime) + return t +} + +func timespecToTime(ts syscall.Timespec) time.Time { + return time.Unix(int64(ts.Sec), int64(ts.Nsec)) +} + +func getTimespec(fi os.FileInfo) (t timespec) { + stat := fi.Sys().(*syscall.Stat_t) + t.atime.v = timespecToTime(stat.Atim) + t.mtime.v = timespecToTime(stat.Mtim) + t.ctime.v = timespecToTime(stat.Ctim) + return t +} diff --git a/vendor/github.com/djherbis/times/times_nacl.go b/vendor/github.com/djherbis/times/times_nacl.go new file mode 100644 index 0000000..7ef3ed5 --- /dev/null +++ b/vendor/github.com/djherbis/times/times_nacl.go @@ -0,0 +1,39 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// https://golang.org/src/os/stat_nacljs.go + +package times + +import ( + "os" + "syscall" + "time" +) + +// HasChangeTime and HasBirthTime are true if and only if +// the target OS supports them. +const ( + HasChangeTime = true + HasBirthTime = false +) + +type timespec struct { + atime + mtime + ctime + nobtime +} + +func timespecToTime(sec, nsec int64) time.Time { + return time.Unix(sec, nsec) +} + +func getTimespec(fi os.FileInfo) (t timespec) { + stat := fi.Sys().(*syscall.Stat_t) + t.atime.v = timespecToTime(stat.Atime, stat.AtimeNsec) + t.mtime.v = timespecToTime(stat.Mtime, stat.MtimeNsec) + t.ctime.v = timespecToTime(stat.Ctime, stat.CtimeNsec) + return t +} diff --git a/vendor/github.com/djherbis/times/times_netbsd.go b/vendor/github.com/djherbis/times/times_netbsd.go new file mode 100644 index 0000000..fa9149e --- /dev/null +++ b/vendor/github.com/djherbis/times/times_netbsd.go @@ -0,0 +1,40 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// http://golang.org/src/os/stat_netbsd.go + +package times + +import ( + "os" + "syscall" + "time" +) + +// HasChangeTime and HasBirthTime are true if and only if +// the target OS supports them. +const ( + HasChangeTime = true + HasBirthTime = true +) + +type timespec struct { + atime + mtime + ctime + btime +} + +func timespecToTime(ts syscall.Timespec) time.Time { + return time.Unix(int64(ts.Sec), int64(ts.Nsec)) +} + +func getTimespec(fi os.FileInfo) (t timespec) { + stat := fi.Sys().(*syscall.Stat_t) + t.atime.v = timespecToTime(stat.Atimespec) + t.mtime.v = timespecToTime(stat.Mtimespec) + t.ctime.v = timespecToTime(stat.Ctimespec) + t.btime.v = timespecToTime(stat.Birthtimespec) + return t +} diff --git a/vendor/github.com/djherbis/times/times_openbsd.go b/vendor/github.com/djherbis/times/times_openbsd.go new file mode 100644 index 0000000..03c7a65 --- /dev/null +++ b/vendor/github.com/djherbis/times/times_openbsd.go @@ -0,0 +1,39 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// http://golang.org/src/os/stat_openbsd.go + +package times + +import ( + "os" + "syscall" + "time" +) + +// HasChangeTime and HasBirthTime are true if and only if +// the target OS supports them. +const ( + HasChangeTime = true + HasBirthTime = false +) + +type timespec struct { + atime + mtime + ctime + nobtime +} + +func timespecToTime(ts syscall.Timespec) time.Time { + return time.Unix(int64(ts.Sec), int64(ts.Nsec)) +} + +func getTimespec(fi os.FileInfo) (t timespec) { + stat := fi.Sys().(*syscall.Stat_t) + t.atime.v = timespecToTime(stat.Atim) + t.mtime.v = timespecToTime(stat.Mtim) + t.ctime.v = timespecToTime(stat.Ctim) + return t +} diff --git a/vendor/github.com/djherbis/times/times_plan9.go b/vendor/github.com/djherbis/times/times_plan9.go new file mode 100644 index 0000000..0b1beeb --- /dev/null +++ b/vendor/github.com/djherbis/times/times_plan9.go @@ -0,0 +1,34 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// http://golang.org/src/os/stat_plan9.go + +package times + +import ( + "os" + "syscall" + "time" +) + +// HasChangeTime and HasBirthTime are true if and only if +// the target OS supports them. +const ( + HasChangeTime = false + HasBirthTime = false +) + +type timespec struct { + atime + mtime + noctime + nobtime +} + +func getTimespec(fi os.FileInfo) (t timespec) { + stat := fi.Sys().(*syscall.Dir) + t.atime.v = time.Unix(int64(stat.Atime), 0) + t.mtime.v = time.Unix(int64(stat.Mtime), 0) + return t +} diff --git a/vendor/github.com/djherbis/times/times_solaris.go b/vendor/github.com/djherbis/times/times_solaris.go new file mode 100644 index 0000000..b1b15bf --- /dev/null +++ b/vendor/github.com/djherbis/times/times_solaris.go @@ -0,0 +1,39 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// http://golang.org/src/os/stat_solaris.go + +package times + +import ( + "os" + "syscall" + "time" +) + +// HasChangeTime and HasBirthTime are true if and only if +// the target OS supports them. +const ( + HasChangeTime = true + HasBirthTime = false +) + +type timespec struct { + atime + mtime + ctime + nobtime +} + +func timespecToTime(ts syscall.Timespec) time.Time { + return time.Unix(int64(ts.Sec), int64(ts.Nsec)) +} + +func getTimespec(fi os.FileInfo) (t timespec) { + stat := fi.Sys().(*syscall.Stat_t) + t.atime.v = timespecToTime(stat.Atim) + t.mtime.v = timespecToTime(stat.Mtim) + t.ctime.v = timespecToTime(stat.Ctim) + return t +} diff --git a/vendor/github.com/djherbis/times/times_wasip1.go b/vendor/github.com/djherbis/times/times_wasip1.go new file mode 100644 index 0000000..5463a8f --- /dev/null +++ b/vendor/github.com/djherbis/times/times_wasip1.go @@ -0,0 +1,42 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// https://github.com/golang/go/blob/master/src/os/stat_wasip1.go + +//go:build wasip1 +// +build wasip1 + +package times + +import ( + "os" + "syscall" + "time" +) + +// HasChangeTime and HasBirthTime are true if and only if +// the target OS supports them. +const ( + HasChangeTime = true + HasBirthTime = false +) + +type timespec struct { + atime + mtime + ctime + nobtime +} + +func timespecToTime(sec, nsec int64) time.Time { + return time.Unix(sec, nsec) +} + +func getTimespec(fi os.FileInfo) (t timespec) { + stat := fi.Sys().(*syscall.Stat_t) + t.atime.v = timespecToTime(int64(stat.Atime), 0) + t.mtime.v = timespecToTime(int64(stat.Mtime), 0) + t.ctime.v = timespecToTime(int64(stat.Ctime), 0) + return t +} diff --git a/vendor/github.com/djherbis/times/times_windows.go b/vendor/github.com/djherbis/times/times_windows.go new file mode 100644 index 0000000..36828f2 --- /dev/null +++ b/vendor/github.com/djherbis/times/times_windows.go @@ -0,0 +1,36 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// http://golang.org/src/os/stat_windows.go + +package times + +import ( + "os" + "syscall" + "time" +) + +// HasChangeTime and HasBirthTime are true if and only if +// the target OS supports them. +const ( + HasChangeTime = false + HasBirthTime = true +) + +type timespec struct { + atime + mtime + noctime + btime +} + +func getTimespec(fi os.FileInfo) Timespec { + var t timespec + stat := fi.Sys().(*syscall.Win32FileAttributeData) + t.atime.v = time.Unix(0, stat.LastAccessTime.Nanoseconds()) + t.mtime.v = time.Unix(0, stat.LastWriteTime.Nanoseconds()) + t.btime.v = time.Unix(0, stat.CreationTime.Nanoseconds()) + return t +} diff --git a/vendor/github.com/djherbis/times/use_generic_stat.go b/vendor/github.com/djherbis/times/use_generic_stat.go new file mode 100644 index 0000000..0040aa9 --- /dev/null +++ b/vendor/github.com/djherbis/times/use_generic_stat.go @@ -0,0 +1,24 @@ +// +build !windows,!linux + +package times + +import "os" + +// Stat returns the Timespec for the given filename. +func Stat(name string) (Timespec, error) { + return stat(name, os.Stat) +} + +// Lstat returns the Timespec for the given filename, and does not follow Symlinks. +func Lstat(name string) (Timespec, error) { + return stat(name, os.Lstat) +} + +// StatFile returns the Timespec for the given *os.File. +func StatFile(file *os.File) (Timespec, error) { + fi, err := file.Stat() + if err != nil { + return nil, err + } + return getTimespec(fi), nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 2d1de86..ba410d2 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,5 +1,6 @@ # github.com/djherbis/times v1.6.0 ## explicit; go 1.16 +github.com/djherbis/times # github.com/gosuri/uilive v0.0.4 ## explicit; go 1.10 github.com/gosuri/uilive