Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x/sys/windows: missing GetFileTime function on Windows #21541

Closed
skillian opened this issue Aug 19, 2017 · 8 comments
Closed

x/sys/windows: missing GetFileTime function on Windows #21541

skillian opened this issue Aug 19, 2017 · 8 comments
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done. OS-Windows
Milestone

Comments

@skillian
Copy link

skillian commented Aug 19, 2017

What version of Go are you using (go version)?

Go 1.8.3

Does this issue reproduce with the latest release?

I am able to reproduce this with Go 1.8.3 which seems to be the latest stable version available from golang.org.

What operating system and processor architecture are you using (go env)?

Windows 10 on x86_64

Output of go env:

set GOEXE=.exe
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOOS=windows
set GOPATH=D:\Users\spkillian@outlook.com\Dropbox\dev\go
set GORACE=
set GOROOT=C:\Go
set GOTOOLDIR=C:\Go\pkg\tool\windows_amd64
set GCCGO=gccgo
set CC=gcc
set GOGCCFLAGS=-m64 -mthreads -fmessage-length=0 -fdebug-prefix-map=C:\Users\SPKIL_~1\AppData\Local\Temp\go-build427541383=/tmp/go-build -gno-record-gcc-switches
set CXX=g++
set CGO_ENABLED=1
set PKG_CONFIG=pkg-config
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2

What did you do?

This is for a missing syscall function, so I suppose a minimum reproducible program would be:

If possible, provide a recipe for reproducing the error.
A complete runnable program is good.
A link on play.golang.org is best.

package main

import (
    "fmt"
    "os"
    "syscall"
)

const (
    anyFile = "any_file.txt"
)

func main() {
    var creationTime syscall.Filetime
    var lastAccessTime syscall.Filetime
    var lastWriteTime syscall.Filetime

    utf16FileName, err := syscall.UTF16FromString(anyFile)
    if err != nil {
        fmt.Fprintf(os.Stderr, "Failed to convert string %q to UTF16: %v", anyFile, err)
    }

    err = syscall.GetFileTime(utf16FileName, &creationTime, &lastAccessTime, &lastWriteTime)
    if err != nil {
        fmt.Fprintf(os.Stderr, "Error getting FileTime of file %v: %v", anyFile, err)
        return
    }
    // do something with creationTime, lastAccessTime and/or lastWriteTime
    return
}

Go Playground

What did you expect to see?

syscall.GetFileName should be defined on Windows, like syscall.SetFileTime

What did you see instead?

PowerShell output:
github.com\skillian\getfiletime\expected.go:23: undefined: syscall.GetFileTime

@odeke-em
Copy link
Member

/cc @alexbrainman

@odeke-em odeke-em changed the title Missing GetFileTime in syscall package syscall/windows: missing GetFileTime function Aug 20, 2017
@odeke-em odeke-em changed the title syscall/windows: missing GetFileTime function syscall: missing GetFileTime function on Windows Aug 20, 2017
@skillian
Copy link
Author

My use case is that I have a simple grep-like program that also needs to include only files created within a date range. Sometimes the files are modified after they are created so using os.FileInfo.ModTime is not sufficient.

@alexbrainman
Copy link
Member

@skillian, yes, syscall package does not have SetFileTime Windows API implemented. Unfortunately, we cannot add SetFileTime to syscall package - it has been decided to freeze syscall package. But we can add SetFileTime to golang.org/x/sys/windows package. In fact you can do it yourself - https://golang.org/doc/contribute.html is how to contribute to Go project. Would you like to do it yourself? If not, I or others will do it.

Alex

@skillian
Copy link
Author

skillian commented Aug 21, 2017

@alexbrainman Thank you for looking into this. I don't mean to be pedantic, but I want to clarify that I'm looking at adding syscall.GetFileTime because syscall.SetFileTime is in fact implemented by syscall currently (just tested in 1.8.3):

package main

import (
    "fmt"
    "os"
    "syscall"
)

func main() {
    filename := os.Args[1]

    handle, err := syscall.CreateFile(
        syscall.StringToUTF16Ptr(filename),
        syscall.FILE_WRITE_ATTRIBUTES,
        syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE | syscall.FILE_SHARE_DELETE,
        nil,
        syscall.OPEN_EXISTING,
        syscall.FILE_ATTRIBUTE_NORMAL,
        0)

    if err != nil {
        fmt.Fprintf(os.Stderr, "Error opening %q: %v", filename, err)
        return
    }

    defer syscall.CloseHandle(handle)

    ctime := syscall.Filetime{0x01020304, 0x05060708}
    atime := syscall.Filetime{0x090A0B0C, 0x0D0E0F10}
    mtime := syscall.Filetime{0x11121314, 0x15161718}

    err = syscall.SetFileTime(handle, &ctime, &atime, &mtime)

    if err != nil {
        fmt.Fprintf(os.Stderr, "Error setting file %q time: %v", filename, err)
    }
}

That being said, I understand what you mean about syscall being frozen and will instead try to put this into golang.org/x/sys/windows like you said.

