1+ import ctypes
2+ from ctypes import wintypes
3+ import msvcrt
4+
15import os
26import platform
37
@@ -13,6 +17,19 @@ cdef extern from 'windows.h':
1317 cdef extern int PROCESS_QUERY_INFORMATION
1418
1519
20+ # Windows API Constants
21+ FILE_WRITE_ATTRIBUTES = 0x0100
22+ FILE_SHARE_READ = 0x00000001
23+ FILE_SHARE_WRITE = 0x00000002
24+ FILE_SHARE_DELETE = 0x00000004
25+ OPEN_EXISTING = 3
26+ FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
27+
28+
29+ class FILETIME (ctypes.Structure ):
30+ _fields_ = [(" dwLowDateTime" , wintypes.DWORD), (" dwHighDateTime" , wintypes.DWORD)]
31+
32+
1633def getosusername ():
1734 """ Return the OS username."""
1835 return os.getlogin()
@@ -37,3 +54,99 @@ def process_alive(host, pid, thread):
3754def local_pid_alive (pid ):
3855 """ Return whether *pid* is alive."""
3956 raise NotImplementedError
57+
58+
59+ def set_birthtime (path , birthtime_ns , *, fd = None ):
60+ """
61+ Set creation time (birthtime) on *path* (or *fd*) to *birthtime_ns*.
62+ """
63+ # Convert ns to Windows FILETIME
64+ unix_epoch_in_100ns = 116444736000000000
65+ intervals = (birthtime_ns // 100 ) + unix_epoch_in_100ns
66+
67+ ft = FILETIME()
68+ ft.dwLowDateTime = intervals & 0xFFFFFFFF
69+ ft.dwHighDateTime = intervals >> 32
70+
71+ handle = - 1
72+ if fd is not None :
73+ handle = msvcrt.get_osfhandle(fd)
74+ close_handle = False
75+ else :
76+ handle = ctypes.windll.kernel32.CreateFileW(
77+ str (path),
78+ FILE_WRITE_ATTRIBUTES,
79+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
80+ None ,
81+ OPEN_EXISTING,
82+ FILE_FLAG_BACKUP_SEMANTICS,
83+ None ,
84+ )
85+ close_handle = True
86+
87+ if handle == - 1 :
88+ return
89+
90+ try :
91+ # SetFileTime(handle, lpCreationTime, lpLastAccessTime, lpLastWriteTime)
92+ ctypes.windll.kernel32.SetFileTime(handle, ctypes.byref(ft), None , None )
93+ finally :
94+ if close_handle:
95+ ctypes.windll.kernel32.CloseHandle(handle)
96+
97+
98+ def set_timestamps (path , item , fd = None , follow_symlinks = False ):
99+ """ Set timestamps (mtime, atime, birthtime) from *item* on *path* (*fd*)."""
100+ # On Windows, we prefer using a single SetFileTime call if we have or can get a handle.
101+ handle = - 1
102+ close_handle = False
103+ if fd is not None :
104+ handle = msvcrt.get_osfhandle(fd)
105+ close_handle = False
106+ elif path is not None :
107+ handle = ctypes.windll.kernel32.CreateFileW(
108+ str (path),
109+ FILE_WRITE_ATTRIBUTES,
110+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
111+ None ,
112+ OPEN_EXISTING,
113+ FILE_FLAG_BACKUP_SEMANTICS,
114+ None ,
115+ )
116+ close_handle = True
117+
118+ if handle != - 1 :
119+ try :
120+ mtime_ns = item.mtime
121+ mtime_intervals = (mtime_ns // 100 ) + 116444736000000000
122+ ft_mtime = FILETIME(mtime_intervals & 0xFFFFFFFF , mtime_intervals >> 32 )
123+
124+ atime_ns = item.atime if " atime" in item else mtime_ns
125+ atime_intervals = (atime_ns // 100 ) + 116444736000000000
126+ ft_atime = FILETIME(atime_intervals & 0xFFFFFFFF , atime_intervals >> 32 )
127+
128+ ft_birthtime = None
129+ if " birthtime" in item:
130+ birthtime_ns = item.birthtime
131+ birthtime_intervals = (birthtime_ns // 100 ) + 116444736000000000
132+ ft_birthtime = FILETIME(birthtime_intervals & 0xFFFFFFFF , birthtime_intervals >> 32 )
133+
134+ ctypes.windll.kernel32.SetFileTime(
135+ handle,
136+ ctypes.byref(ft_birthtime) if ft_birthtime else None ,
137+ ctypes.byref(ft_atime),
138+ ctypes.byref(ft_mtime),
139+ )
140+ return
141+ finally :
142+ if close_handle:
143+ ctypes.windll.kernel32.CloseHandle(handle)
144+
145+ # Fallback to os.utime if handle acquisition failed or wasn't attempted (path only)
146+ # Note: os.utime on Windows doesn't support birthtime or fd.
147+ mtime = item.mtime
148+ atime = item.atime if " atime" in item else mtime
149+ try :
150+ os.utime(path, ns = (atime, mtime))
151+ except OSError :
152+ pass
0 commit comments