1 /* Work around platform bugs in utime.
2 Copyright (C) 2017-2024 Free Software Foundation, Inc.
4 This file is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation, either version 3 of the
7 License, or (at your option) any later version.
9 This file is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* Written by Bruno Haible. */
24 #if defined _WIN32 && ! defined __CYGWIN__
28 # include "filename.h"
31 /* Don't assume that UNICODE is not defined. */
33 # define CreateFile CreateFileA
34 # undef GetFileAttributes
35 # define GetFileAttributes GetFileAttributesA
38 _gl_utimens_windows (const char *name
, struct timespec ts
[2])
40 /* POSIX <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13>
41 specifies: "More than two leading <slash> characters shall be treated as
42 a single <slash> character." */
43 if (ISSLASH (name
[0]) && ISSLASH (name
[1]) && ISSLASH (name
[2]))
46 while (ISSLASH (name
[1]))
50 size_t len
= strlen (name
);
51 size_t drive_prefix_len
= (HAS_DEVICE (name
) ? 2 : 0);
53 /* Remove trailing slashes (except the very first one, at position
54 drive_prefix_len), but remember their presence. */
56 bool check_dir
= false;
59 while (rlen
> drive_prefix_len
&& ISSLASH (name
[rlen
-1]))
62 if (rlen
== drive_prefix_len
+ 1)
76 malloca_rname
= malloca (rlen
+ 1);
77 if (malloca_rname
== NULL
)
82 memcpy (malloca_rname
, name
, rlen
);
83 malloca_rname
[rlen
] = '\0';
84 rname
= malloca_rname
;
89 /* Open a handle to the file.
91 <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-createfilea>
92 <https://docs.microsoft.com/en-us/windows/desktop/FileIO/creating-and-opening-files> */
95 FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
,
96 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
99 /* FILE_FLAG_POSIX_SEMANTICS (treat file names that differ only
100 in case as different) makes sense only when applied to *all*
101 filesystem operations. */
102 FILE_FLAG_BACKUP_SEMANTICS
/* | FILE_FLAG_POSIX_SEMANTICS */,
104 if (handle
== INVALID_HANDLE_VALUE
)
106 error
= GetLastError ();
113 <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfileattributesa> */
114 DWORD attributes
= GetFileAttributes (rname
);
115 if (attributes
== INVALID_FILE_ATTRIBUTES
)
117 error
= GetLastError ();
118 CloseHandle (handle
);
121 if ((attributes
& FILE_ATTRIBUTE_DIRECTORY
) == 0)
123 CloseHandle (handle
);
124 if (malloca_rname
!= NULL
)
125 freea (malloca_rname
);
132 /* Use SetFileTime(). See
133 <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-setfiletime>
134 <https://docs.microsoft.com/en-us/windows/desktop/api/minwinbase/ns-minwinbase-filetime> */
135 FILETIME last_access_time
;
136 FILETIME last_write_time
;
139 /* GetSystemTimeAsFileTime is the same as
140 GetSystemTime followed by SystemTimeToFileTime.
141 <https://docs.microsoft.com/en-us/windows/desktop/api/sysinfoapi/nf-sysinfoapi-getsystemtimeasfiletime>.
142 It would be overkill to use
143 GetSystemTimePreciseAsFileTime
144 <https://docs.microsoft.com/en-us/windows/desktop/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime>. */
145 FILETIME current_time
;
146 GetSystemTimeAsFileTime (¤t_time
);
147 last_access_time
= current_time
;
148 last_write_time
= current_time
;
153 ULONGLONG time_since_16010101
=
154 (ULONGLONG
) ts
[0].tv_sec
* 10000000 + ts
[0].tv_nsec
/ 100 + 116444736000000000LL;
155 last_access_time
.dwLowDateTime
= (DWORD
) time_since_16010101
;
156 last_access_time
.dwHighDateTime
= time_since_16010101
>> 32;
159 ULONGLONG time_since_16010101
=
160 (ULONGLONG
) ts
[1].tv_sec
* 10000000 + ts
[1].tv_nsec
/ 100 + 116444736000000000LL;
161 last_write_time
.dwLowDateTime
= (DWORD
) time_since_16010101
;
162 last_write_time
.dwHighDateTime
= time_since_16010101
>> 32;
165 if (SetFileTime (handle
, NULL
, &last_access_time
, &last_write_time
))
167 CloseHandle (handle
);
168 if (malloca_rname
!= NULL
)
169 freea (malloca_rname
);
175 DWORD sft_error
= GetLastError ();
176 fprintf (stderr
, "utimens SetFileTime error 0x%x\n", (unsigned int) sft_error
);
178 CloseHandle (handle
);
179 if (malloca_rname
!= NULL
)
180 freea (malloca_rname
);
189 fprintf (stderr
, "utimens CreateFile/GetFileAttributes error 0x%x\n", (unsigned int) error
);
191 if (malloca_rname
!= NULL
)
192 freea (malloca_rname
);
196 /* Some of these errors probably cannot happen with the specific flags
197 that we pass to CreateFile. But who knows... */
198 case ERROR_FILE_NOT_FOUND
: /* The last component of rname does not exist. */
199 case ERROR_PATH_NOT_FOUND
: /* Some directory component in rname does not exist. */
200 case ERROR_BAD_PATHNAME
: /* rname is such as '\\server'. */
201 case ERROR_BAD_NETPATH
: /* rname is such as '\\nonexistentserver\share'. */
202 case ERROR_BAD_NET_NAME
: /* rname is such as '\\server\nonexistentshare'. */
203 case ERROR_INVALID_NAME
: /* rname contains wildcards, misplaced colon, etc. */
204 case ERROR_DIRECTORY
:
208 case ERROR_ACCESS_DENIED
: /* rname is such as 'C:\System Volume Information\foo'. */
209 case ERROR_SHARING_VIOLATION
: /* rname is such as 'C:\pagefile.sys'. */
210 errno
= (ts
!= NULL
? EPERM
: EACCES
);
213 case ERROR_OUTOFMEMORY
:
217 case ERROR_WRITE_PROTECT
:
221 case ERROR_WRITE_FAULT
:
222 case ERROR_READ_FAULT
:
223 case ERROR_GEN_FAILURE
:
227 case ERROR_BUFFER_OVERFLOW
:
228 case ERROR_FILENAME_EXCED_RANGE
:
229 errno
= ENAMETOOLONG
;
232 case ERROR_DELETE_PENDING
: /* XXX map to EACCES or EPERM? */
246 utime (const char *name
, const struct utimbuf
*ts
)
249 return _gl_utimens_windows (name
, NULL
);
252 struct timespec ts_with_nanoseconds
[2];
253 ts_with_nanoseconds
[0].tv_sec
= ts
->actime
;
254 ts_with_nanoseconds
[0].tv_nsec
= 0;
255 ts_with_nanoseconds
[1].tv_sec
= ts
->modtime
;
256 ts_with_nanoseconds
[1].tv_nsec
= 0;
257 return _gl_utimens_windows (name
, ts_with_nanoseconds
);
264 # include <sys/stat.h>
265 # include "filename.h"
268 utime (const char *name
, const struct utimbuf
*ts
)
271 # if REPLACE_FUNC_UTIME_FILE
272 /* macOS 10.13 mistakenly succeeds when given a symbolic link to a
273 non-directory with a trailing slash. */
274 size_t len
= strlen (name
);
275 if (len
> 0 && ISSLASH (name
[len
- 1]))
279 if (stat (name
, &buf
) == -1 && errno
!= EOVERFLOW
)
282 # endif /* REPLACE_FUNC_UTIME_FILE */
284 return utime (name
, ts
);