I would love to contribute this myself and had begun following the directions on the contribute.html page you referenced, but I don't understand how the zsyscall_*.go files are emitted by go generate, so I don't know what I'd have to change to have go generate emit the code for GetFileTime (is it just by adding a signature in a sys comment like the other lines starting on line 83?) If you could tell me or tell me where I could look to figure that out myself, I'd be more than happy to do so.

EDIT: removed # from line number to remove link to Issue 83.

@alexbrainman
Copy link
Member

I don't mean to be pedantic, but I want to clarify that I'm looking at adding syscall.GetFileTime because syscall.SetFileTime is in fact implemented by syscall currently

Correct. Sorry for confusing you. I made a mistake.

I understand what you mean about syscall being frozen and will instead try to put this into golang.org/x/sys/windows

Please do. That was what I was suggesting.

so I don't know what I'd have to change to have go generate emit the code for GetFileTime (is it just by adding a signature in a sys comment like the other lines starting on line 83?) If you could tell me or tell me where I could look to figure that out myself, I'd be more than happy to do so.

When you run "go generate" command, it looks for "//go:generate" string anywhere in your source files in the current directory:

$ pwd
/home/a/src/golang.org/x/sys/windows
$ grep //go:generate * -n
syscall_windows.go:17://go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go eventlog.go service.go syscall_windows.go security_windows.go
$

and then "go generate" command executes whatever command it finds. So in golang.org/x/sys/windows it will run

go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go eventlog.go service.go syscall_windows.go security_windows.go

You can see what $GOROOT/src/syscall/mksyscall_windows.go program do. But it looks for lines starting with "//sys", and generates syscalls for each line and output them all into zsyscall_windows.go. For example, this:

$ grep ^//sys.*SetFileTime * -n
syscall_windows.go:139://sys    SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (err error)
$

is what "//sys" line for SetFileTime looks like. You should add similar line for GetFileTime and run "go generate", and that should do it.

You can even do it in your own package, for example github.com/alexbrainman/winapi
But feel free to update golang.org/x/sys/windows if you like.

Alex

@ianlancetaylor ianlancetaylor changed the title syscall: missing GetFileTime function on Windows x/sys/windows: missing GetFileTime function on Windows Mar 30, 2018
@gopherbot gopherbot added this to the Unreleased milestone Mar 30, 2018
@ianlancetaylor ianlancetaylor added the NeedsFix The path to resolution is known, but the work has not been done. label Mar 30, 2018
@ianlancetaylor ianlancetaylor modified the milestones: Unreleased, Unplanned Mar 30, 2018
@cobolbaby
Copy link

import (
	"log"
	"syscall"
	"time"
)

func main() {

	ctime := time.Now()
	err := Chtimes("C:\\Users\\dev\\Desktop\\test2.zip", ctime, ctime, ctime)
	if err != nil {
		log.Fatal(err)
	}
}

// Chtimes changes the access and modification times of the named
// file, similar to the Unix utime() or utimes() functions.
//
// The underlying filesystem may truncate or round the values to a
// less precise time unit.
// If there is an error, it will be of type *PathError.
func Chtimes(name string, ctime time.Time, atime time.Time, mtime time.Time) error {
	var utimes [3]syscall.Timespec
	utimes[0] = syscall.NsecToTimespec(atime.UnixNano())
	utimes[1] = syscall.NsecToTimespec(mtime.UnixNano())
	utimes[2] = syscall.NsecToTimespec(ctime.UnixNano())
	if e := UtimesNano(name, utimes[0:]); e != nil {
		return e
	}
	return nil
}

func UtimesNano(path string, ts []syscall.Timespec) (err error) {
	if len(ts) != 3 {
		return syscall.EINVAL
	}
	pathp, e := syscall.UTF16PtrFromString(path)
	if e != nil {
		return e
	}
	h, e := syscall.CreateFile(pathp,
		syscall.FILE_WRITE_ATTRIBUTES, syscall.FILE_SHARE_WRITE, nil,
		syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
	if e != nil {
		return e
	}
	defer syscall.Close(h)
	a := syscall.NsecToFiletime(syscall.TimespecToNsec(ts[0]))
	w := syscall.NsecToFiletime(syscall.TimespecToNsec(ts[1]))
	c := syscall.NsecToFiletime(syscall.TimespecToNsec(ts[2]))
	return syscall.SetFileTime(h, &c, &a, &w)
}

@iwdgo
Copy link
Contributor

iwdgo commented Dec 12, 2021

Using the existing os.Stat() might solve the issue as it also returns the elements of GetFileTime().
The following code elements illustrate how to get ctime (creation time):

        fs, err := os.Stat(<file name>)
	if err != nil {
		// Error handling
		return
	}
	fsys := fs.Sys().(*syscall.Win32FileAttributeData)
        // fsys contains all values of syscall.GetFileAttributesEx()
        fsys.CreationTime
	fsys.LastAccessTime
	fsys.LastWriteTime // == fs.ModTime() after conversion
        // To get a time format, one method is:
        time.Unix(0, fsys.CreationTime.Nanoseconds()))

@gopherbot gopherbot added the compiler/runtime Issues related to the Go compiler and/or runtime. label Jul 7, 2022
@gopherbot
Copy link
Contributor

Change https://go.dev/cl/528015 mentions this issue: windows: add GetFileTime

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done. OS-Windows
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants