]>
Commit | Line | Data |
---|---|---|
1fd5e000 CF |
1 | /* syscalls.cc: syscalls |
2 | ||
bc837d22 CF |
3 | Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, |
4 | 2007, 2008, 2009, 2010, 2011, 2012, 2013 Red Hat, Inc. | |
1fd5e000 CF |
5 | |
6 | This file is part of Cygwin. | |
7 | ||
8 | This software is a copyrighted work licensed under the terms of the | |
9 | Cygwin license. Please consult the file "CYGWIN_LICENSE" for | |
10 | details. */ | |
11 | ||
3543669f | 12 | #define fstat __FOOfstat__ |
ae89a496 | 13 | #define lstat __FOOlstat__ |
3543669f | 14 | #define stat __FOOstat__ |
5ec14fe4 CF |
15 | #define _close __FOO_close__ |
16 | #define _lseek __FOO_lseek__ | |
17 | #define _open __FOO_open__ | |
18 | #define _read __FOO_read__ | |
19 | #define _write __FOO_write__ | |
b1aae492 CV |
20 | #define _open64 __FOO_open64__ |
21 | #define _lseek64 __FOO_lseek64__ | |
22 | #define _fstat64 __FOO_fstat64__ | |
7d7d09ae CF |
23 | #define pread __FOO_pread |
24 | #define pwrite __FOO_pwrite | |
5ec14fe4 | 25 | |
4c8d72de | 26 | #include "winsup.h" |
57c7e05e | 27 | #include <winnls.h> |
ade47a34 | 28 | #include "miscfuncs.h" |
1fd5e000 CF |
29 | #include <sys/stat.h> |
30 | #include <sys/vfs.h> /* needed for statfs */ | |
a652e6d5 | 31 | #include <sys/statvfs.h> /* needed for statvfs */ |
1fd5e000 CF |
32 | #include <stdlib.h> |
33 | #include <stdio.h> | |
3b8698d7 | 34 | #include <process.h> |
1fd5e000 | 35 | #include <utmp.h> |
2ef89b22 | 36 | #include <utmpx.h> |
1fd5e000 | 37 | #include <sys/uio.h> |
24efca14 | 38 | #include <ctype.h> |
0d6f2b01 | 39 | #include <wctype.h> |
3543669f | 40 | #include <unistd.h> |
c16548b2 | 41 | #include <sys/wait.h> |
e587f14b | 42 | #include <dirent.h> |
1c9b2968 | 43 | #include "ntdll.h" |
3543669f CF |
44 | |
45 | #undef fstat | |
ae89a496 | 46 | #undef lstat |
3543669f | 47 | #undef stat |
7d7d09ae CF |
48 | #undef pread |
49 | #undef pwrite | |
3543669f | 50 | |
f0338f54 | 51 | #include <cygwin/version.h> |
bccd5e0d | 52 | #include "cygerrno.h" |
95a8465b | 53 | #include "perprocess.h" |
6b91b8d5 | 54 | #include "security.h" |
bccd5e0d | 55 | #include "path.h" |
7ac61736 | 56 | #include "fhandler.h" |
e2ebe117 | 57 | #include "dtable.h" |
bccd5e0d | 58 | #include "sigproc.h" |
e2ebe117 | 59 | #include "pinfo.h" |
29ac7f89 | 60 | #include "shared_info.h" |
a4785603 | 61 | #include "cygheap.h" |
d6ffc075 | 62 | #include "pwdgrp.h" |
c448f78f CF |
63 | #include "cpuid.h" |
64 | #include "registry.h" | |
ac300315 | 65 | #include "environ.h" |
c57b57e5 | 66 | #include "tls_pbuf.h" |
56a19715 CF |
67 | #include "sync.h" |
68 | #include "child_info.h" | |
1fd5e000 | 69 | |
5ec14fe4 CF |
70 | #undef _close |
71 | #undef _lseek | |
72 | #undef _open | |
73 | #undef _read | |
74 | #undef _write | |
b1aae492 CV |
75 | #undef _open64 |
76 | #undef _lseek64 | |
77 | #undef _fstat64 | |
5ec14fe4 | 78 | |
7ac61736 CF |
79 | static int __stdcall mknod_worker (const char *, mode_t, mode_t, _major_t, |
80 | _minor_t); | |
81 | ||
1fd5e000 CF |
82 | /* Close all files and process any queued deletions. |
83 | Lots of unix style applications will open a tmp file, unlink it, | |
84 | but never call close. This function is called by _exit to | |
85 | ensure we don't leave any such files lying around. */ | |
86 | ||
87 | void __stdcall | |
26edeb6a | 88 | close_all_files (bool norelease) |
1fd5e000 | 89 | { |
2d1d1eb1 | 90 | cygheap->fdtab.lock (); |
1dc16fc7 | 91 | |
8fbd574e CV |
92 | semaphore::terminate (); |
93 | ||
93d606f6 CF |
94 | HANDLE h = NULL; |
95 | ||
0381fec6 | 96 | for (int i = 0; i < (int) cygheap->fdtab.size; i++) |
ab1fb535 | 97 | { |
3e9c8f2e | 98 | cygheap_fdget cfd (i, false, false); |
ab1fb535 CF |
99 | if (cfd >= 0) |
100 | { | |
101 | debug_only_printf ("closing fd %d", i); | |
102 | if (i == 2) | |
103 | DuplicateHandle (GetCurrentProcess (), cfd->get_output_handle (), | |
104 | GetCurrentProcess (), &h, | |
105 | 0, false, DUPLICATE_SAME_ACCESS); | |
106 | cfd->close_with_arch (); | |
107 | if (!norelease) | |
108 | cfd.release (); | |
109 | } | |
110 | } | |
1fd5e000 | 111 | |
56a19715 | 112 | if (!have_execed && cygheap->ctty) |
2d1d1eb1 | 113 | cygheap->close_ctty (); |
fe861ce9 | 114 | |
480b13a3 | 115 | fhandler_base_overlapped::flush_all_async_io (); |
93d606f6 CF |
116 | if (h) |
117 | SetStdHandle (STD_ERROR_HANDLE, h); | |
2d1d1eb1 | 118 | cygheap->fdtab.unlock (); |
1fd5e000 CF |
119 | } |
120 | ||
e70fdfb9 | 121 | extern "C" int |
35f879a6 CF |
122 | dup (int fd) |
123 | { | |
ca35d41c CF |
124 | int res = cygheap->fdtab.dup3 (fd, cygheap_fdnew (), 0); |
125 | syscall_printf ("%R = dup(%d)", res, fd); | |
126 | return res; | |
35f879a6 CF |
127 | } |
128 | ||
3143cb7c CF |
129 | inline int |
130 | dup_finish (int oldfd, int newfd, int flags) | |
131 | { | |
132 | int res; | |
b9c61a8d | 133 | if ((res = cygheap->fdtab.dup3 (oldfd, newfd, flags | O_EXCL)) == newfd) |
3143cb7c CF |
134 | { |
135 | cygheap_fdget (newfd)->inc_refcnt (); | |
136 | cygheap->fdtab.unlock (); /* dup3 exits with lock set on success */ | |
137 | } | |
138 | return res; | |
139 | } | |
140 | ||
e70fdfb9 | 141 | extern "C" int |
35f879a6 CF |
142 | dup2 (int oldfd, int newfd) |
143 | { | |
ca35d41c | 144 | int res; |
f6187d46 | 145 | if (newfd >= OPEN_MAX_MAX || newfd < 0) |
29bca9bb | 146 | { |
29bca9bb | 147 | set_errno (EBADF); |
ca35d41c | 148 | res = -1; |
29bca9bb | 149 | } |
ca35d41c | 150 | else if (newfd == oldfd) |
e70fdfb9 CV |
151 | { |
152 | cygheap_fdget cfd (oldfd); | |
ca35d41c | 153 | res = (cfd >= 0) ? oldfd : -1; |
e70fdfb9 | 154 | } |
3143cb7c CF |
155 | else |
156 | res = dup_finish (oldfd, newfd, 0); | |
ca35d41c CF |
157 | |
158 | syscall_printf ("%R = dup2(%d, %d)", res, oldfd, newfd); | |
159 | return res; | |
e70fdfb9 CV |
160 | } |
161 | ||
162 | extern "C" int | |
163 | dup3 (int oldfd, int newfd, int flags) | |
164 | { | |
ca35d41c | 165 | int res; |
e70fdfb9 CV |
166 | if (newfd >= OPEN_MAX_MAX) |
167 | { | |
e70fdfb9 | 168 | set_errno (EBADF); |
ca35d41c | 169 | res = -1; |
e70fdfb9 | 170 | } |
ca35d41c | 171 | else if (newfd == oldfd) |
e70fdfb9 CV |
172 | { |
173 | cygheap_fdget cfd (oldfd, false, false); | |
174 | set_errno (cfd < 0 ? EBADF : EINVAL); | |
ca35d41c | 175 | res = -1; |
e70fdfb9 | 176 | } |
3143cb7c CF |
177 | else |
178 | res = dup_finish (oldfd, newfd, flags); | |
8fa8b3a3 | 179 | |
d3cb1dff | 180 | syscall_printf ("%R = dup3(%d, %d, %p)", res, oldfd, newfd, flags); |
ca35d41c | 181 | return res; |
35f879a6 CF |
182 | } |
183 | ||
18df3935 CV |
184 | static inline void |
185 | start_transaction (HANDLE &old_trans, HANDLE &trans) | |
186 | { | |
187 | NTSTATUS status = NtCreateTransaction (&trans, | |
188 | SYNCHRONIZE | TRANSACTION_ALL_ACCESS, | |
189 | NULL, NULL, NULL, 0, 0, 0, NULL, NULL); | |
190 | if (NT_SUCCESS (status)) | |
191 | { | |
192 | old_trans = RtlGetCurrentTransaction (); | |
193 | RtlSetCurrentTransaction (trans); | |
194 | } | |
195 | else | |
196 | { | |
197 | debug_printf ("NtCreateTransaction failed, %p", status); | |
198 | old_trans = trans = NULL; | |
199 | } | |
200 | } | |
201 | ||
202 | static inline NTSTATUS | |
203 | stop_transaction (NTSTATUS status, HANDLE old_trans, HANDLE trans) | |
204 | { | |
205 | RtlSetCurrentTransaction (old_trans); | |
206 | if (NT_SUCCESS (status)) | |
207 | status = NtCommitTransaction (trans, TRUE); | |
208 | else | |
209 | status = NtRollbackTransaction (trans, TRUE); | |
210 | NtClose (trans); | |
211 | return status; | |
212 | } | |
213 | ||
61c44b72 | 214 | static char desktop_ini[] = |
115d74b9 CV |
215 | "[.ShellClassInfo]\r\n" |
216 | "CLSID={645FF040-5081-101B-9F08-00AA002F954E}\r\n"; | |
217 | ||
218 | static char desktop_ini_ext[] = | |
219 | "LocalizedResourceName=@%SystemRoot%\\system32\\shell32.dll,-8964\r\n"; | |
220 | ||
61c44b72 CV |
221 | static BYTE info2[] = |
222 | { | |
223 | 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
224 | 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | |
225 | }; | |
226 | ||
8a0f3bbf | 227 | enum bin_status |
cbfb7b1b | 228 | { |
8a0f3bbf CV |
229 | dont_move, |
230 | move_to_bin, | |
a654829a CV |
231 | has_been_moved, |
232 | dir_not_empty | |
8a0f3bbf CV |
233 | }; |
234 | ||
235 | static bin_status | |
236 | try_to_bin (path_conv &pc, HANDLE &fh, ACCESS_MASK access) | |
237 | { | |
238 | bin_status bin_stat = move_to_bin; | |
cbfb7b1b | 239 | NTSTATUS status; |
61c44b72 | 240 | OBJECT_ATTRIBUTES attr; |
cbfb7b1b | 241 | IO_STATUS_BLOCK io; |
8a0f3bbf | 242 | HANDLE rootdir = NULL, recyclerdir = NULL, tmp_fh = NULL; |
61c44b72 CV |
243 | USHORT recycler_base_len = 0, recycler_user_len = 0; |
244 | UNICODE_STRING root, recycler, fname; | |
245 | WCHAR recyclerbuf[NAME_MAX + 1]; /* Enough for recycler + SID + filename */ | |
246 | PFILE_NAME_INFORMATION pfni; | |
247 | PFILE_INTERNAL_INFORMATION pfii; | |
248 | PFILE_RENAME_INFORMATION pfri; | |
a654829a | 249 | ULONG frisiz; |
8a0f3bbf | 250 | FILE_DISPOSITION_INFORMATION disp = { TRUE }; |
115d74b9 | 251 | bool fs_has_per_user_recycler = pc.fs_is_ntfs () || pc.fs_is_refs (); |
cbfb7b1b | 252 | |
0c402301 CV |
253 | tmp_pathbuf tp; |
254 | PBYTE infobuf = (PBYTE) tp.w_get (); | |
255 | ||
61c44b72 | 256 | pfni = (PFILE_NAME_INFORMATION) infobuf; |
0c402301 | 257 | status = NtQueryInformationFile (fh, &io, pfni, 65536, FileNameInformation); |
61c44b72 | 258 | if (!NT_SUCCESS (status)) |
cbfb7b1b | 259 | { |
aa982024 CV |
260 | debug_printf ("NtQueryInformationFile (%S, FileNameInformation) " |
261 | "failed, status = %p", pc.get_nt_native_path (), status); | |
61c44b72 | 262 | goto out; |
cbfb7b1b | 263 | } |
61c44b72 CV |
264 | /* The filename could change, the parent dir not. So we split both paths |
265 | and take the prefix. However, there are two special cases: | |
266 | - The handle refers to the root dir of the volume. | |
267 | - The handle refers to the recycler or a subdir. | |
268 | Both cases are handled by just returning and not even trying to move | |
269 | them into the recycler. */ | |
270 | if (pfni->FileNameLength == 2) /* root dir. */ | |
271 | goto out; | |
272 | /* Initialize recycler path. */ | |
273 | RtlInitEmptyUnicodeString (&recycler, recyclerbuf, sizeof recyclerbuf); | |
314c2d2f | 274 | if (!pc.isremote ()) |
cbfb7b1b | 275 | { |
115d74b9 CV |
276 | if (wincap.has_recycle_dot_bin ()) /* NTFS and FAT since Vista, ReFS */ |
277 | RtlAppendUnicodeToString (&recycler, L"\\$RECYCLE.BIN\\"); | |
314c2d2f CV |
278 | else if (pc.fs_is_ntfs ()) /* NTFS up to 2K3 */ |
279 | RtlAppendUnicodeToString (&recycler, L"\\RECYCLER\\"); | |
280 | else if (pc.fs_is_fat ()) /* FAT up to 2K3 */ | |
281 | RtlAppendUnicodeToString (&recycler, L"\\Recycled\\"); | |
282 | else | |
283 | goto out; | |
284 | /* Is the file a subdir of the recycler? */ | |
285 | RtlInitCountedUnicodeString(&fname, pfni->FileName, pfni->FileNameLength); | |
286 | if (RtlEqualUnicodePathPrefix (&fname, &recycler, TRUE)) | |
287 | goto out; | |
288 | /* Is fname the recycler? Temporarily hide trailing backslash. */ | |
289 | recycler.Length -= sizeof (WCHAR); | |
290 | if (RtlEqualUnicodeString (&fname, &recycler, TRUE)) | |
291 | goto out; | |
292 | ||
293 | /* Create root dir path from file name information. */ | |
294 | RtlSplitUnicodePath (&fname, &fname, NULL); | |
295 | RtlSplitUnicodePath (pc.get_nt_native_path (), &root, NULL); | |
296 | root.Length -= fname.Length - sizeof (WCHAR); | |
297 | ||
298 | /* Open root directory. All recycler bin ops are caseinsensitive. */ | |
299 | InitializeObjectAttributes (&attr, &root, OBJ_CASE_INSENSITIVE, | |
300 | NULL, NULL); | |
301 | status = NtOpenFile (&rootdir, FILE_TRAVERSE, &attr, &io, | |
302 | FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT); | |
303 | if (!NT_SUCCESS (status)) | |
304 | { | |
aa982024 | 305 | debug_printf ("NtOpenFile (%S) failed, status = %p", &root, status); |
314c2d2f CV |
306 | goto out; |
307 | } | |
cbfb7b1b | 308 | |
314c2d2f CV |
309 | /* Strip leading backslash */ |
310 | ++recycler.Buffer; | |
311 | recycler.Length -= sizeof (WCHAR); | |
312 | /* Store length of recycler base dir, if it's necessary to create it. */ | |
313 | recycler_base_len = recycler.Length; | |
115d74b9 CV |
314 | /* On NTFS or ReFS the recycler dir contains user specific subdirs, which |
315 | are the actual recycle bins per user. The name if this dir is the | |
316 | string representation of the user SID. */ | |
317 | if (fs_has_per_user_recycler) | |
314c2d2f CV |
318 | { |
319 | UNICODE_STRING sid; | |
320 | WCHAR sidbuf[128]; | |
321 | /* Unhide trailing backslash. */ | |
322 | recycler.Length += sizeof (WCHAR); | |
323 | RtlInitEmptyUnicodeString (&sid, sidbuf, sizeof sidbuf); | |
324 | /* In contrast to what MSDN claims, this function is already available | |
325 | since NT4. */ | |
326 | RtlConvertSidToUnicodeString (&sid, cygheap->user.sid (), FALSE); | |
327 | RtlAppendUnicodeStringToString (&recycler, &sid); | |
328 | recycler_user_len = recycler.Length; | |
329 | } | |
330 | RtlAppendUnicodeToString (&recycler, L"\\"); | |
61c44b72 | 331 | } |
314c2d2f CV |
332 | /* Create hopefully unique filename. |
333 | Since we have to stick to the current directory on remote shares, make | |
334 | the new filename at least very unlikely to match by accident. It starts | |
335 | with ".cyg", with "cyg" transposed into the Unicode low surrogate area | |
336 | starting at U+dc00. Use plain ASCII chars on filesystems not supporting | |
337 | Unicode. The rest of the filename is the inode number in hex encoding | |
338 | and a hash of the full NT path in hex. The combination allows to remove | |
339 | multiple hardlinks to the same file. */ | |
340 | RtlAppendUnicodeToString (&recycler, | |
341 | pc.fs_flags () & FILE_UNICODE_ON_DISK | |
342 | ? L".\xdc63\xdc79\xdc67" : L".cyg"); | |
61c44b72 | 343 | pfii = (PFILE_INTERNAL_INFORMATION) infobuf; |
a654829a CV |
344 | /* Note: Modern Samba versions apparently don't like buffer sizes of more |
345 | than 65535 in some NtQueryInformationFile/NtSetInformationFile calls. | |
346 | Therefore we better use exact buffer sizes from now on. */ | |
347 | status = NtQueryInformationFile (fh, &io, pfii, sizeof *pfii, | |
61c44b72 CV |
348 | FileInternalInformation); |
349 | if (!NT_SUCCESS (status)) | |
350 | { | |
aa982024 CV |
351 | debug_printf ("NtQueryInformationFile (%S, FileInternalInformation) " |
352 | "failed, status = %p", pc.get_nt_native_path (), status); | |
61c44b72 CV |
353 | goto out; |
354 | } | |
355 | RtlInt64ToHexUnicodeString (pfii->FileId.QuadPart, &recycler, TRUE); | |
314c2d2f CV |
356 | RtlInt64ToHexUnicodeString (hash_path_name (0, pc.get_nt_native_path ()), |
357 | &recycler, TRUE); | |
61c44b72 CV |
358 | /* Shoot. */ |
359 | pfri = (PFILE_RENAME_INFORMATION) infobuf; | |
360 | pfri->ReplaceIfExists = TRUE; | |
314c2d2f | 361 | pfri->RootDirectory = pc.isremote () ? NULL : rootdir; |
61c44b72 CV |
362 | pfri->FileNameLength = recycler.Length; |
363 | memcpy (pfri->FileName, recycler.Buffer, recycler.Length); | |
a654829a CV |
364 | frisiz = sizeof *pfri + pfri->FileNameLength - sizeof (WCHAR); |
365 | status = NtSetInformationFile (fh, &io, pfri, frisiz, FileRenameInformation); | |
314c2d2f | 366 | if (status == STATUS_OBJECT_PATH_NOT_FOUND && !pc.isremote ()) |
61c44b72 CV |
367 | { |
368 | /* Ok, so the recycler and/or the recycler/SID directory don't exist. | |
369 | First reopen root dir with permission to create subdirs. */ | |
370 | NtClose (rootdir); | |
115d74b9 CV |
371 | InitializeObjectAttributes (&attr, &root, OBJ_CASE_INSENSITIVE, |
372 | NULL, NULL); | |
61c44b72 CV |
373 | status = NtOpenFile (&rootdir, FILE_ADD_SUBDIRECTORY, &attr, &io, |
374 | FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT); | |
375 | if (!NT_SUCCESS (status)) | |
376 | { | |
aa982024 CV |
377 | debug_printf ("NtOpenFile (%S) failed, status = %p", |
378 | &recycler, status); | |
61c44b72 CV |
379 | goto out; |
380 | } | |
381 | /* Then check if recycler exists by opening and potentially creating it. | |
382 | Yes, we can really do that. Typically the recycle bin is created | |
115d74b9 CV |
383 | by the first user actually using the bin. Pre-Vista, the permissions |
384 | are the default permissions propagated from the root directory. | |
385 | Since Vista the top-level recycle dir has explicit permissions. */ | |
61c44b72 | 386 | InitializeObjectAttributes (&attr, &recycler, OBJ_CASE_INSENSITIVE, |
115d74b9 CV |
387 | rootdir, |
388 | wincap.has_recycle_dot_bin () | |
389 | ? recycler_sd (true, true) : NULL); | |
61c44b72 CV |
390 | recycler.Length = recycler_base_len; |
391 | status = NtCreateFile (&recyclerdir, | |
392 | READ_CONTROL | |
115d74b9 | 393 | | (fs_has_per_user_recycler ? 0 : FILE_ADD_FILE), |
61c44b72 CV |
394 | &attr, &io, NULL, |
395 | FILE_ATTRIBUTE_DIRECTORY | |
396 | | FILE_ATTRIBUTE_SYSTEM | |
397 | | FILE_ATTRIBUTE_HIDDEN, | |
398 | FILE_SHARE_VALID_FLAGS, FILE_OPEN_IF, | |
399 | FILE_DIRECTORY_FILE, NULL, 0); | |
400 | if (!NT_SUCCESS (status)) | |
a10c6f03 | 401 | { |
aa982024 CV |
402 | debug_printf ("NtCreateFile (%S) failed, status = %p", |
403 | &recycler, status); | |
61c44b72 CV |
404 | goto out; |
405 | } | |
406 | /* Next, if necessary, check if the recycler/SID dir exists and | |
70300fdb | 407 | create it if not. */ |
115d74b9 | 408 | if (fs_has_per_user_recycler) |
70300fdb | 409 | { |
61c44b72 CV |
410 | NtClose (recyclerdir); |
411 | recycler.Length = recycler_user_len; | |
115d74b9 CV |
412 | InitializeObjectAttributes (&attr, &recycler, OBJ_CASE_INSENSITIVE, |
413 | rootdir, recycler_sd (false, true)); | |
61c44b72 CV |
414 | status = NtCreateFile (&recyclerdir, READ_CONTROL | FILE_ADD_FILE, |
415 | &attr, &io, NULL, FILE_ATTRIBUTE_DIRECTORY | |
416 | | FILE_ATTRIBUTE_SYSTEM | |
417 | | FILE_ATTRIBUTE_HIDDEN, | |
418 | FILE_SHARE_VALID_FLAGS, FILE_OPEN_IF, | |
419 | FILE_DIRECTORY_FILE, NULL, 0); | |
420 | if (!NT_SUCCESS (status)) | |
421 | { | |
aa982024 | 422 | debug_printf ("NtCreateFile (%S) failed, status = %p", |
61c44b72 CV |
423 | &recycler, status); |
424 | goto out; | |
425 | } | |
426 | } | |
427 | /* The desktop.ini and INFO2 (pre-Vista) files are expected by | |
70300fdb | 428 | Windows Explorer. Otherwise, the created bin is treated as |
61c44b72 CV |
429 | corrupted */ |
430 | if (io.Information == FILE_CREATED) | |
70300fdb | 431 | { |
61c44b72 CV |
432 | RtlInitUnicodeString (&fname, L"desktop.ini"); |
433 | InitializeObjectAttributes (&attr, &fname, OBJ_CASE_INSENSITIVE, | |
115d74b9 | 434 | recyclerdir, recycler_sd (false, false)); |
8a0f3bbf | 435 | status = NtCreateFile (&tmp_fh, FILE_GENERIC_WRITE, &attr, &io, NULL, |
61c44b72 CV |
436 | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN, |
437 | FILE_SHARE_VALID_FLAGS, FILE_CREATE, | |
438 | FILE_SYNCHRONOUS_IO_NONALERT | |
439 | | FILE_NON_DIRECTORY_FILE, NULL, 0); | |
440 | if (!NT_SUCCESS (status)) | |
aa982024 CV |
441 | debug_printf ("NtCreateFile (%S) failed, status = %p", |
442 | &recycler, status); | |
61c44b72 CV |
443 | else |
444 | { | |
8a0f3bbf | 445 | status = NtWriteFile (tmp_fh, NULL, NULL, NULL, &io, desktop_ini, |
61c44b72 CV |
446 | sizeof desktop_ini - 1, NULL, NULL); |
447 | if (!NT_SUCCESS (status)) | |
aa982024 CV |
448 | debug_printf ("NtWriteFile (%S) failed, status = %p", |
449 | &fname, status); | |
115d74b9 CV |
450 | else if (wincap.has_recycle_dot_bin ()) |
451 | { | |
452 | status = NtWriteFile (tmp_fh, NULL, NULL, NULL, &io, | |
453 | desktop_ini_ext, | |
454 | sizeof desktop_ini_ext - 1, NULL, NULL); | |
455 | if (!NT_SUCCESS (status)) | |
456 | debug_printf ("NtWriteFile (%S) failed, status = %p", | |
457 | &fname, status); | |
458 | } | |
8a0f3bbf | 459 | NtClose (tmp_fh); |
61c44b72 CV |
460 | } |
461 | if (!wincap.has_recycle_dot_bin ()) /* No INFO2 file since Vista */ | |
cbfb7b1b | 462 | { |
61c44b72 | 463 | RtlInitUnicodeString (&fname, L"INFO2"); |
8a0f3bbf CV |
464 | status = NtCreateFile (&tmp_fh, FILE_GENERIC_WRITE, &attr, &io, |
465 | NULL, FILE_ATTRIBUTE_ARCHIVE | |
466 | | FILE_ATTRIBUTE_HIDDEN, | |
61c44b72 CV |
467 | FILE_SHARE_VALID_FLAGS, FILE_CREATE, |
468 | FILE_SYNCHRONOUS_IO_NONALERT | |
469 | | FILE_NON_DIRECTORY_FILE, NULL, 0); | |
470 | if (!NT_SUCCESS (status)) | |
aa982024 | 471 | debug_printf ("NtCreateFile (%S) failed, status = %p", |
61c44b72 CV |
472 | &recycler, status); |
473 | else | |
474 | { | |
8a0f3bbf | 475 | status = NtWriteFile (tmp_fh, NULL, NULL, NULL, &io, info2, |
61c44b72 CV |
476 | sizeof info2, NULL, NULL); |
477 | if (!NT_SUCCESS (status)) | |
aa982024 | 478 | debug_printf ("NtWriteFile (%S) failed, status = %p", |
61c44b72 | 479 | &fname, status); |
8a0f3bbf | 480 | NtClose (tmp_fh); |
61c44b72 | 481 | } |
cbfb7b1b | 482 | } |
cbfb7b1b | 483 | } |
61c44b72 CV |
484 | NtClose (recyclerdir); |
485 | /* Shoot again. */ | |
a654829a | 486 | status = NtSetInformationFile (fh, &io, pfri, frisiz, |
61c44b72 | 487 | FileRenameInformation); |
cbfb7b1b | 488 | } |
cbfb7b1b | 489 | if (!NT_SUCCESS (status)) |
8a0f3bbf CV |
490 | { |
491 | debug_printf ("Move %S to %S failed, status = %p", | |
492 | pc.get_nt_native_path (), &recycler, status); | |
493 | goto out; | |
494 | } | |
495 | /* Moving to the bin worked. */ | |
496 | bin_stat = has_been_moved; | |
497 | /* Now we try to set the delete disposition. If that worked, we're done. | |
498 | We try this here first, as long as we still have the open handle. | |
499 | Otherwise the below code closes the handle to allow replacing the file. */ | |
500 | status = NtSetInformationFile (fh, &io, &disp, sizeof disp, | |
501 | FileDispositionInformation); | |
a654829a CV |
502 | if (status == STATUS_DIRECTORY_NOT_EMPTY) |
503 | { | |
504 | /* Uh oh! This was supposed to be avoided by the check_dir_not_empty | |
505 | test in unlink_nt, but given that the test isn't atomic, this *can* | |
506 | happen. Try to move the dir back ASAP. */ | |
507 | pfri->RootDirectory = NULL; | |
508 | pfri->FileNameLength = pc.get_nt_native_path ()->Length; | |
509 | memcpy (pfri->FileName, pc.get_nt_native_path ()->Buffer, | |
510 | pc.get_nt_native_path ()->Length); | |
511 | frisiz = sizeof *pfri + pfri->FileNameLength - sizeof (WCHAR); | |
512 | if (NT_SUCCESS (NtSetInformationFile (fh, &io, pfri, frisiz, | |
513 | FileRenameInformation))) | |
514 | { | |
515 | /* Give notice to unlink_nt and leave immediately. This avoids | |
516 | closing the handle, which might still be used if called from | |
517 | the rm -r workaround code. */ | |
518 | bin_stat = dir_not_empty; | |
519 | goto out; | |
520 | } | |
521 | } | |
8a0f3bbf CV |
522 | /* In case of success, restore R/O attribute to accommodate hardlinks. |
523 | That leaves potentially hardlinks around with the R/O bit suddenly | |
524 | off if setting the delete disposition failed, but please, keep in | |
525 | mind this is really a border case only. */ | |
526 | if ((access & FILE_WRITE_ATTRIBUTES) && NT_SUCCESS (status) && !pc.isdir ()) | |
527 | NtSetAttributesFile (fh, pc.file_attributes ()); | |
528 | NtClose (fh); | |
529 | fh = NULL; /* So unlink_nt doesn't close the handle twice. */ | |
530 | /* On success or when trying to unlink a directory we just return here. | |
531 | The below code only works for files. */ | |
532 | if (NT_SUCCESS (status) || pc.isdir ()) | |
533 | goto out; | |
534 | /* The final trick. We create a temporary file with delete-on-close | |
535 | semantic and rename that file to the file just moved to the bin. | |
536 | This typically overwrites the original file and we get rid of it, | |
537 | even if neither setting the delete dispostion, nor setting | |
538 | delete-on-close on the original file succeeds. There are still | |
539 | cases in which this fails, for instance, when trying to delete a | |
540 | hardlink to a DLL used by the unlinking application itself. */ | |
541 | RtlAppendUnicodeToString (&recycler, L"X"); | |
542 | InitializeObjectAttributes (&attr, &recycler, 0, rootdir, NULL); | |
543 | status = NtCreateFile (&tmp_fh, DELETE, &attr, &io, NULL, | |
544 | FILE_ATTRIBUTE_NORMAL, 0, FILE_SUPERSEDE, | |
545 | FILE_NON_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE, | |
546 | NULL, 0); | |
547 | if (!NT_SUCCESS (status)) | |
548 | { | |
549 | debug_printf ("Creating file for overwriting failed, status = %p", | |
550 | status); | |
551 | goto out; | |
552 | } | |
a654829a | 553 | status = NtSetInformationFile (tmp_fh, &io, pfri, frisiz, |
8a0f3bbf CV |
554 | FileRenameInformation); |
555 | NtClose (tmp_fh); | |
556 | if (!NT_SUCCESS (status)) | |
aa982024 | 557 | debug_printf ("Overwriting with another file failed, status = %p", status); |
8a0f3bbf | 558 | |
61c44b72 CV |
559 | out: |
560 | if (rootdir) | |
561 | NtClose (rootdir); | |
aa982024 | 562 | debug_printf ("%S, return status %d", pc.get_nt_native_path (), bin_stat); |
8a0f3bbf | 563 | return bin_stat; |
398e28fb CV |
564 | } |
565 | ||
8884a168 | 566 | static NTSTATUS |
aa982024 | 567 | check_dir_not_empty (HANDLE dir, path_conv &pc) |
8884a168 CV |
568 | { |
569 | IO_STATUS_BLOCK io; | |
570 | const ULONG bufsiz = 3 * sizeof (FILE_NAMES_INFORMATION) | |
571 | + 3 * NAME_MAX * sizeof (WCHAR); | |
572 | PFILE_NAMES_INFORMATION pfni = (PFILE_NAMES_INFORMATION) | |
573 | alloca (bufsiz); | |
574 | NTSTATUS status = NtQueryDirectoryFile (dir, NULL, NULL, 0, &io, pfni, | |
575 | bufsiz, FileNamesInformation, | |
576 | FALSE, NULL, TRUE); | |
577 | if (!NT_SUCCESS (status)) | |
578 | { | |
aa982024 CV |
579 | debug_printf ("Checking if directory %S is empty failed, status = %p", |
580 | pc.get_nt_native_path (), status); | |
8884a168 CV |
581 | return status; |
582 | } | |
583 | int cnt = 1; | |
28fa2a72 | 584 | do |
8884a168 | 585 | { |
28fa2a72 | 586 | while (pfni->NextEntryOffset) |
e52961c4 | 587 | { |
28fa2a72 | 588 | if (++cnt > 2) |
aa982024 CV |
589 | { |
590 | UNICODE_STRING fname; | |
28fa2a72 CV |
591 | OBJECT_ATTRIBUTES attr; |
592 | FILE_BASIC_INFORMATION fbi; | |
aa982024 CV |
593 | |
594 | pfni = (PFILE_NAMES_INFORMATION) | |
595 | ((caddr_t) pfni + pfni->NextEntryOffset); | |
596 | RtlInitCountedUnicodeString(&fname, pfni->FileName, | |
597 | pfni->FileNameLength); | |
28fa2a72 CV |
598 | InitializeObjectAttributes (&attr, &fname, 0, dir, NULL); |
599 | status = NtQueryAttributesFile (&attr, &fbi); | |
600 | /* Intensive testing shows that sometimes directories, for which | |
601 | the delete disposition has already been set, and the deleting | |
602 | handle is already closed, can linger in the parent dir for a | |
603 | couple of ms for no apparent reason (Windows Defender or other | |
604 | real-time scanners are suspect). | |
605 | ||
606 | A fast rm -r is capable to exploit this problem. Setting the | |
607 | delete disposition of the parent dir then fails with | |
608 | STATUS_DIRECTORY_NOT_EMPTY. Examining the content of the | |
609 | affected dir can then show either that the dir is empty, or it | |
610 | can contain a lingering subdir. Calling NtQueryAttributesFile | |
611 | on that subdir returns with STATUS_DELETE_PENDING, or it | |
612 | disappeared before that call. | |
613 | ||
614 | That's what we do here. If NtQueryAttributesFile succeeded, | |
615 | or if the error code does not indicate an already deleted | |
616 | entry, STATUS_DIRECTORY_NOT_EMPTY is returned. | |
617 | ||
618 | Otherwise STATUS_SUCCESS is returned. Read on in unlink_nt. */ | |
619 | if (status != STATUS_DELETE_PENDING | |
620 | && status != STATUS_OBJECT_NAME_NOT_FOUND | |
621 | && status != STATUS_OBJECT_PATH_NOT_FOUND) | |
622 | { | |
623 | debug_printf ("Directory %S not empty, found file <%S>, " | |
624 | "query status = %p", | |
625 | pc.get_nt_native_path (), &fname, status); | |
626 | return STATUS_DIRECTORY_NOT_EMPTY; | |
627 | } | |
aa982024 | 628 | } |
28fa2a72 | 629 | pfni = (PFILE_NAMES_INFORMATION) ((caddr_t) pfni + pfni->NextEntryOffset); |
e52961c4 | 630 | } |
8884a168 | 631 | } |
28fa2a72 CV |
632 | while (NT_SUCCESS (NtQueryDirectoryFile (dir, NULL, NULL, 0, &io, pfni, |
633 | bufsiz, FileNamesInformation, | |
634 | FALSE, NULL, FALSE))); | |
8884a168 CV |
635 | return STATUS_SUCCESS; |
636 | } | |
637 | ||
ed7ec849 CV |
638 | NTSTATUS |
639 | unlink_nt (path_conv &pc) | |
1c9b2968 | 640 | { |
ed7ec849 | 641 | NTSTATUS status; |
d6f45fb0 | 642 | HANDLE fh, fh_ro = NULL; |
1c9b2968 CV |
643 | OBJECT_ATTRIBUTES attr; |
644 | IO_STATUS_BLOCK io; | |
18df3935 CV |
645 | HANDLE old_trans = NULL, trans = NULL; |
646 | ULONG num_links = 1; | |
647 | FILE_DISPOSITION_INFORMATION disp = { TRUE }; | |
28fa2a72 | 648 | int reopened = 0; |
ed7ec849 | 649 | |
d6f45fb0 | 650 | bin_status bin_stat = dont_move; |
1c9b2968 | 651 | |
aa982024 CV |
652 | syscall_printf ("Trying to delete %S, isdir = %d", |
653 | pc.get_nt_native_path (), pc.isdir ()); | |
d6f45fb0 | 654 | ACCESS_MASK access = DELETE; |
1c9b2968 | 655 | ULONG flags = FILE_OPEN_FOR_BACKUP_INTENT; |
e563a4d5 CV |
656 | /* Add the reparse point flag to native symlinks, otherwise we remove the |
657 | target, not the symlink. */ | |
ed7ec849 | 658 | if (pc.is_rep_symlink ()) |
e563a4d5 | 659 | flags |= FILE_OPEN_REPARSE_POINT; |
1c9b2968 | 660 | |
ed7ec849 | 661 | pc.get_object_attr (attr, sec_none_nih); |
d6f45fb0 CV |
662 | /* If the R/O attribute is set, we have to open the file with |
663 | FILE_WRITE_ATTRIBUTES to be able to remove this flags before trying | |
664 | to delete it. We do this separately because there are filesystems | |
665 | out there (MVFS), which refuse a request to open a file for DELETE | |
666 | if the DOS R/O attribute is set for the file. After removing the R/O | |
667 | attribute, just re-open the file for DELETE and go ahead. */ | |
668 | if (pc.file_attributes () & FILE_ATTRIBUTE_READONLY) | |
669 | { | |
18df3935 CV |
670 | FILE_STANDARD_INFORMATION fsi; |
671 | ||
672 | /* If possible, hide the non-atomicity of the "remove R/O flag, remove | |
b86f999a | 673 | link to file" operation behind a transaction. */ |
18df3935 CV |
674 | if (wincap.has_transactions () |
675 | && (pc.fs_flags () & FILE_SUPPORTS_TRANSACTIONS)) | |
676 | start_transaction (old_trans, trans); | |
677 | ||
d6f45fb0 CV |
678 | status = NtOpenFile (&fh_ro, FILE_WRITE_ATTRIBUTES, &attr, &io, |
679 | FILE_SHARE_VALID_FLAGS, flags); | |
680 | if (NT_SUCCESS (status)) | |
681 | { | |
aa982024 CV |
682 | debug_printf ("Opening %S for removing R/O succeeded", |
683 | pc.get_nt_native_path ()); | |
684 | NTSTATUS status2 = NtSetAttributesFile (fh_ro, | |
685 | pc.file_attributes () | |
686 | & ~FILE_ATTRIBUTE_READONLY); | |
687 | if (!NT_SUCCESS (status2)) | |
688 | debug_printf ("Removing R/O on %S failed, status = %p", | |
689 | pc.get_nt_native_path (), status2); | |
4c9d01fd | 690 | pc.init_reopen_attr (&attr, fh_ro); |
b86f999a | 691 | } |
aa982024 CV |
692 | else |
693 | debug_printf ("Opening %S for removing R/O failed, status = %p", | |
694 | pc.get_nt_native_path (), status); | |
18df3935 CV |
695 | if (pc.is_lnk_symlink ()) |
696 | { | |
697 | status = NtQueryInformationFile (fh_ro, &io, &fsi, sizeof fsi, | |
698 | FileStandardInformation); | |
699 | if (NT_SUCCESS (status)) | |
700 | num_links = fsi.NumberOfLinks; | |
701 | } | |
702 | access |= FILE_WRITE_ATTRIBUTES; | |
d6f45fb0 | 703 | } |
f4ad0404 CV |
704 | /* First try to open the file with only allowing sharing for delete. If |
705 | the file has an open handle on it, other than just for deletion, this | |
706 | will fail. That indicates that the file has to be moved to the recycle | |
707 | bin so that it actually disappears from its directory even though its | |
708 | in use. Otherwise, if opening doesn't fail, the file is not in use and | |
28fa2a72 | 709 | we can go straight to setting the delete disposition flag. |
1b23b30b | 710 | |
28fa2a72 CV |
711 | NOTE: The missing sharing modes FILE_SHARE_READ and FILE_SHARE_WRITE do |
712 | NOT result in a STATUS_SHARING_VIOLATION, if another handle is | |
713 | opened for reading/writing metadata only. In other words, if | |
714 | another handle is open, but does not have the file open with | |
715 | FILE_READ_DATA or FILE_WRITE_DATA, the following NtOpenFile call | |
716 | will succeed. So, apparently there is no reliable way to find out | |
717 | if a file is already open elsewhere for other purposes than | |
718 | reading and writing data. */ | |
f4ad0404 | 719 | status = NtOpenFile (&fh, access, &attr, &io, FILE_SHARE_DELETE, flags); |
a654829a CV |
720 | /* STATUS_SHARING_VIOLATION is what we expect. STATUS_LOCK_NOT_GRANTED can |
721 | be generated under not quite clear circumstances when trying to open a | |
722 | file on NFS with FILE_SHARE_DELETE only. This has been observed with | |
723 | SFU 3.5 if the NFS share has been mounted under a drive letter. It's | |
724 | not generated for all files, but only for some. If it's generated once | |
725 | for a file, it will be generated all the time. It looks as if wrong file | |
726 | state information is stored within the NFS client which never times out. | |
727 | Opening the file with FILE_SHARE_VALID_FLAGS will work, though, and it | |
728 | is then possible to delete the file quite normally. */ | |
03897d8d | 729 | if (status == STATUS_SHARING_VIOLATION || status == STATUS_LOCK_NOT_GRANTED) |
a0638e5e | 730 | { |
a654829a CV |
731 | debug_printf ("Sharing violation when opening %S", |
732 | pc.get_nt_native_path ()); | |
733 | /* We never call try_to_bin on NFS and NetApp for the follwing reasons: | |
46f5dd59 CF |
734 | |
735 | NFS implements its own mechanism to remove in-use files, which looks | |
a654829a | 736 | quite similar to what we do in try_to_bin for remote files. |
92ddb742 | 737 | |
975a85f4 CV |
738 | Netapp filesystems don't understand the "move and delete" method |
739 | at all and have all kinds of weird effects. Just setting the delete | |
740 | dispositon usually works fine, though. */ | |
741 | if (!pc.fs_is_nfs () && !pc.fs_is_netapp ()) | |
8a0f3bbf | 742 | bin_stat = move_to_bin; |
a654829a CV |
743 | /* If the file is not a directory, of if we didn't set the move_to_bin |
744 | flag, just proceed with the FILE_SHARE_VALID_FLAGS set. */ | |
745 | if (!pc.isdir () || bin_stat == dont_move) | |
ed7ec849 CV |
746 | status = NtOpenFile (&fh, access, &attr, &io, |
747 | FILE_SHARE_VALID_FLAGS, flags); | |
788e7da1 CV |
748 | else |
749 | { | |
a654829a CV |
750 | /* Otherwise it's getting tricky. The directory is opened in some |
751 | process, so we're supposed to move it to the recycler and mark it | |
752 | for deletion. But what if the directory is not empty? The move | |
788e7da1 | 753 | will work, but the subsequent delete will fail. So we would |
a654829a CV |
754 | have to move it back. While we do that in try_to_bin, it's bad, |
755 | because the move results in a temporary inconsistent state. | |
756 | So, we test first if the directory is empty. If not, we bail | |
757 | out with STATUS_DIRECTORY_NOT_EMPTY. This avoids most of the | |
758 | problems. */ | |
ed7ec849 | 759 | status = NtOpenFile (&fh, access | FILE_LIST_DIRECTORY | SYNCHRONIZE, |
788e7da1 CV |
760 | &attr, &io, FILE_SHARE_VALID_FLAGS, |
761 | flags | FILE_SYNCHRONOUS_IO_NONALERT); | |
762 | if (NT_SUCCESS (status)) | |
763 | { | |
aa982024 | 764 | status = check_dir_not_empty (fh, pc); |
788e7da1 | 765 | if (!NT_SUCCESS (status)) |
70300fdb | 766 | { |
ed7ec849 | 767 | NtClose (fh); |
d6f45fb0 CV |
768 | if (fh_ro) |
769 | NtClose (fh_ro); | |
18df3935 | 770 | goto out; |
788e7da1 | 771 | } |
788e7da1 CV |
772 | } |
773 | } | |
a0638e5e | 774 | } |
d6f45fb0 CV |
775 | if (fh_ro) |
776 | NtClose (fh_ro); | |
1c9b2968 CV |
777 | if (!NT_SUCCESS (status)) |
778 | { | |
779 | if (status == STATUS_DELETE_PENDING) | |
780 | { | |
aa982024 | 781 | debug_printf ("Delete %S already pending", pc.get_nt_native_path ()); |
28fa2a72 | 782 | status = STATUS_SUCCESS; |
18df3935 | 783 | goto out; |
1c9b2968 | 784 | } |
aa982024 CV |
785 | debug_printf ("Opening %S for delete failed, status = %p", |
786 | pc.get_nt_native_path (), status); | |
18df3935 | 787 | goto out; |
1c9b2968 | 788 | } |
8a0f3bbf CV |
789 | /* Try to move to bin if a sharing violation occured. If that worked, |
790 | we're done. */ | |
791 | if (bin_stat == move_to_bin | |
a654829a | 792 | && (bin_stat = try_to_bin (pc, fh, access)) >= has_been_moved) |
18df3935 | 793 | { |
a654829a CV |
794 | if (bin_stat == has_been_moved) |
795 | status = STATUS_SUCCESS; | |
796 | else | |
797 | { | |
798 | status = STATUS_DIRECTORY_NOT_EMPTY; | |
799 | NtClose (fh); | |
800 | } | |
18df3935 CV |
801 | goto out; |
802 | } | |
28fa2a72 CV |
803 | |
804 | try_again: | |
8a0f3bbf | 805 | /* Try to set delete disposition. */ |
ed7ec849 CV |
806 | status = NtSetInformationFile (fh, &io, &disp, sizeof disp, |
807 | FileDispositionInformation); | |
1c9b2968 CV |
808 | if (!NT_SUCCESS (status)) |
809 | { | |
aa982024 CV |
810 | debug_printf ("Setting delete disposition on %S failed, status = %p", |
811 | pc.get_nt_native_path (), status); | |
28fa2a72 | 812 | if (status == STATUS_DIRECTORY_NOT_EMPTY) |
aa982024 | 813 | { |
28fa2a72 CV |
814 | NTSTATUS status2 = STATUS_SUCCESS; |
815 | ||
816 | if (!reopened) | |
817 | { | |
818 | /* Have to close and reopen the file from scratch, otherwise | |
819 | we collide with the delete-only sharing mode. */ | |
820 | pc.get_object_attr (attr, sec_none_nih); | |
821 | NtClose (fh); | |
822 | status2 = NtOpenFile (&fh, access | FILE_LIST_DIRECTORY | |
823 | | SYNCHRONIZE, | |
824 | &attr, &io, FILE_SHARE_VALID_FLAGS, | |
825 | flags | FILE_SYNCHRONOUS_IO_NONALERT); | |
826 | } | |
827 | if (NT_SUCCESS (status2) && reopened < 20) | |
828 | { | |
829 | /* Workaround rm -r problem: | |
1b23b30b | 830 | |
28fa2a72 CV |
831 | Sometimes a deleted directory lingers in its parent dir |
832 | after the deleting handle has already been closed. This | |
833 | can break deleting the parent dir. See the comment in | |
834 | check_dir_not_empty for more information. | |
835 | ||
836 | What we do here is this: If check_dir_not_empty returns | |
837 | STATUS_SUCCESS, the dir is either empty, or only inhabited | |
838 | by already deleted entries. If so, we try to move the dir | |
839 | into the bin. This usually works. | |
1b23b30b | 840 | |
28fa2a72 CV |
841 | However, if we're on a filesystem which doesn't support |
842 | the try_to_bin method, or if moving to the bin doesn't work | |
843 | for some reason, just try to delete the directory again, | |
844 | with a very short grace period to free the CPU for a while. | |
845 | This gives the OS time to clean up. 5ms is enough in my | |
846 | testing to make sure that we don't have to try more than | |
847 | once in practically all cases. | |
848 | While this is an extrem bordercase, we don't want to hang | |
849 | infinitely in case a file in the directory is in the "delete | |
850 | pending" state but an application holds an open handle to it | |
851 | for a longer time. So we don't try this more than 20 times, | |
852 | which means a process time of 100-120ms. */ | |
853 | if (check_dir_not_empty (fh, pc) == STATUS_SUCCESS) | |
854 | { | |
855 | if (bin_stat == dont_move) | |
856 | { | |
857 | bin_stat = move_to_bin; | |
858 | if (!pc.fs_is_nfs () && !pc.fs_is_netapp ()) | |
859 | { | |
860 | debug_printf ("Try-to-bin %S", | |
861 | pc.get_nt_native_path ()); | |
862 | bin_stat = try_to_bin (pc, fh, access); | |
863 | } | |
864 | } | |
a654829a | 865 | /* Do NOT handle bin_stat == dir_not_empty here! */ |
28fa2a72 CV |
866 | if (bin_stat == has_been_moved) |
867 | status = STATUS_SUCCESS; | |
868 | else | |
869 | { | |
870 | debug_printf ("Try %S again", pc.get_nt_native_path ()); | |
871 | ++reopened; | |
872 | Sleep (5L); | |
873 | goto try_again; | |
874 | } | |
875 | } | |
876 | } | |
a654829a CV |
877 | else if (status2 != STATUS_OBJECT_PATH_NOT_FOUND |
878 | && status2 != STATUS_OBJECT_NAME_NOT_FOUND) | |
aa982024 CV |
879 | { |
880 | fh = NULL; | |
881 | debug_printf ("Opening dir %S for check_dir_not_empty failed, " | |
53e93974 | 882 | "status = %p", pc.get_nt_native_path (), status2); |
aa982024 | 883 | } |
a654829a CV |
884 | else /* Directory disappeared between NtClose and NtOpenFile. */ |
885 | status = STATUS_SUCCESS; | |
aa982024 | 886 | } |
f4ad0404 CV |
887 | /* Trying to delete a hardlink to a file in use by the system in some |
888 | way (for instance, font files) by setting the delete disposition fails | |
889 | with STATUS_CANNOT_DELETE. Strange enough, deleting these hardlinks | |
8a0f3bbf | 890 | using delete-on-close semantic works... most of the time. |
b4fa8164 | 891 | |
f4ad0404 CV |
892 | Don't use delete-on-close on remote shares. If two processes |
893 | have open handles on a file and one of them calls unlink, the | |
894 | file is removed from the remote share even though the other | |
895 | process still has an open handle. That process than gets Win32 | |
896 | error 59, ERROR_UNEXP_NET_ERR when trying to access the file. | |
897 | Microsoft KB 837665 describes this problem as a bug in 2K3, but | |
898 | I have reproduced it on other systems. */ | |
28fa2a72 CV |
899 | else if (status == STATUS_CANNOT_DELETE |
900 | && (!pc.isremote () || pc.fs_is_ncfsd ())) | |
b4fa8164 | 901 | { |
f4ad0404 | 902 | HANDLE fh2; |
f4ad0404 | 903 | |
aa982024 CV |
904 | debug_printf ("Cannot delete %S, try delete-on-close", |
905 | pc.get_nt_native_path ()); | |
f4ad0404 CV |
906 | /* Re-open from handle so we open the correct file no matter if it |
907 | has been moved to the bin or not. */ | |
4c9d01fd | 908 | pc.init_reopen_attr (&attr, fh); |
f4ad0404 | 909 | status = NtOpenFile (&fh2, DELETE, &attr, &io, |
8a0f3bbf CV |
910 | bin_stat == move_to_bin ? FILE_SHARE_VALID_FLAGS |
911 | : FILE_SHARE_DELETE, | |
f4ad0404 CV |
912 | flags | FILE_DELETE_ON_CLOSE); |
913 | if (!NT_SUCCESS (status)) | |
8a0f3bbf | 914 | { |
aa982024 CV |
915 | debug_printf ("Setting delete-on-close on %S failed, status = %p", |
916 | pc.get_nt_native_path (), status); | |
8a0f3bbf | 917 | /* This is really the last chance. If it hasn't been moved |
b4fa8164 | 918 | to the bin already, try it now. If moving to the bin |
8a0f3bbf CV |
919 | succeeds, we got rid of the file in some way, even if |
920 | unlinking didn't work. */ | |
921 | if (bin_stat == dont_move) | |
b4fa8164 | 922 | bin_stat = try_to_bin (pc, fh, access); |
a654829a CV |
923 | if (bin_stat >= has_been_moved) |
924 | status = bin_stat == has_been_moved | |
925 | ? STATUS_SUCCESS | |
926 | : STATUS_DIRECTORY_NOT_EMPTY; | |
8a0f3bbf | 927 | } |
f4ad0404 CV |
928 | else |
929 | NtClose (fh2); | |
930 | } | |
1c9b2968 | 931 | } |
8a0f3bbf | 932 | if (fh) |
9d017bd0 | 933 | { |
18df3935 | 934 | if (access & FILE_WRITE_ATTRIBUTES) |
b86f999a | 935 | { |
a654829a | 936 | /* Restore R/O attribute if setting the delete disposition failed. */ |
18df3935 CV |
937 | if (!NT_SUCCESS (status)) |
938 | NtSetAttributesFile (fh, pc.file_attributes ()); | |
939 | /* If we succeeded, restore R/O attribute to accommodate hardlinks. | |
940 | Only ever try to do this for our own winsymlinks, because there's | |
941 | a problem with setting the delete disposition: | |
942 | http://msdn.microsoft.com/en-us/library/ff545765%28VS.85%29.aspx | |
943 | "Subsequently, the only legal operation by such a caller is | |
944 | to close the open file handle." | |
92ddb742 | 945 | |
18df3935 CV |
946 | FIXME? On Vista and later, we could use FILE_HARD_LINK_INFORMATION |
947 | to find all hardlinks and use one of them to restore the R/O bit, | |
948 | after the NtClose, but before we stop the transaction. This | |
949 | avoids the aforementioned problem entirely . */ | |
950 | else if (pc.is_lnk_symlink () && num_links > 1) | |
951 | NtSetAttributesFile (fh, pc.file_attributes ()); | |
952 | } | |
953 | ||
8a0f3bbf | 954 | NtClose (fh); |
18df3935 | 955 | |
9d017bd0 | 956 | } |
18df3935 CV |
957 | out: |
958 | /* Stop transaction if we started one. */ | |
959 | if ((access & FILE_WRITE_ATTRIBUTES) | |
960 | && wincap.has_transactions () | |
961 | && (pc.fs_flags () & FILE_SUPPORTS_TRANSACTIONS)) | |
962 | stop_transaction (status, old_trans, trans); | |
963 | ||
aa982024 | 964 | syscall_printf ("%S, return status = %p", pc.get_nt_native_path (), status); |
ed7ec849 | 965 | return status; |
1c9b2968 CV |
966 | } |
967 | ||
90fe7739 | 968 | extern "C" int |
5ec14fe4 | 969 | unlink (const char *ourname) |
1fd5e000 CF |
970 | { |
971 | int res = -1; | |
ea4e6ec8 | 972 | DWORD devn; |
ed7ec849 | 973 | NTSTATUS status; |
1fd5e000 | 974 | |
9bf7c7e9 | 975 | path_conv win32_name (ourname, PC_SYM_NOFOLLOW, stat_suffixes); |
1fd5e000 CF |
976 | |
977 | if (win32_name.error) | |
978 | { | |
979 | set_errno (win32_name.error); | |
980 | goto done; | |
981 | } | |
982 | ||
96d7dee2 CV |
983 | devn = win32_name.get_devn (); |
984 | if (isproc_dev (devn)) | |
ea4e6ec8 CF |
985 | { |
986 | set_errno (EROFS); | |
987 | goto done; | |
988 | } | |
989 | ||
47063f00 | 990 | if (!win32_name.exists ()) |
5564cd7a | 991 | { |
aa982024 | 992 | debug_printf ("unlinking a nonexistent file"); |
5564cd7a CV |
993 | set_errno (ENOENT); |
994 | goto done; | |
995 | } | |
47063f00 | 996 | else if (win32_name.isdir ()) |
1fd5e000 | 997 | { |
aa982024 | 998 | debug_printf ("unlinking a directory"); |
1fd5e000 CF |
999 | set_errno (EPERM); |
1000 | goto done; | |
1001 | } | |
1002 | ||
ed7ec849 CV |
1003 | status = unlink_nt (win32_name); |
1004 | if (NT_SUCCESS (status)) | |
1c9b2968 CV |
1005 | res = 0; |
1006 | else | |
176c3f21 | 1007 | __seterrno_from_nt_status (status); |
8e9b0aee | 1008 | |
20a2c443 | 1009 | done: |
b9aa8149 | 1010 | syscall_printf ("%R = unlink(%s)", res, ourname); |
1fd5e000 CF |
1011 | return res; |
1012 | } | |
1013 | ||
be1ea8cd CF |
1014 | extern "C" int |
1015 | _remove_r (struct _reent *, const char *ourname) | |
1016 | { | |
063f1df2 | 1017 | path_conv win32_name (ourname, PC_SYM_NOFOLLOW); |
be1ea8cd CF |
1018 | |
1019 | if (win32_name.error) | |
1020 | { | |
1021 | set_errno (win32_name.error); | |
b9aa8149 | 1022 | syscall_printf ("%R = remove(%s)",-1, ourname); |
be1ea8cd CF |
1023 | return -1; |
1024 | } | |
1025 | ||
1026 | return win32_name.isdir () ? rmdir (ourname) : unlink (ourname); | |
1027 | } | |
1028 | ||
3112d7a4 CV |
1029 | extern "C" int |
1030 | remove (const char *ourname) | |
1031 | { | |
063f1df2 | 1032 | path_conv win32_name (ourname, PC_SYM_NOFOLLOW); |
3112d7a4 CV |
1033 | |
1034 | if (win32_name.error) | |
1035 | { | |
1036 | set_errno (win32_name.error); | |
1037 | syscall_printf ("-1 = remove (%s)", ourname); | |
1038 | return -1; | |
1039 | } | |
1040 | ||
ca35d41c CF |
1041 | int res = win32_name.isdir () ? rmdir (ourname) : unlink (ourname); |
1042 | syscall_printf ("%R = remove(%s)", res, ourname); | |
1043 | return res; | |
3112d7a4 CV |
1044 | } |
1045 | ||
90fe7739 | 1046 | extern "C" pid_t |
d25c187f | 1047 | getpid () |
1fd5e000 | 1048 | { |
ca35d41c | 1049 | syscall_printf ("%d = getpid()", myself->pid); |
1fd5e000 CF |
1050 | return myself->pid; |
1051 | } | |
1052 | ||
d25c187f CF |
1053 | extern "C" pid_t |
1054 | _getpid_r (struct _reent *) | |
1055 | { | |
1056 | return getpid (); | |
1057 | } | |
1058 | ||
1fd5e000 | 1059 | /* getppid: POSIX 4.1.1.1 */ |
90fe7739 | 1060 | extern "C" pid_t |
1fd5e000 CF |
1061 | getppid () |
1062 | { | |
ca35d41c | 1063 | syscall_printf ("%d = getppid()", myself->ppid); |
1fd5e000 CF |
1064 | return myself->ppid; |
1065 | } | |
1066 | ||
1067 | /* setsid: POSIX 4.3.2.1 */ | |
90fe7739 | 1068 | extern "C" pid_t |
1fd5e000 CF |
1069 | setsid (void) |
1070 | { | |
f7239090 | 1071 | #ifdef NEWVFORK |
8dca9e23 CF |
1072 | vfork_save *vf = vfork_storage.val (); |
1073 | /* This is a horrible, horrible kludge */ | |
1074 | if (vf && vf->pid < 0) | |
1fd5e000 | 1075 | { |
8dca9e23 CF |
1076 | pid_t pid = fork (); |
1077 | if (pid > 0) | |
1078 | { | |
1079 | syscall_printf ("longjmping due to vfork"); | |
1080 | vf->restore_pid (pid); | |
1081 | } | |
1082 | /* assuming that fork was successful */ | |
1083 | } | |
f7239090 | 1084 | #endif |
8dca9e23 | 1085 | |
29d52c8a CF |
1086 | if (myself->pgid == myself->pid) |
1087 | syscall_printf ("hmm. pgid %d pid %d", myself->pgid, myself->pid); | |
1088 | else | |
8dca9e23 | 1089 | { |
c38a2d83 | 1090 | myself->ctty = -2; |
4eb5175d CF |
1091 | myself->sid = myself->pid; |
1092 | myself->pgid = myself->pid; | |
8e10c431 | 1093 | if (cygheap->ctty) |
2d1d1eb1 | 1094 | cygheap->close_ctty (); |
5a0826c3 | 1095 | syscall_printf ("sid %d, pgid %d, %s", myself->sid, myself->pgid, myctty ()); |
1fd5e000 CF |
1096 | return myself->sid; |
1097 | } | |
8dca9e23 | 1098 | |
1fd5e000 CF |
1099 | set_errno (EPERM); |
1100 | return -1; | |
1101 | } | |
1102 | ||
8dca9e23 CF |
1103 | extern "C" pid_t |
1104 | getsid (pid_t pid) | |
1105 | { | |
1106 | pid_t res; | |
1107 | if (!pid) | |
1108 | res = myself->sid; | |
1109 | else | |
1110 | { | |
1111 | pinfo p (pid); | |
1112 | if (p) | |
1113 | res = p->sid; | |
1114 | else | |
1115 | { | |
1116 | set_errno (ESRCH); | |
1117 | res = -1; | |
1118 | } | |
1119 | } | |
ca35d41c | 1120 | syscall_printf ("%R = getsid(%d)", pid); |
8dca9e23 CF |
1121 | return res; |
1122 | } | |
1123 | ||
6ebccdc1 | 1124 | extern "C" ssize_t |
5ec14fe4 | 1125 | read (int fd, void *ptr, size_t len) |
1fd5e000 | 1126 | { |
a4e5706e CV |
1127 | pthread_testcancel (); |
1128 | ||
1129 | myfault efault; | |
1130 | if (efault.faulted (EFAULT)) | |
1131 | return -1; | |
1132 | ||
1133 | size_t res = (size_t) -1; | |
1134 | ||
1135 | cygheap_fdget cfd (fd); | |
1136 | if (cfd < 0) | |
1137 | goto done; | |
1138 | ||
1139 | if ((cfd->get_flags () & O_ACCMODE) == O_WRONLY) | |
ab7f9b93 | 1140 | { |
a4e5706e CV |
1141 | set_errno (EBADF); |
1142 | goto done; | |
1143 | } | |
ab7f9b93 | 1144 | |
a4e5706e | 1145 | /* Could block, so let user know we at least got here. */ |
ca35d41c | 1146 | syscall_printf ("read(%d, %p, %d) %sblocking", |
69864e48 | 1147 | fd, ptr, len, cfd->is_nonblocking () ? "non" : ""); |
a4e5706e CV |
1148 | |
1149 | cfd->read (ptr, res = len); | |
1150 | ||
1151 | done: | |
b9aa8149 | 1152 | syscall_printf ("%R = read(%d, %p, %d)", res, fd, ptr, len); |
a4e5706e CV |
1153 | MALLOC_CHECK; |
1154 | return (ssize_t) res; | |
ab7f9b93 | 1155 | } |
a0626ebe | 1156 | |
12374d7d CV |
1157 | EXPORT_ALIAS (read, _read) |
1158 | ||
7d7d09ae | 1159 | extern "C" ssize_t |
a4e5706e | 1160 | readv (int fd, const struct iovec *const iov, const int iovcnt) |
7d7d09ae | 1161 | { |
1804be04 CV |
1162 | pthread_testcancel (); |
1163 | ||
a4e5706e CV |
1164 | myfault efault; |
1165 | if (efault.faulted (EFAULT)) | |
1166 | return -1; | |
1167 | ||
1168 | ssize_t res = -1; | |
1169 | const ssize_t tot = check_iovec_for_read (iov, iovcnt); | |
1170 | ||
7d7d09ae CF |
1171 | cygheap_fdget cfd (fd); |
1172 | if (cfd < 0) | |
a4e5706e | 1173 | goto done; |
7d7d09ae | 1174 | |
a4e5706e CV |
1175 | if (tot <= 0) |
1176 | { | |
1177 | res = tot; | |
1178 | goto done; | |
1179 | } | |
1180 | ||
1181 | if ((cfd->get_flags () & O_ACCMODE) == O_WRONLY) | |
1182 | { | |
1183 | set_errno (EBADF); | |
1184 | goto done; | |
1185 | } | |
1186 | ||
1187 | /* Could block, so let user know we at least got here. */ | |
ca35d41c | 1188 | syscall_printf ("readv(%d, %p, %d) %sblocking", |
69864e48 | 1189 | fd, iov, iovcnt, cfd->is_nonblocking () ? "non" : ""); |
a4e5706e CV |
1190 | |
1191 | res = cfd->readv (iov, iovcnt, tot); | |
1192 | ||
1193 | done: | |
b9aa8149 | 1194 | syscall_printf ("%R = readv(%d, %p, %d)", res, fd, iov, iovcnt); |
a4e5706e | 1195 | MALLOC_CHECK; |
7d7d09ae CF |
1196 | return res; |
1197 | } | |
1198 | ||
1199 | extern "C" ssize_t | |
a4e5706e | 1200 | pread (int fd, void *ptr, size_t len, _off64_t off) |
7d7d09ae | 1201 | { |
1804be04 CV |
1202 | pthread_testcancel (); |
1203 | ||
7d7d09ae CF |
1204 | ssize_t res; |
1205 | cygheap_fdget cfd (fd); | |
1206 | if (cfd < 0) | |
1207 | res = -1; | |
1208 | else | |
a4e5706e | 1209 | res = cfd->pread (ptr, len, off); |
7d7d09ae | 1210 | |
b9aa8149 | 1211 | syscall_printf ("%R = pread(%d, %p, %d, %d)", res, fd, ptr, len, off); |
7d7d09ae CF |
1212 | return res; |
1213 | } | |
1214 | ||
ab7f9b93 | 1215 | extern "C" ssize_t |
5ec14fe4 | 1216 | write (int fd, const void *ptr, size_t len) |
ab7f9b93 | 1217 | { |
1804be04 CV |
1218 | pthread_testcancel (); |
1219 | ||
23db0a41 CV |
1220 | myfault efault; |
1221 | if (efault.faulted (EFAULT)) | |
1222 | return -1; | |
ab7f9b93 | 1223 | |
a4e5706e | 1224 | int res = -1; |
ab7f9b93 | 1225 | |
23db0a41 CV |
1226 | cygheap_fdget cfd (fd); |
1227 | if (cfd < 0) | |
1228 | goto done; | |
1229 | ||
a4e5706e | 1230 | if ((cfd->get_flags () & O_ACCMODE) == O_RDONLY) |
1fd5e000 | 1231 | { |
23db0a41 CV |
1232 | set_errno (EBADF); |
1233 | goto done; | |
1234 | } | |
f6111483 | 1235 | |
23db0a41 | 1236 | /* Could block, so let user know we at least got here. */ |
a4e5706e | 1237 | if (fd == 1 || fd == 2) |
ca35d41c | 1238 | paranoid_printf ("write(%d, %p, %d)", fd, ptr, len); |
a4e5706e | 1239 | else |
ca35d41c | 1240 | syscall_printf ("write(%d, %p, %d)", fd, ptr, len); |
6ea0c04e | 1241 | |
a4e5706e | 1242 | res = cfd->write (ptr, len); |
1fd5e000 | 1243 | |
ab7f9b93 | 1244 | done: |
ce331834 | 1245 | syscall_printf ("%R = write(%d, %p, %d)", res, fd, ptr, len); |
a4e5706e | 1246 | |
1fd5e000 | 1247 | MALLOC_CHECK; |
fb5956da | 1248 | return res; |
1fd5e000 CF |
1249 | } |
1250 | ||
a4e5706e CV |
1251 | EXPORT_ALIAS (write, _write) |
1252 | ||
6ebccdc1 | 1253 | extern "C" ssize_t |
ab7f9b93 | 1254 | writev (const int fd, const struct iovec *const iov, const int iovcnt) |
1fd5e000 | 1255 | { |
1804be04 CV |
1256 | pthread_testcancel (); |
1257 | ||
23db0a41 CV |
1258 | myfault efault; |
1259 | if (efault.faulted (EFAULT)) | |
1260 | return -1; | |
1261 | ||
1fd5e000 | 1262 | int res = -1; |
ab7f9b93 | 1263 | const ssize_t tot = check_iovec_for_write (iov, iovcnt); |
1fd5e000 | 1264 | |
df63bd49 CF |
1265 | cygheap_fdget cfd (fd); |
1266 | if (cfd < 0) | |
1267 | goto done; | |
1fd5e000 | 1268 | |
ab7f9b93 | 1269 | if (tot <= 0) |
e2b3dc25 | 1270 | { |
ab7f9b93 | 1271 | res = tot; |
e2b3dc25 CF |
1272 | goto done; |
1273 | } | |
1274 | ||
ab7f9b93 CF |
1275 | if ((cfd->get_flags () & O_ACCMODE) == O_RDONLY) |
1276 | { | |
1277 | set_errno (EBADF); | |
1278 | goto done; | |
1279 | } | |
e2b3dc25 | 1280 | |
1fd5e000 CF |
1281 | /* Could block, so let user know we at least got here. */ |
1282 | if (fd == 1 || fd == 2) | |
ca35d41c | 1283 | paranoid_printf ("writev(%d, %p, %d)", fd, iov, iovcnt); |
1fd5e000 | 1284 | else |
ca35d41c | 1285 | syscall_printf ("writev(%d, %p, %d)", fd, iov, iovcnt); |
1fd5e000 | 1286 | |
d8ff9638 | 1287 | res = cfd->writev (iov, iovcnt, tot); |
1fd5e000 CF |
1288 | |
1289 | done: | |
1290 | if (fd == 1 || fd == 2) | |
b9aa8149 | 1291 | paranoid_printf ("%R = writev(%d, %p, %d)", res, fd, iov, iovcnt); |
1fd5e000 | 1292 | else |
b9aa8149 | 1293 | syscall_printf ("%R = writev(%d, %p, %d)", res, fd, iov, iovcnt); |
1fd5e000 | 1294 | |
ab7f9b93 CF |
1295 | MALLOC_CHECK; |
1296 | return res; | |
1fd5e000 CF |
1297 | } |
1298 | ||
a4e5706e CV |
1299 | extern "C" ssize_t |
1300 | pwrite (int fd, void *ptr, size_t len, _off64_t off) | |
1301 | { | |
1302 | pthread_testcancel (); | |
1303 | ||
1304 | ssize_t res; | |
1305 | cygheap_fdget cfd (fd); | |
1306 | if (cfd < 0) | |
1307 | res = -1; | |
1308 | else | |
1309 | res = cfd->pwrite (ptr, len, off); | |
1310 | ||
b9aa8149 | 1311 | syscall_printf ("%R = pwrite(%d, %p, %d, %d)", res, fd, ptr, len, off); |
a4e5706e CV |
1312 | return res; |
1313 | } | |
1314 | ||
1fd5e000 CF |
1315 | /* _open */ |
1316 | /* newlib's fcntl.h defines _open as taking variable args so we must | |
1317 | correspond. The third arg if it exists is: mode_t mode. */ | |
90fe7739 | 1318 | extern "C" int |
5ec14fe4 | 1319 | open (const char *unix_path, int flags, ...) |
1fd5e000 | 1320 | { |
1fd5e000 CF |
1321 | int res = -1; |
1322 | va_list ap; | |
1323 | mode_t mode = 0; | |
1fd5e000 | 1324 | |
ca35d41c | 1325 | syscall_printf ("open(%s, %p)", unix_path, flags); |
1804be04 | 1326 | pthread_testcancel (); |
893ac8e0 CF |
1327 | myfault efault; |
1328 | if (efault.faulted (EFAULT)) | |
1329 | /* errno already set */; | |
1330 | else if (!*unix_path) | |
1331 | set_errno (ENOENT); | |
1332 | else | |
1fd5e000 | 1333 | { |
1fd5e000 CF |
1334 | /* check for optional mode argument */ |
1335 | va_start (ap, flags); | |
1336 | mode = va_arg (ap, mode_t); | |
1337 | va_end (ap); | |
1338 | ||
df63bd49 CF |
1339 | fhandler_base *fh; |
1340 | cygheap_fdnew fd; | |
1fd5e000 | 1341 | |
243a041b | 1342 | if (fd >= 0) |
1fd5e000 | 1343 | { |
be96a30c CF |
1344 | /* This is a temporary kludge until all utilities can catch up with |
1345 | a change in behavior that implements linux functionality: opening | |
1346 | a tty should not automatically cause it to become the controlling | |
1347 | tty for the process. */ | |
23771fa1 CF |
1348 | int opt = PC_OPEN | ((flags & (O_NOFOLLOW | O_EXCL)) |
1349 | ? PC_SYM_NOFOLLOW : PC_SYM_FOLLOW); | |
c38a2d83 | 1350 | if (!(flags & O_NOCTTY) && fd > 2 && myself->ctty != -2) |
23771fa1 CF |
1351 | { |
1352 | flags |= O_NOCTTY; | |
1353 | opt |= PC_CTTY; /* flag that, if opened, this fhandler could | |
1354 | later be capable of being a controlling | |
1355 | terminal if /dev/tty is opened. */ | |
1356 | } | |
1357 | if (!(fh = build_fh_name (unix_path, opt, stat_suffixes))) | |
9854ada7 | 1358 | res = -1; // errno already set |
52792a77 CV |
1359 | else if ((flags & O_NOFOLLOW) && fh->issymlink ()) |
1360 | { | |
1361 | delete fh; | |
1362 | res = -1; | |
1363 | set_errno (ELOOP); | |
1364 | } | |
64d6e1d4 CV |
1365 | else if ((flags & O_DIRECTORY) && !fh->pc.isdir ()) |
1366 | { | |
1367 | delete fh; | |
1368 | res = -1; | |
1369 | set_errno (ENOTDIR); | |
1370 | } | |
5c70f2f9 CF |
1371 | else if (((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) && fh->exists ()) |
1372 | { | |
1373 | delete fh; | |
1374 | res = -1; | |
1375 | set_errno (EEXIST); | |
1376 | } | |
92ddb742 CF |
1377 | else if ((fh->is_fs_special () && fh->device_access_denied (flags)) |
1378 | || !fh->open_with_arch (flags, (mode & 07777) & ~cygheap->umask)) | |
7ac61736 CF |
1379 | { |
1380 | delete fh; | |
1381 | res = -1; | |
1382 | } | |
7ac61736 CF |
1383 | else |
1384 | { | |
92ddb742 CF |
1385 | fd = fh; |
1386 | if (fd <= 2) | |
1387 | set_std_handle (fd); | |
1388 | res = fd; | |
7ac61736 | 1389 | } |
1fd5e000 | 1390 | } |
1fd5e000 CF |
1391 | } |
1392 | ||
b9aa8149 | 1393 | syscall_printf ("%R = open(%s, %p)", res, unix_path, flags); |
1fd5e000 CF |
1394 | return res; |
1395 | } | |
1396 | ||
4875a4b6 CV |
1397 | EXPORT_ALIAS (open, _open ) |
1398 | EXPORT_ALIAS (open, _open64 ) | |
b1aae492 | 1399 | |
1727fba0 CV |
1400 | extern "C" _off64_t |
1401 | lseek64 (int fd, _off64_t pos, int dir) | |
1fd5e000 | 1402 | { |
1727fba0 | 1403 | _off64_t res; |
1fd5e000 | 1404 | |
0312ede4 | 1405 | if (dir != SEEK_SET && dir != SEEK_CUR && dir != SEEK_END) |
fc68bf34 | 1406 | { |
0312ede4 | 1407 | set_errno (EINVAL); |
fc68bf34 ED |
1408 | res = -1; |
1409 | } | |
1fd5e000 CF |
1410 | else |
1411 | { | |
df63bd49 CF |
1412 | cygheap_fdget cfd (fd); |
1413 | if (cfd >= 0) | |
1414 | res = cfd->lseek (pos, dir); | |
1415 | else | |
1416 | res = -1; | |
1fd5e000 | 1417 | } |
c1c62a13 CV |
1418 | /* Can't use %R here since res is 8 bytes */ |
1419 | syscall_printf (res == -1 ? "%D = lseek(%d, %D, %d), errno %d" | |
1420 | : "%D = lseek(%d, %D, %d)", | |
1421 | res, fd, pos, dir, get_errno ()); | |
1fd5e000 CF |
1422 | |
1423 | return res; | |
1424 | } | |
1425 | ||
4875a4b6 | 1426 | EXPORT_ALIAS (lseek64, _lseek64) |
b1aae492 | 1427 | |
1727fba0 CV |
1428 | extern "C" _off_t |
1429 | lseek (int fd, _off_t pos, int dir) | |
acb56175 | 1430 | { |
1727fba0 | 1431 | return lseek64 (fd, (_off64_t) pos, dir); |
acb56175 CV |
1432 | } |
1433 | ||
4875a4b6 | 1434 | EXPORT_ALIAS (lseek, _lseek) |
5ec14fe4 | 1435 | |
90fe7739 | 1436 | extern "C" int |
5ec14fe4 | 1437 | close (int fd) |
1fd5e000 CF |
1438 | { |
1439 | int res; | |
1440 | ||
ca35d41c | 1441 | syscall_printf ("close(%d)", fd); |
1fd5e000 | 1442 | |
1804be04 CV |
1443 | pthread_testcancel (); |
1444 | ||
1fd5e000 | 1445 | MALLOC_CHECK; |
df63bd49 CF |
1446 | cygheap_fdget cfd (fd, true); |
1447 | if (cfd < 0) | |
1448 | res = -1; | |
1fd5e000 CF |
1449 | else |
1450 | { | |
d004a279 | 1451 | cfd->isclosed (true); |
92ddb742 | 1452 | res = cfd->close_with_arch (); |
df63bd49 | 1453 | cfd.release (); |
1fd5e000 CF |
1454 | } |
1455 | ||
b9aa8149 | 1456 | syscall_printf ("%R = close(%d)", res, fd); |
1fd5e000 CF |
1457 | MALLOC_CHECK; |
1458 | return res; | |
1459 | } | |
1460 | ||
4875a4b6 | 1461 | EXPORT_ALIAS (close, _close) |
5ec14fe4 | 1462 | |
90fe7739 | 1463 | extern "C" int |
1fd5e000 CF |
1464 | isatty (int fd) |
1465 | { | |
1466 | int res; | |
1467 | ||
df63bd49 CF |
1468 | cygheap_fdget cfd (fd); |
1469 | if (cfd < 0) | |
1470 | res = 0; | |
9c510edc | 1471 | else |
df63bd49 | 1472 | res = cfd->is_tty (); |
b9aa8149 | 1473 | syscall_printf ("%R = isatty(%d)", res, fd); |
1fd5e000 CF |
1474 | return res; |
1475 | } | |
b40597cb | 1476 | EXPORT_ALIAS (isatty, _isatty) |
1fd5e000 | 1477 | |
90fe7739 | 1478 | extern "C" int |
0d75ce96 | 1479 | link (const char *oldpath, const char *newpath) |
1fd5e000 CF |
1480 | { |
1481 | int res = -1; | |
0d75ce96 | 1482 | fhandler_base *fh; |
109e4822 | 1483 | |
5a0d1edb CV |
1484 | if (!(fh = build_fh_name (oldpath, PC_SYM_NOFOLLOW | PC_KEEP_HANDLE, |
1485 | stat_suffixes))) | |
0d75ce96 | 1486 | goto error; |
1fd5e000 | 1487 | |
0d75ce96 | 1488 | if (fh->error ()) |
1fd5e000 | 1489 | { |
0d75ce96 CV |
1490 | debug_printf ("got %d error from build_fh_name", fh->error ()); |
1491 | set_errno (fh->error ()); | |
1fd5e000 | 1492 | } |
52dba6a5 EB |
1493 | else if (fh->pc.isdir ()) |
1494 | set_errno (EPERM); /* We do not permit linking directories. */ | |
1495 | else if (!fh->pc.exists ()) | |
1496 | set_errno (ENOENT); | |
1fd5e000 | 1497 | else |
0d75ce96 | 1498 | res = fh->link (newpath); |
1fd5e000 | 1499 | |
0d75ce96 CV |
1500 | delete fh; |
1501 | error: | |
b9aa8149 | 1502 | syscall_printf ("%R = link(%s, %s)", res, oldpath, newpath); |
1fd5e000 CF |
1503 | return res; |
1504 | } | |
1505 | ||
1fd5e000 CF |
1506 | /* chown: POSIX 5.6.5.1 */ |
1507 | /* | |
0312ede4 | 1508 | * chown () is only implemented for Windows NT. Under other operating |
1fd5e000 | 1509 | * systems, it is only a stub that always returns zero. |
1fd5e000 | 1510 | */ |
796e3b20 | 1511 | static int |
a8d7ae61 | 1512 | chown_worker (const char *name, unsigned fmode, __uid32_t uid, __gid32_t gid) |
1fd5e000 | 1513 | { |
e3d1d515 | 1514 | int res = -1; |
c8daf998 CV |
1515 | fhandler_base *fh; |
1516 | ||
4ee93264 | 1517 | if (!(fh = build_fh_name (name, fmode, stat_suffixes))) |
c8daf998 CV |
1518 | goto error; |
1519 | ||
e3d1d515 CV |
1520 | if (fh->error ()) |
1521 | { | |
1522 | debug_printf ("got %d error from build_fh_name", fh->error ()); | |
1523 | set_errno (fh->error ()); | |
1fd5e000 | 1524 | } |
e3d1d515 CV |
1525 | else |
1526 | res = fh->fchown (uid, gid); | |
1fd5e000 | 1527 | |
e84c7766 | 1528 | delete fh; |
c8daf998 | 1529 | error: |
b9aa8149 | 1530 | syscall_printf ("%R = %schown(%s,...)", |
78d2c08c | 1531 | res, (fmode & PC_SYM_NOFOLLOW) ? "l" : "", name); |
1fd5e000 CF |
1532 | return res; |
1533 | } | |
1534 | ||
90fe7739 | 1535 | extern "C" int |
a8d7ae61 | 1536 | chown32 (const char * name, __uid32_t uid, __gid32_t gid) |
796e3b20 | 1537 | { |
5bc584ba | 1538 | return chown_worker (name, PC_SYM_FOLLOW, uid, gid); |
796e3b20 CV |
1539 | } |
1540 | ||
90fe7739 | 1541 | extern "C" int |
57196405 CV |
1542 | chown (const char * name, __uid16_t uid, __gid16_t gid) |
1543 | { | |
6331b658 CV |
1544 | return chown_worker (name, PC_SYM_FOLLOW, |
1545 | uid16touid32 (uid), gid16togid32 (gid)); | |
57196405 CV |
1546 | } |
1547 | ||
1548 | extern "C" int | |
a8d7ae61 | 1549 | lchown32 (const char * name, __uid32_t uid, __gid32_t gid) |
796e3b20 | 1550 | { |
78d2c08c | 1551 | return chown_worker (name, PC_SYM_NOFOLLOW, uid, gid); |
796e3b20 CV |
1552 | } |
1553 | ||
90fe7739 | 1554 | extern "C" int |
57196405 CV |
1555 | lchown (const char * name, __uid16_t uid, __gid16_t gid) |
1556 | { | |
6331b658 CV |
1557 | return chown_worker (name, PC_SYM_NOFOLLOW, |
1558 | uid16touid32 (uid), gid16togid32 (gid)); | |
57196405 CV |
1559 | } |
1560 | ||
1561 | extern "C" int | |
a8d7ae61 | 1562 | fchown32 (int fd, __uid32_t uid, __gid32_t gid) |
796e3b20 | 1563 | { |
df63bd49 CF |
1564 | cygheap_fdget cfd (fd); |
1565 | if (cfd < 0) | |
796e3b20 CV |
1566 | { |
1567 | syscall_printf ("-1 = fchown (%d,...)", fd); | |
796e3b20 CV |
1568 | return -1; |
1569 | } | |
1570 | ||
e3d1d515 | 1571 | int res = cfd->fchown (uid, gid); |
796e3b20 | 1572 | |
b9aa8149 | 1573 | syscall_printf ("%R = fchown(%s,...)", res, cfd->get_name ()); |
e3d1d515 | 1574 | return res; |
796e3b20 CV |
1575 | } |
1576 | ||
57196405 CV |
1577 | extern "C" int |
1578 | fchown (int fd, __uid16_t uid, __gid16_t gid) | |
1579 | { | |
6331b658 | 1580 | return fchown32 (fd, uid16touid32 (uid), gid16togid32 (gid)); |
57196405 CV |
1581 | } |
1582 | ||
1fd5e000 | 1583 | /* umask: POSIX 5.3.3.1 */ |
90fe7739 | 1584 | extern "C" mode_t |
1fd5e000 CF |
1585 | umask (mode_t mask) |
1586 | { | |
1587 | mode_t oldmask; | |
1588 | ||
a4785603 CF |
1589 | oldmask = cygheap->umask; |
1590 | cygheap->umask = mask & 0777; | |
1fd5e000 CF |
1591 | return oldmask; |
1592 | } | |
1593 | ||
7ac61736 CF |
1594 | int |
1595 | chmod_device (path_conv& pc, mode_t mode) | |
1596 | { | |
44d2fc0a | 1597 | return mknod_worker (pc.get_win32 (), pc.dev.mode & S_IFMT, mode, pc.dev.get_major (), pc.dev.get_minor ()); |
7ac61736 CF |
1598 | } |
1599 | ||
2762ac6c CV |
1600 | #define FILTERED_MODE(m) ((m) & (S_ISUID | S_ISGID | S_ISVTX \ |
1601 | | S_IRWXU | S_IRWXG | S_IRWXO)) | |
1602 | ||
1fd5e000 | 1603 | /* chmod: POSIX 5.6.4.1 */ |
90fe7739 | 1604 | extern "C" int |
1fd5e000 CF |
1605 | chmod (const char *path, mode_t mode) |
1606 | { | |
1607 | int res = -1; | |
c8daf998 | 1608 | fhandler_base *fh; |
4ee93264 | 1609 | if (!(fh = build_fh_name (path, PC_SYM_FOLLOW, stat_suffixes))) |
c8daf998 | 1610 | goto error; |
e3778517 | 1611 | |
854c8700 | 1612 | if (fh->error ()) |
1fd5e000 | 1613 | { |
854c8700 CV |
1614 | debug_printf ("got %d error from build_fh_name", fh->error ()); |
1615 | set_errno (fh->error ()); | |
1fd5e000 | 1616 | } |
1fd5e000 | 1617 | else |
2762ac6c | 1618 | res = fh->fchmod (FILTERED_MODE (mode)); |
1fd5e000 | 1619 | |
e84c7766 | 1620 | delete fh; |
c8daf998 | 1621 | error: |
b9aa8149 | 1622 | syscall_printf ("%R = chmod(%s, %p)", res, path, mode); |
1fd5e000 CF |
1623 | return res; |
1624 | } | |
1625 | ||
1626 | /* fchmod: P96 5.6.4.1 */ | |
1627 | ||
90fe7739 | 1628 | extern "C" int |
1fd5e000 CF |
1629 | fchmod (int fd, mode_t mode) |
1630 | { | |
df63bd49 CF |
1631 | cygheap_fdget cfd (fd); |
1632 | if (cfd < 0) | |
1fd5e000 CF |
1633 | { |
1634 | syscall_printf ("-1 = fchmod (%d, 0%o)", fd, mode); | |
1fd5e000 CF |
1635 | return -1; |
1636 | } | |
1637 | ||
2762ac6c | 1638 | return cfd->fchmod (FILTERED_MODE (mode)); |
1fd5e000 CF |
1639 | } |
1640 | ||
acb56175 CV |
1641 | static void |
1642 | stat64_to_stat32 (struct __stat64 *src, struct __stat32 *dst) | |
1643 | { | |
b3c29731 | 1644 | dst->st_dev = ((src->st_dev >> 8) & 0xff00) | (src->st_dev & 0xff); |
066ca06f | 1645 | dst->st_ino = ((unsigned) (src->st_ino >> 32)) | (unsigned) src->st_ino; |
acb56175 CV |
1646 | dst->st_mode = src->st_mode; |
1647 | dst->st_nlink = src->st_nlink; | |
1648 | dst->st_uid = src->st_uid; | |
1649 | dst->st_gid = src->st_gid; | |
1a7cb557 | 1650 | dst->st_rdev = ((src->st_rdev >> 8) & 0xff00) | (src->st_rdev & 0xff); |
acb56175 | 1651 | dst->st_size = src->st_size; |
c4e6ff48 CV |
1652 | dst->st_atim = src->st_atim; |
1653 | dst->st_mtim = src->st_mtim; | |
1654 | dst->st_ctim = src->st_ctim; | |
acb56175 CV |
1655 | dst->st_blksize = src->st_blksize; |
1656 | dst->st_blocks = src->st_blocks; | |
1657 | } | |
1658 | ||
949cc8b6 CV |
1659 | static struct __stat64 dev_st; |
1660 | static bool dev_st_inited; | |
1661 | ||
1662 | void | |
467e17b9 | 1663 | fhandler_base::stat_fixup (struct __stat64 *buf) |
949cc8b6 | 1664 | { |
e62d782a CV |
1665 | /* For devices, set inode number to device number. This gives us a valid, |
1666 | unique inode number without having to call hash_path_name. */ | |
949cc8b6 | 1667 | if (!buf->st_ino) |
e62d782a CV |
1668 | buf->st_ino = (get_major () == DEV_VIRTFS_MAJOR) ? get_ino () |
1669 | : get_device (); | |
949cc8b6 CV |
1670 | /* For /dev-based devices, st_dev must be set to the device number of /dev, |
1671 | not it's own device major/minor numbers. What we do here to speed up | |
1672 | the process is to fetch the device number of /dev only once, liberally | |
1673 | assuming that /dev doesn't change over the lifetime of a process. */ | |
1674 | if (!buf->st_dev) | |
1675 | { | |
f777ddfb | 1676 | if (dev ().is_dev_resident ()) |
949cc8b6 CV |
1677 | { |
1678 | if (!dev_st_inited) | |
1679 | { | |
1680 | stat64 ("/dev", &dev_st); | |
1681 | dev_st_inited = true; | |
1682 | } | |
1683 | buf->st_dev = dev_st.st_dev; | |
1684 | } | |
1685 | else | |
1686 | buf->st_dev = get_device (); | |
1687 | } | |
1688 | /* Only set st_rdev if it's a device. */ | |
f7c8c454 | 1689 | if (!buf->st_rdev && get_major () != DEV_VIRTFS_MAJOR) |
467e17b9 CV |
1690 | { |
1691 | buf->st_rdev = get_device (); | |
1692 | /* consX, console, conin, and conout point to the same device. | |
eb7729eb | 1693 | Make sure the link count is correct. */ |
467e17b9 CV |
1694 | if (buf->st_rdev == (dev_t) myself->ctty && iscons_dev (myself->ctty)) |
1695 | buf->st_nlink = 4; | |
eb7729eb CV |
1696 | /* CD-ROM drives have two links, /dev/srX and /dev/scdX. */ |
1697 | else if (gnu_dev_major (buf->st_rdev) == DEV_CDROM_MAJOR) | |
1698 | buf->st_nlink = 2; | |
467e17b9 | 1699 | } |
949cc8b6 CV |
1700 | } |
1701 | ||
90fe7739 | 1702 | extern "C" int |
acb56175 | 1703 | fstat64 (int fd, struct __stat64 *buf) |
1fd5e000 | 1704 | { |
df63bd49 | 1705 | int res; |
1fd5e000 | 1706 | |
df63bd49 CF |
1707 | cygheap_fdget cfd (fd); |
1708 | if (cfd < 0) | |
1709 | res = -1; | |
1fd5e000 CF |
1710 | else |
1711 | { | |
acb56175 | 1712 | memset (buf, 0, sizeof (struct __stat64)); |
7ac61736 CF |
1713 | res = cfd->fstat (buf); |
1714 | if (!res) | |
467e17b9 | 1715 | cfd->stat_fixup (buf); |
1fd5e000 CF |
1716 | } |
1717 | ||
b9aa8149 | 1718 | syscall_printf ("%R = fstat(%d, %p)", res, fd, buf); |
df63bd49 | 1719 | return res; |
1fd5e000 CF |
1720 | } |
1721 | ||
194d9eb3 CV |
1722 | extern "C" int |
1723 | _fstat64_r (struct _reent *ptr, int fd, struct __stat64 *buf) | |
1724 | { | |
1725 | int ret; | |
1726 | ||
4717214c | 1727 | if ((ret = fstat64 (fd, buf)) == -1) |
194d9eb3 CV |
1728 | ptr->_errno = get_errno (); |
1729 | return ret; | |
1730 | } | |
b1aae492 | 1731 | |
acb56175 | 1732 | extern "C" int |
3543669f | 1733 | fstat (int fd, struct __stat32 *buf) |
acb56175 CV |
1734 | { |
1735 | struct __stat64 buf64; | |
1736 | int ret = fstat64 (fd, &buf64); | |
1737 | if (!ret) | |
1738 | stat64_to_stat32 (&buf64, buf); | |
1739 | return ret; | |
1740 | } | |
1741 | ||
194d9eb3 CV |
1742 | extern "C" int |
1743 | _fstat_r (struct _reent *ptr, int fd, struct __stat32 *buf) | |
1744 | { | |
1745 | int ret; | |
1746 | ||
4717214c | 1747 | if ((ret = fstat (fd, buf)) == -1) |
194d9eb3 CV |
1748 | ptr->_errno = get_errno (); |
1749 | return ret; | |
1750 | } | |
3543669f | 1751 | |
1fd5e000 | 1752 | /* fsync: P96 6.6.1.1 */ |
90fe7739 | 1753 | extern "C" int |
1fd5e000 CF |
1754 | fsync (int fd) |
1755 | { | |
1804be04 | 1756 | pthread_testcancel (); |
df63bd49 CF |
1757 | cygheap_fdget cfd (fd); |
1758 | if (cfd < 0) | |
1fd5e000 CF |
1759 | { |
1760 | syscall_printf ("-1 = fsync (%d)", fd); | |
1fd5e000 CF |
1761 | return -1; |
1762 | } | |
4944ca2f CV |
1763 | return cfd->fsync (); |
1764 | } | |
1fd5e000 | 1765 | |
a62f6b80 CV |
1766 | EXPORT_ALIAS (fsync, fdatasync) |
1767 | ||
05726ddd | 1768 | static void |
0d6f2b01 | 1769 | sync_worker (HANDLE dir, USHORT len, LPCWSTR vol) |
4944ca2f | 1770 | { |
0d6f2b01 CV |
1771 | NTSTATUS status; |
1772 | HANDLE fh; | |
1773 | IO_STATUS_BLOCK io; | |
1774 | OBJECT_ATTRIBUTES attr; | |
1775 | UNICODE_STRING uvol = { len, len, (WCHAR *) vol }; | |
1776 | ||
1777 | InitializeObjectAttributes (&attr, &uvol, OBJ_CASE_INSENSITIVE, dir, NULL); | |
1778 | status = NtOpenFile (&fh, GENERIC_WRITE, &attr, &io, | |
1779 | FILE_SHARE_VALID_FLAGS, 0); | |
1780 | if (!NT_SUCCESS (status)) | |
1781 | debug_printf ("NtOpenFile (%S), status %p", &uvol, status); | |
1782 | else | |
1fd5e000 | 1783 | { |
0d6f2b01 CV |
1784 | status = NtFlushBuffersFile (fh, &io); |
1785 | if (!NT_SUCCESS (status)) | |
1786 | debug_printf ("NtFlushBuffersFile (%S), status %p", &uvol, status); | |
1787 | NtClose (fh); | |
1fd5e000 | 1788 | } |
1fd5e000 CF |
1789 | } |
1790 | ||
66d9ac39 CV |
1791 | /* sync: SUSv3 */ |
1792 | extern "C" void | |
1fd5e000 CF |
1793 | sync () |
1794 | { | |
0d6f2b01 CV |
1795 | OBJECT_ATTRIBUTES attr; |
1796 | NTSTATUS status; | |
1797 | HANDLE devhdl; | |
1798 | UNICODE_STRING device; | |
05726ddd | 1799 | |
0d6f2b01 CV |
1800 | /* Open \Device object directory. */ |
1801 | RtlInitUnicodeString (&device, L"\\Device"); | |
1802 | InitializeObjectAttributes (&attr, &device, OBJ_CASE_INSENSITIVE, NULL, NULL); | |
1803 | status = NtOpenDirectoryObject (&devhdl, DIRECTORY_QUERY, &attr); | |
1804 | if (!NT_SUCCESS (status)) | |
4944ca2f | 1805 | { |
0d6f2b01 CV |
1806 | debug_printf ("NtOpenDirectoryObject, status %p", status); |
1807 | return; | |
4944ca2f | 1808 | } |
0d6f2b01 CV |
1809 | /* Traverse \Device directory ... */ |
1810 | PDIRECTORY_BASIC_INFORMATION dbi = (PDIRECTORY_BASIC_INFORMATION) | |
b86f999a | 1811 | alloca (640); |
0d6f2b01 CV |
1812 | BOOLEAN restart = TRUE; |
1813 | ULONG context = 0; | |
1814 | while (NT_SUCCESS (NtQueryDirectoryObject (devhdl, dbi, 640, TRUE, restart, | |
b86f999a | 1815 | &context, NULL))) |
4944ca2f | 1816 | { |
0d6f2b01 CV |
1817 | restart = FALSE; |
1818 | /* ... and call sync_worker for each HarddiskVolumeX entry. */ | |
1819 | if (dbi->ObjectName.Length >= 15 * sizeof (WCHAR) | |
1820 | && !wcsncasecmp (dbi->ObjectName.Buffer, L"HarddiskVolume", 14) | |
1821 | && iswdigit (dbi->ObjectName.Buffer[14])) | |
1822 | sync_worker (devhdl, dbi->ObjectName.Length, dbi->ObjectName.Buffer); | |
4944ca2f | 1823 | } |
0d6f2b01 | 1824 | NtClose (devhdl); |
1fd5e000 CF |
1825 | } |
1826 | ||
1fd5e000 | 1827 | /* Cygwin internal */ |
214c3a11 CV |
1828 | int __stdcall |
1829 | stat_worker (path_conv &pc, struct __stat64 *buf) | |
1fd5e000 CF |
1830 | { |
1831 | int res = -1; | |
d6581f44 | 1832 | |
893ac8e0 CF |
1833 | myfault efault; |
1834 | if (efault.faulted (EFAULT)) | |
c8daf998 | 1835 | goto error; |
e065a187 | 1836 | |
73151c54 | 1837 | if (pc.error) |
1fd5e000 | 1838 | { |
4ee93264 | 1839 | debug_printf ("got %d error from path_conv", pc.error); |
73151c54 | 1840 | set_errno (pc.error); |
1fd5e000 | 1841 | } |
73151c54 | 1842 | else if (pc.exists ()) |
1fd5e000 | 1843 | { |
73151c54 CV |
1844 | fhandler_base *fh; |
1845 | ||
1846 | if (!(fh = build_fh_pc (pc))) | |
1847 | goto error; | |
1848 | ||
c4bd8377 CV |
1849 | debug_printf ("(%S, %p, %p), file_attributes %d", |
1850 | pc.get_nt_native_path (), buf, fh, (DWORD) *fh); | |
e065a187 | 1851 | memset (buf, 0, sizeof (*buf)); |
7ac61736 CF |
1852 | res = fh->fstat (buf); |
1853 | if (!res) | |
467e17b9 | 1854 | fh->stat_fixup (buf); |
73151c54 | 1855 | delete fh; |
1fd5e000 | 1856 | } |
c8daf998 CV |
1857 | else |
1858 | set_errno (ENOENT); | |
1fd5e000 | 1859 | |
c8daf998 | 1860 | error: |
1fd5e000 | 1861 | MALLOC_CHECK; |
b9aa8149 | 1862 | syscall_printf ("%d = (%S,%p)", res, pc.get_nt_native_path (), buf); |
1fd5e000 CF |
1863 | return res; |
1864 | } | |
1865 | ||
90fe7739 | 1866 | extern "C" int |
acb56175 | 1867 | stat64 (const char *name, struct __stat64 *buf) |
1fd5e000 | 1868 | { |
32fb80db | 1869 | syscall_printf ("entering"); |
23771fa1 CF |
1870 | path_conv pc (name, PC_SYM_FOLLOW | PC_POSIX | PC_KEEP_HANDLE, |
1871 | stat_suffixes); | |
214c3a11 | 1872 | return stat_worker (pc, buf); |
1fd5e000 CF |
1873 | } |
1874 | ||
194d9eb3 CV |
1875 | extern "C" int |
1876 | _stat64_r (struct _reent *ptr, const char *name, struct __stat64 *buf) | |
1877 | { | |
1878 | int ret; | |
1879 | ||
4717214c | 1880 | if ((ret = stat64 (name, buf)) == -1) |
194d9eb3 CV |
1881 | ptr->_errno = get_errno (); |
1882 | return ret; | |
1883 | } | |
1884 | ||
acb56175 | 1885 | extern "C" int |
3543669f | 1886 | stat (const char *name, struct __stat32 *buf) |
acb56175 CV |
1887 | { |
1888 | struct __stat64 buf64; | |
1889 | int ret = stat64 (name, &buf64); | |
1890 | if (!ret) | |
1891 | stat64_to_stat32 (&buf64, buf); | |
1892 | return ret; | |
1893 | } | |
1894 | ||
194d9eb3 CV |
1895 | extern "C" int |
1896 | _stat_r (struct _reent *ptr, const char *name, struct __stat32 *buf) | |
1897 | { | |
1898 | int ret; | |
1899 | ||
4717214c | 1900 | if ((ret = stat (name, buf)) == -1) |
194d9eb3 CV |
1901 | ptr->_errno = get_errno (); |
1902 | return ret; | |
1903 | } | |
1904 | ||
1fd5e000 | 1905 | /* lstat: Provided by SVR4 and 4.3+BSD, POSIX? */ |
90fe7739 | 1906 | extern "C" int |
acb56175 | 1907 | lstat64 (const char *name, struct __stat64 *buf) |
1fd5e000 | 1908 | { |
32fb80db | 1909 | syscall_printf ("entering"); |
5a0d1edb CV |
1910 | path_conv pc (name, PC_SYM_NOFOLLOW | PC_POSIX | PC_KEEP_HANDLE, |
1911 | stat_suffixes); | |
214c3a11 | 1912 | return stat_worker (pc, buf); |
1fd5e000 CF |
1913 | } |
1914 | ||
acb56175 CV |
1915 | /* lstat: Provided by SVR4 and 4.3+BSD, POSIX? */ |
1916 | extern "C" int | |
ae89a496 | 1917 | lstat (const char *name, struct __stat32 *buf) |
acb56175 CV |
1918 | { |
1919 | struct __stat64 buf64; | |
1920 | int ret = lstat64 (name, &buf64); | |
1921 | if (!ret) | |
1922 | stat64_to_stat32 (&buf64, buf); | |
1923 | return ret; | |
1924 | } | |
1925 | ||
7ac61736 CF |
1926 | extern "C" int |
1927 | access (const char *fn, int flags) | |
1928 | { | |
7ac61736 | 1929 | // flags were incorrectly specified |
f4e815bc | 1930 | int res = -1; |
7ac61736 | 1931 | if (flags & ~(F_OK|R_OK|W_OK|X_OK)) |
f4e815bc CF |
1932 | set_errno (EINVAL); |
1933 | else | |
7ac61736 | 1934 | { |
303dc023 CV |
1935 | fhandler_base *fh = build_fh_name (fn, PC_SYM_FOLLOW | PC_KEEP_HANDLE, |
1936 | stat_suffixes); | |
c8daf998 | 1937 | if (fh) |
e3778517 | 1938 | { |
3dbe243a | 1939 | res = fh->fhaccess (flags, false); |
c8daf998 CV |
1940 | delete fh; |
1941 | } | |
7ac61736 | 1942 | } |
f4e815bc CF |
1943 | debug_printf ("returning %d", res); |
1944 | return res; | |
7ac61736 CF |
1945 | } |
1946 | ||
2bf78f09 EB |
1947 | /* Linux provides this extension; it is basically a wrapper around the |
1948 | POSIX:2008 faccessat (AT_FDCWD, fn, flags, AT_EACCESS). We also | |
1949 | provide eaccess as an alias for this, in cygwin.din. */ | |
1950 | extern "C" int | |
1951 | euidaccess (const char *fn, int flags) | |
1952 | { | |
1953 | // flags were incorrectly specified | |
1954 | int res = -1; | |
1955 | if (flags & ~(F_OK|R_OK|W_OK|X_OK)) | |
1956 | set_errno (EINVAL); | |
1957 | else | |
1958 | { | |
303dc023 CV |
1959 | fhandler_base *fh = build_fh_name (fn, PC_SYM_FOLLOW | PC_KEEP_HANDLE, |
1960 | stat_suffixes); | |
2bf78f09 EB |
1961 | if (fh) |
1962 | { | |
1963 | res = fh->fhaccess (flags, true); | |
1964 | delete fh; | |
1965 | } | |
1966 | } | |
1967 | debug_printf ("returning %d", res); | |
1968 | return res; | |
1969 | } | |
1970 | ||
9235f3ea CV |
1971 | static void |
1972 | rename_append_suffix (path_conv &pc, const char *path, size_t len, | |
1973 | const char *suffix) | |
1fd5e000 | 1974 | { |
9235f3ea | 1975 | char buf[len + 5]; |
1fd5e000 | 1976 | |
c69d873f CV |
1977 | if (ascii_strcasematch (path + len - 4, ".lnk") |
1978 | || ascii_strcasematch (path + len - 4, ".exe")) | |
9235f3ea CV |
1979 | len -= 4; |
1980 | stpcpy (stpncpy (buf, path, len), suffix); | |
1981 | pc.check (buf, PC_SYM_NOFOLLOW); | |
1982 | } | |
1fd5e000 | 1983 | |
b36d8c46 CV |
1984 | /* This function tests if a filename has one of the "approved" executable |
1985 | suffix. This list is probably not complete... */ | |
ac4ad8bf | 1986 | static inline bool |
b36d8c46 | 1987 | nt_path_has_executable_suffix (PUNICODE_STRING upath) |
ac4ad8bf | 1988 | { |
8e75995c | 1989 | static const PUNICODE_STRING blessed_executable_suffixes[] = |
b36d8c46 CV |
1990 | { |
1991 | &ro_u_com, | |
d9e9c7b5 CV |
1992 | &ro_u_dll, /* Messy, messy. Per MSDN, the GetBinaryType function is |
1993 | supposed to return with ERROR_BAD_EXE_FORMAT. if the file | |
1994 | is a DLL. On 64-bit Windows, this works as expected for | |
1995 | 32-bit and 64-bit DLLs. On 32-bit Windows this only works | |
1996 | for 32-bit DLLs. For 64-bit DLLs, 32-bit Windows returns | |
1997 | true with the type set to SCS_64BIT_BINARY. */ | |
b36d8c46 CV |
1998 | &ro_u_exe, |
1999 | &ro_u_scr, | |
2000 | &ro_u_sys, | |
2001 | NULL | |
2002 | }; | |
2003 | ||
ac4ad8bf | 2004 | USHORT pos = upath->Length / sizeof (WCHAR); |
b36d8c46 CV |
2005 | PWCHAR path; |
2006 | UNICODE_STRING usuf; | |
2007 | const PUNICODE_STRING *suf; | |
ac4ad8bf CV |
2008 | |
2009 | /* Too short for a native path? */ | |
2010 | if (pos < 8) | |
2011 | return false; | |
b36d8c46 CV |
2012 | /* Assumption: All executable suffixes have a length of three. */ |
2013 | path = upath->Buffer + pos - 4; | |
2014 | if (*path != L'.') | |
2015 | return false; | |
2016 | RtlInitCountedUnicodeString (&usuf, path, 4 * sizeof (WCHAR)); | |
2017 | for (suf = blessed_executable_suffixes; *suf; ++suf) | |
2018 | if (RtlEqualUnicodeString (&usuf, *suf, TRUE)) | |
2019 | return true; | |
ac4ad8bf CV |
2020 | return false; |
2021 | } | |
2022 | ||
9235f3ea CV |
2023 | extern "C" int |
2024 | rename (const char *oldpath, const char *newpath) | |
2025 | { | |
78ee2ae1 | 2026 | tmp_pathbuf tp; |
9235f3ea CV |
2027 | int res = -1; |
2028 | path_conv oldpc, newpc, new2pc, *dstpc, *removepc = NULL; | |
349fba0c | 2029 | bool old_dir_requested = false, new_dir_requested = false; |
9235f3ea CV |
2030 | bool old_explicit_suffix = false, new_explicit_suffix = false; |
2031 | size_t olen, nlen; | |
349fba0c | 2032 | bool equal_path; |
28fa2a72 | 2033 | NTSTATUS status = STATUS_SUCCESS; |
349fba0c | 2034 | HANDLE fh = NULL, nfh; |
4c153b24 | 2035 | HANDLE old_trans = NULL, trans = NULL; |
9235f3ea CV |
2036 | OBJECT_ATTRIBUTES attr; |
2037 | IO_STATUS_BLOCK io; | |
e84b3704 | 2038 | FILE_STANDARD_INFORMATION ofsi; |
9235f3ea | 2039 | PFILE_RENAME_INFORMATION pfri; |
8884a168 CV |
2040 | |
2041 | myfault efault; | |
2042 | if (efault.faulted (EFAULT)) | |
2043 | return -1; | |
2044 | ||
52dba6a5 EB |
2045 | if (!*oldpath || !*newpath) |
2046 | { | |
1c090988 | 2047 | /* Reject rename("","x"), rename("x",""). */ |
52dba6a5 EB |
2048 | set_errno (ENOENT); |
2049 | goto out; | |
2050 | } | |
124d4c50 | 2051 | if (has_dot_last_component (oldpath, true)) |
8884a168 | 2052 | { |
1c090988 | 2053 | /* Reject rename("dir/.","x"). */ |
124d4c50 | 2054 | oldpc.check (oldpath, PC_SYM_NOFOLLOW, stat_suffixes); |
52dba6a5 | 2055 | set_errno (oldpc.isdir () ? EINVAL : ENOTDIR); |
124d4c50 CV |
2056 | goto out; |
2057 | } | |
2058 | if (has_dot_last_component (newpath, true)) | |
2059 | { | |
1c090988 | 2060 | /* Reject rename("dir","x/."). */ |
124d4c50 | 2061 | newpc.check (newpath, PC_SYM_NOFOLLOW, stat_suffixes); |
52dba6a5 | 2062 | set_errno (!newpc.exists () ? ENOENT : newpc.isdir () ? EINVAL : ENOTDIR); |
8884a168 CV |
2063 | goto out; |
2064 | } | |
2065 | ||
349fba0c CV |
2066 | /* A trailing slash requires that the pathname points to an existing |
2067 | directory. If it's not, it's a ENOTDIR condition. The same goes | |
2068 | for newpath a bit further down this function. */ | |
2069 | olen = strlen (oldpath); | |
2070 | if (isdirsep (oldpath[olen - 1])) | |
2071 | { | |
52dba6a5 EB |
2072 | char *buf; |
2073 | char *p = stpcpy (buf = tp.c_get (), oldpath) - 1; | |
2074 | oldpath = buf; | |
2075 | while (p >= oldpath && isdirsep (*p)) | |
b86f999a | 2076 | *p-- = '\0'; |
52dba6a5 EB |
2077 | olen = p + 1 - oldpath; |
2078 | if (!olen) | |
b86f999a CF |
2079 | { |
2080 | /* The root directory cannot be renamed. This also rejects | |
2081 | the corner case of rename("/","/"), even though it is the | |
2082 | same file. */ | |
2083 | set_errno (EINVAL); | |
2084 | goto out; | |
2085 | } | |
349fba0c CV |
2086 | old_dir_requested = true; |
2087 | } | |
9235f3ea CV |
2088 | oldpc.check (oldpath, PC_SYM_NOFOLLOW, stat_suffixes); |
2089 | if (oldpc.error) | |
1fd5e000 | 2090 | { |
9235f3ea CV |
2091 | set_errno (oldpc.error); |
2092 | goto out; | |
1fd5e000 | 2093 | } |
9235f3ea | 2094 | if (!oldpc.exists ()) |
75fbcb44 | 2095 | { |
75fbcb44 | 2096 | set_errno (ENOENT); |
9235f3ea | 2097 | goto out; |
75fbcb44 | 2098 | } |
ce9898da | 2099 | if (oldpc.isspecial () && !oldpc.issocket () && !oldpc.is_fs_special ()) |
8884a168 | 2100 | { |
ce9898da | 2101 | /* No renames from virtual FS */ |
8884a168 CV |
2102 | set_errno (EROFS); |
2103 | goto out; | |
2104 | } | |
16a72f7e CV |
2105 | if (oldpc.has_attribute (FILE_ATTRIBUTE_REPARSE_POINT) && !oldpc.issymlink ()) |
2106 | { | |
2107 | /* Volume mount point. If we try to rename a volume mount point, NT | |
2108 | returns STATUS_NOT_SAME_DEVICE ==> Win32 ERROR_NOT_SAME_DEVICE ==> | |
2109 | errno EXDEV. That's bad since mv(1) will now perform a cross-device | |
2110 | move. So what we do here is to treat the volume mount point just | |
2111 | like Linux treats a mount point. */ | |
2112 | set_errno (EBUSY); | |
2113 | goto out; | |
2114 | } | |
349fba0c CV |
2115 | if (old_dir_requested && !oldpc.isdir ()) |
2116 | { | |
1c090988 | 2117 | /* Reject rename("file/","x"). */ |
349fba0c CV |
2118 | set_errno (ENOTDIR); |
2119 | goto out; | |
2120 | } | |
8e75995c | 2121 | if (oldpc.known_suffix |
b36d8c46 CV |
2122 | && (ascii_strcasematch (oldpath + olen - 4, ".lnk") |
2123 | || ascii_strcasematch (oldpath + olen - 4, ".exe"))) | |
9235f3ea CV |
2124 | old_explicit_suffix = true; |
2125 | ||
349fba0c CV |
2126 | nlen = strlen (newpath); |
2127 | if (isdirsep (newpath[nlen - 1])) | |
2128 | { | |
52dba6a5 EB |
2129 | char *buf; |
2130 | char *p = stpcpy (buf = tp.c_get (), newpath) - 1; | |
2131 | newpath = buf; | |
2132 | while (p >= newpath && isdirsep (*p)) | |
b86f999a | 2133 | *p-- = '\0'; |
52dba6a5 EB |
2134 | nlen = p + 1 - newpath; |
2135 | if (!nlen) /* The root directory is never empty. */ | |
b86f999a CF |
2136 | { |
2137 | set_errno (ENOTEMPTY); | |
2138 | goto out; | |
2139 | } | |
349fba0c CV |
2140 | new_dir_requested = true; |
2141 | } | |
9235f3ea CV |
2142 | newpc.check (newpath, PC_SYM_NOFOLLOW, stat_suffixes); |
2143 | if (newpc.error) | |
2144 | { | |
2145 | set_errno (newpc.error); | |
2146 | goto out; | |
2147 | } | |
4bcdec72 | 2148 | if (newpc.isspecial () && !newpc.issocket ()) /* No renames to virtual FSes */ |
c5a4eacc | 2149 | { |
9235f3ea CV |
2150 | set_errno (EROFS); |
2151 | goto out; | |
2152 | } | |
1c090988 | 2153 | if (new_dir_requested && !(newpc.exists () |
b86f999a | 2154 | ? newpc.isdir () : oldpc.isdir ())) |
349fba0c | 2155 | { |
1c090988 EB |
2156 | /* Reject rename("file1","file2/"), but allow rename("dir","d/"). */ |
2157 | set_errno (newpc.exists () ? ENOTDIR : ENOENT); | |
2158 | goto out; | |
52dba6a5 EB |
2159 | } |
2160 | if (newpc.exists () && (oldpc.isdir () ? !newpc.isdir () : newpc.isdir ())) | |
2161 | { | |
1c090988 | 2162 | /* Reject rename("file","dir") and rename("dir","file"). */ |
52dba6a5 | 2163 | set_errno (newpc.isdir () ? EISDIR : ENOTDIR); |
349fba0c CV |
2164 | goto out; |
2165 | } | |
9235f3ea | 2166 | if (newpc.known_suffix |
c69d873f CV |
2167 | && (ascii_strcasematch (newpath + nlen - 4, ".lnk") |
2168 | || ascii_strcasematch (newpath + nlen - 4, ".exe"))) | |
9235f3ea | 2169 | new_explicit_suffix = true; |
fbae2bf8 | 2170 | |
349fba0c CV |
2171 | /* This test is necessary in almost every case, so just do it once here. */ |
2172 | equal_path = RtlEqualUnicodeString (oldpc.get_nt_native_path (), | |
2173 | newpc.get_nt_native_path (), | |
e4b57503 | 2174 | oldpc.objcaseinsensitive ()); |
349fba0c CV |
2175 | |
2176 | /* First check if oldpath and newpath only differ by case. If so, it's | |
2177 | just a request to change the case of the filename. By simply setting | |
2178 | the file attributes to INVALID_FILE_ATTRIBUTES (which translates to | |
0f7ac147 | 2179 | "file doesn't exist"), all later tests are skipped. */ |
c32849c5 CV |
2180 | if (oldpc.objcaseinsensitive () && newpc.exists () && equal_path |
2181 | && old_explicit_suffix == new_explicit_suffix) | |
48726c8a CV |
2182 | { |
2183 | if (RtlEqualUnicodeString (oldpc.get_nt_native_path (), | |
349fba0c CV |
2184 | newpc.get_nt_native_path (), |
2185 | FALSE)) | |
48726c8a | 2186 | { |
0f7ac147 | 2187 | res = 0; |
48726c8a CV |
2188 | goto out; |
2189 | } | |
2190 | newpc.file_attributes (INVALID_FILE_ATTRIBUTES); | |
2191 | } | |
349fba0c | 2192 | else if (oldpc.isdir ()) |
9235f3ea | 2193 | { |
52dba6a5 | 2194 | /* Check for newpath being identical or a subdir of oldpath. */ |
9235f3ea CV |
2195 | if (RtlPrefixUnicodeString (oldpc.get_nt_native_path (), |
2196 | newpc.get_nt_native_path (), | |
52dba6a5 | 2197 | TRUE)) |
34f5d087 | 2198 | { |
52dba6a5 EB |
2199 | if (newpc.get_nt_native_path ()->Length |
2200 | == oldpc.get_nt_native_path ()->Length) | |
2201 | { | |
2202 | res = 0; | |
2203 | goto out; | |
2204 | } | |
2205 | if (*(PWCHAR) ((PBYTE) newpc.get_nt_native_path ()->Buffer | |
2206 | + oldpc.get_nt_native_path ()->Length) == L'\\') | |
2207 | { | |
2208 | set_errno (EINVAL); | |
2209 | goto out; | |
2210 | } | |
fbae2bf8 | 2211 | } |
c5a4eacc | 2212 | } |
9235f3ea | 2213 | else if (!newpc.exists ()) |
1fd5e000 | 2214 | { |
349fba0c | 2215 | if (equal_path && old_explicit_suffix != new_explicit_suffix) |
9235f3ea CV |
2216 | { |
2217 | newpc.check (newpath, PC_SYM_NOFOLLOW); | |
2218 | if (RtlEqualUnicodeString (oldpc.get_nt_native_path (), | |
2219 | newpc.get_nt_native_path (), | |
e4b57503 | 2220 | oldpc.objcaseinsensitive ())) |
9235f3ea CV |
2221 | { |
2222 | res = 0; | |
2223 | goto out; | |
2224 | } | |
2225 | } | |
ce9898da | 2226 | else if (oldpc.is_lnk_special () |
9235f3ea | 2227 | && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (), |
8deb4118 | 2228 | &ro_u_lnk, TRUE)) |
70300fdb | 2229 | rename_append_suffix (newpc, newpath, nlen, ".lnk"); |
0ea2c199 | 2230 | else if (oldpc.is_binary () && !old_explicit_suffix |
8e75995c | 2231 | && oldpc.known_suffix |
b36d8c46 | 2232 | && !nt_path_has_executable_suffix (newpc.get_nt_native_path ())) |
8e75995c CV |
2233 | /* Never append .exe suffix if oldpath had .exe suffix given |
2234 | explicitely, or if oldpath wasn't already a .exe file, or | |
2235 | if the destination filename has one of the blessed executable | |
2236 | suffixes. | |
2237 | Note: To rename an executable foo.exe to bar-without-suffix, | |
2238 | the .exe suffix must be given explicitly in oldpath. */ | |
70300fdb | 2239 | rename_append_suffix (newpc, newpath, nlen, ".exe"); |
1fd5e000 | 2240 | } |
bc28fe95 | 2241 | else |
1fd5e000 | 2242 | { |
349fba0c | 2243 | if (equal_path && old_explicit_suffix != new_explicit_suffix) |
1ff9f4b9 | 2244 | { |
9235f3ea CV |
2245 | newpc.check (newpath, PC_SYM_NOFOLLOW); |
2246 | if (RtlEqualUnicodeString (oldpc.get_nt_native_path (), | |
2247 | newpc.get_nt_native_path (), | |
e4b57503 | 2248 | oldpc.objcaseinsensitive ())) |
9235f3ea CV |
2249 | { |
2250 | res = 0; | |
2251 | goto out; | |
2252 | } | |
2253 | } | |
ce9898da | 2254 | else if (oldpc.is_lnk_special ()) |
70300fdb | 2255 | { |
ce9898da | 2256 | if (!newpc.is_lnk_special () |
9235f3ea | 2257 | && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (), |
8deb4118 | 2258 | &ro_u_lnk, TRUE)) |
9235f3ea CV |
2259 | { |
2260 | rename_append_suffix (new2pc, newpath, nlen, ".lnk"); | |
2261 | removepc = &newpc; | |
2262 | } | |
bc28fe95 | 2263 | } |
9235f3ea | 2264 | else if (oldpc.is_binary ()) |
34f5d087 | 2265 | { |
8e75995c CV |
2266 | /* Never append .exe suffix if oldpath had .exe suffix given |
2267 | explicitely, or if newfile is a binary (in which case the given | |
18d0c0fb | 2268 | name probably makes sense as it is), or if the destination |
8e75995c CV |
2269 | filename has one of the blessed executable suffixes. */ |
2270 | if (!old_explicit_suffix && oldpc.known_suffix | |
2271 | && !newpc.is_binary () | |
92ddb742 | 2272 | && !nt_path_has_executable_suffix (newpc.get_nt_native_path ())) |
9235f3ea CV |
2273 | { |
2274 | rename_append_suffix (new2pc, newpath, nlen, ".exe"); | |
2275 | removepc = &newpc; | |
2276 | } | |
70300fdb | 2277 | } |
9235f3ea | 2278 | else |
70300fdb | 2279 | { |
18d0c0fb CV |
2280 | /* If the new path is an existing .lnk symlink or a .exe file, |
2281 | but the new path has not been specified with explicit suffix, | |
2282 | rename to the new name without suffix, as expected, but also | |
2283 | remove the clashing symlink or executable. Did I ever mention | |
2284 | how I hate the file suffix idea? */ | |
2285 | if ((newpc.is_lnk_special () | |
9235f3ea | 2286 | || RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (), |
8deb4118 | 2287 | &ro_u_exe, TRUE)) |
9235f3ea CV |
2288 | && !new_explicit_suffix) |
2289 | { | |
2290 | new2pc.check (newpath, PC_SYM_NOFOLLOW, stat_suffixes); | |
2291 | newpc.get_nt_native_path ()->Length -= 4 * sizeof (WCHAR); | |
ce9898da | 2292 | if (new2pc.is_binary () || new2pc.is_lnk_special ()) |
9235f3ea CV |
2293 | removepc = &new2pc; |
2294 | } | |
75fbcb44 | 2295 | } |
1fd5e000 | 2296 | } |
9235f3ea | 2297 | dstpc = (removepc == &newpc) ? &new2pc : &newpc; |
2bee37bc | 2298 | |
34ce8088 CV |
2299 | /* Check cross-device before touching anything. Otherwise we might end |
2300 | up with an unlinked target dir even if the actual rename didn't work. */ | |
2301 | if (oldpc.fs_type () != dstpc->fs_type () | |
2302 | || oldpc.fs_serial_number () != dstpc->fs_serial_number ()) | |
2303 | { | |
2304 | set_errno (EXDEV); | |
2305 | goto out; | |
2306 | } | |
2307 | ||
4c153b24 CV |
2308 | /* Opening the file must be part of the transaction. It's not sufficient |
2309 | to call only NtSetInformationFile under the transaction. Therefore we | |
2310 | have to start the transaction here, if necessary. */ | |
2311 | if (wincap.has_transactions () | |
b6125160 | 2312 | && (dstpc->fs_flags () & FILE_SUPPORTS_TRANSACTIONS) |
2f33b799 CV |
2313 | && (dstpc->isdir () |
2314 | || (!removepc && dstpc->has_attribute (FILE_ATTRIBUTE_READONLY)))) | |
4c153b24 CV |
2315 | start_transaction (old_trans, trans); |
2316 | ||
f7dea7f2 CV |
2317 | int retry_count; |
2318 | retry_count = 0; | |
bc8a5a9f | 2319 | retry: |
a1c35b8b CV |
2320 | /* Talking about inconsistent behaviour... |
2321 | - DELETE is required to rename a file. So far, so good. | |
2322 | - At least one cifs FS (Tru64) needs FILE_READ_ATTRIBUTE, otherwise the | |
2323 | FileRenameInformation call fails with STATUS_ACCESS_DENIED. However, | |
2324 | on NFS we get a STATUS_ACCESS_DENIED if FILE_READ_ATTRIBUTE is used | |
2325 | and the file we try to rename is a symlink. Urgh. | |
2326 | - Samba (only some versions?) doesn't like the FILE_SHARE_DELETE mode if | |
2327 | the file has the R/O attribute set and returns STATUS_ACCESS_DENIED in | |
2328 | that case. */ | |
2329 | { | |
2330 | ULONG access = DELETE | (oldpc.fs_is_cifs () ? FILE_READ_ATTRIBUTES : 0); | |
2331 | ULONG sharing = FILE_SHARE_READ | FILE_SHARE_WRITE | |
2332 | | (oldpc.fs_is_samba () ? 0 : FILE_SHARE_DELETE); | |
2333 | ULONG flags = FILE_OPEN_FOR_BACKUP_INTENT | |
2334 | | (oldpc.is_rep_symlink () ? FILE_OPEN_REPARSE_POINT : 0); | |
2335 | status = NtOpenFile (&fh, access, | |
2336 | oldpc.get_object_attr (attr, sec_none_nih), | |
2337 | &io, sharing, flags); | |
2338 | } | |
9235f3ea CV |
2339 | if (!NT_SUCCESS (status)) |
2340 | { | |
bc8a5a9f CV |
2341 | debug_printf ("status %p", status); |
2342 | if (status == STATUS_SHARING_VIOLATION | |
962f9a2c | 2343 | && cygwait (10L) != WAIT_SIGNALED) |
bc8a5a9f CV |
2344 | { |
2345 | /* Typical BLODA problem. Some virus scanners check newly generated | |
2346 | files and while doing that disallow DELETE access. That's really | |
2347 | bad because it breaks applications which copy files by creating | |
2348 | a temporary filename and then rename the temp filename to the | |
2349 | target filename. This renaming fails due to the jealous virus | |
2350 | scanner and the application fails to create the target file. | |
92ddb742 | 2351 | |
bc8a5a9f | 2352 | This kludge tries to work around that by yielding until the |
f7dea7f2 CV |
2353 | sharing violation goes away, or a signal arrived, or after |
2354 | about a second, give or take. */ | |
2355 | if (++retry_count < 40) | |
2356 | { | |
2357 | yield (); | |
2358 | goto retry; | |
2359 | } | |
bc8a5a9f | 2360 | } |
9235f3ea CV |
2361 | __seterrno_from_nt_status (status); |
2362 | goto out; | |
2363 | } | |
349fba0c CV |
2364 | |
2365 | /* Renaming a dir to another, existing dir fails always, even if | |
2366 | ReplaceIfExists is set to TRUE and the existing dir is empty. So | |
2367 | we have to remove the destination dir first. This also covers the | |
2368 | case that the destination directory is not empty. In that case, | |
2369 | unlink_nt returns with STATUS_DIRECTORY_NOT_EMPTY. */ | |
2370 | if (dstpc->isdir ()) | |
2371 | { | |
2372 | status = unlink_nt (*dstpc); | |
2373 | if (!NT_SUCCESS (status)) | |
2374 | { | |
2375 | __seterrno_from_nt_status (status); | |
2376 | goto out; | |
2377 | } | |
2378 | } | |
2379 | /* You can't copy a file if the destination exists and has the R/O | |
78ee2ae1 CV |
2380 | attribute set. Remove the R/O attribute first. But first check |
2381 | if a removepc exists. If so, dstpc points to a non-existing file | |
2382 | due to a mangled suffix. */ | |
2383 | else if (!removepc && dstpc->has_attribute (FILE_ATTRIBUTE_READONLY)) | |
349fba0c CV |
2384 | { |
2385 | status = NtOpenFile (&nfh, FILE_WRITE_ATTRIBUTES, | |
2386 | dstpc->get_object_attr (attr, sec_none_nih), | |
2387 | &io, FILE_SHARE_VALID_FLAGS, | |
2388 | FILE_OPEN_FOR_BACKUP_INTENT | |
2389 | | (dstpc->is_rep_symlink () | |
2390 | ? FILE_OPEN_REPARSE_POINT : 0)); | |
2391 | if (!NT_SUCCESS (status)) | |
70300fdb | 2392 | { |
349fba0c CV |
2393 | __seterrno_from_nt_status (status); |
2394 | goto out; | |
2395 | } | |
669bdeb8 CV |
2396 | status = NtSetAttributesFile (nfh, dstpc->file_attributes () |
2397 | & ~FILE_ATTRIBUTE_READONLY); | |
349fba0c CV |
2398 | NtClose (nfh); |
2399 | if (!NT_SUCCESS (status)) | |
70300fdb | 2400 | { |
349fba0c CV |
2401 | __seterrno_from_nt_status (status); |
2402 | goto out; | |
2403 | } | |
2404 | } | |
2405 | ||
e84b3704 CV |
2406 | /* SUSv3: If the old argument and the new argument resolve to the same |
2407 | existing file, rename() shall return successfully and perform no | |
2408 | other action. | |
71421974 CV |
2409 | The test tries to be as quick as possible. Due to the above cross device |
2410 | check we already know both files are on the same device. So it just | |
2411 | tests if oldpath has more than 1 hardlink, then it opens newpath | |
59c7f5bc CV |
2412 | and tests for identical file ids. If so, oldpath and newpath refer to |
2413 | the same file. */ | |
ae08b378 | 2414 | if ((removepc || dstpc->exists ()) |
349fba0c | 2415 | && !oldpc.isdir () |
e84b3704 CV |
2416 | && NT_SUCCESS (NtQueryInformationFile (fh, &io, &ofsi, sizeof ofsi, |
2417 | FileStandardInformation)) | |
2418 | && ofsi.NumberOfLinks > 1 | |
ae08b378 CV |
2419 | && NT_SUCCESS (NtOpenFile (&nfh, READ_CONTROL, |
2420 | (removepc ?: dstpc)->get_object_attr (attr, sec_none_nih), | |
e809fec9 CV |
2421 | &io, FILE_SHARE_VALID_FLAGS, |
2422 | FILE_OPEN_FOR_BACKUP_INTENT | |
2423 | | ((removepc ?: dstpc)->is_rep_symlink () | |
70300fdb | 2424 | ? FILE_OPEN_REPARSE_POINT : 0)))) |
ae08b378 | 2425 | { |
ae08b378 CV |
2426 | FILE_INTERNAL_INFORMATION ofii, nfii; |
2427 | ||
e84b3704 CV |
2428 | if (NT_SUCCESS (NtQueryInformationFile (fh, &io, &ofii, sizeof ofii, |
2429 | FileInternalInformation)) | |
2430 | && NT_SUCCESS (NtQueryInformationFile (nfh, &io, &nfii, sizeof nfii, | |
2431 | FileInternalInformation)) | |
59c7f5bc | 2432 | && ofii.FileId.QuadPart == nfii.FileId.QuadPart) |
ae08b378 CV |
2433 | { |
2434 | debug_printf ("%s and %s are the same file", oldpath, newpath); | |
2435 | NtClose (nfh); | |
ae08b378 CV |
2436 | res = 0; |
2437 | goto out; | |
2438 | } | |
2439 | NtClose (nfh); | |
2440 | } | |
71421974 CV |
2441 | /* Create FILE_RENAME_INFORMATION struct. Using a tmp_pathbuf area allows |
2442 | for paths of up to 32757 chars. This test is just for paranoia's sake. */ | |
2443 | if (dstpc->get_nt_native_path ()->Length > NT_MAX_PATH * sizeof (WCHAR) | |
2444 | - sizeof (FILE_RENAME_INFORMATION)) | |
2445 | { | |
2446 | debug_printf ("target filename too long"); | |
2447 | set_errno (EINVAL); | |
2448 | goto out; | |
2449 | } | |
2450 | pfri = (PFILE_RENAME_INFORMATION) tp.w_get (); | |
2451 | pfri->ReplaceIfExists = TRUE; | |
2452 | pfri->RootDirectory = NULL; | |
a32a9a87 CV |
2453 | pfri->FileNameLength = dstpc->get_nt_native_path ()->Length; |
2454 | memcpy (&pfri->FileName, dstpc->get_nt_native_path ()->Buffer, | |
2455 | pfri->FileNameLength); | |
71421974 CV |
2456 | status = NtSetInformationFile (fh, &io, pfri, |
2457 | sizeof *pfri + pfri->FileNameLength, | |
2458 | FileRenameInformation); | |
349fba0c CV |
2459 | /* This happens if the access rights don't allow deleting the destination. |
2460 | Even if the handle to the original file is opened with BACKUP | |
2461 | and/or RECOVERY, these flags don't apply to the destination of the | |
2462 | rename operation. So, a privileged user can't rename a file to an | |
2463 | existing file, if the permissions of the existing file aren't right. | |
2464 | Like directories, we have to handle this separately by removing the | |
2465 | destination before renaming. */ | |
4c153b24 CV |
2466 | if (status == STATUS_ACCESS_DENIED && dstpc->exists () && !dstpc->isdir ()) |
2467 | { | |
b6125160 CV |
2468 | if (wincap.has_transactions () |
2469 | && (dstpc->fs_flags () & FILE_SUPPORTS_TRANSACTIONS) | |
2470 | && !trans) | |
4c153b24 CV |
2471 | { |
2472 | start_transaction (old_trans, trans); | |
2473 | /* As mentioned earlier, opening the file must be part of the | |
2474 | transaction. Therefore we have to reopen the file here if the | |
2475 | transaction hasn't been started already. Unfortunately we can't | |
2476 | use the NT "reopen file from existing handle" feature. In that | |
2477 | case NtOpenFile returns STATUS_TRANSACTIONAL_CONFLICT. We *have* | |
2478 | to close the handle to the file first, *then* we can re-open it. | |
2479 | Fortunately nothing has happened yet, so the atomicity of the | |
2480 | rename functionality is not spoiled. */ | |
2481 | NtClose (fh); | |
2482 | status = NtOpenFile (&fh, DELETE, | |
2483 | oldpc.get_object_attr (attr, sec_none_nih), | |
2484 | &io, FILE_SHARE_VALID_FLAGS, | |
2485 | FILE_OPEN_FOR_BACKUP_INTENT | |
2486 | | (oldpc.is_rep_symlink () | |
2487 | ? FILE_OPEN_REPARSE_POINT : 0)); | |
2488 | if (!NT_SUCCESS (status)) | |
2489 | { | |
2490 | __seterrno_from_nt_status (status); | |
2491 | goto out; | |
2492 | } | |
2493 | } | |
2494 | if (NT_SUCCESS (status = unlink_nt (*dstpc))) | |
71421974 CV |
2495 | status = NtSetInformationFile (fh, &io, pfri, |
2496 | sizeof *pfri + pfri->FileNameLength, | |
4c153b24 CV |
2497 | FileRenameInformation); |
2498 | } | |
9235f3ea CV |
2499 | if (NT_SUCCESS (status)) |
2500 | { | |
2501 | if (removepc) | |
2502 | unlink_nt (*removepc); | |
2503 | res = 0; | |
2504 | } | |
2505 | else | |
349fba0c | 2506 | __seterrno_from_nt_status (status); |
1fd5e000 | 2507 | |
9235f3ea | 2508 | out: |
349fba0c CV |
2509 | if (fh) |
2510 | NtClose (fh); | |
4c153b24 CV |
2511 | if (wincap.has_transactions () && trans) |
2512 | stop_transaction (status, old_trans, trans); | |
b9aa8149 | 2513 | syscall_printf ("%R = rename(%s, %s)", res, oldpath, newpath); |
1fd5e000 CF |
2514 | return res; |
2515 | } | |
2516 | ||
90fe7739 | 2517 | extern "C" int |
1fd5e000 CF |
2518 | system (const char *cmdstring) |
2519 | { | |
e14328f4 TP |
2520 | pthread_testcancel (); |
2521 | ||
893ac8e0 CF |
2522 | myfault efault; |
2523 | if (efault.faulted (EFAULT)) | |
da8f3291 CF |
2524 | return -1; |
2525 | ||
1fd5e000 CF |
2526 | int res; |
2527 | const char* command[4]; | |
1fd5e000 | 2528 | |
893ac8e0 | 2529 | if (cmdstring == NULL) |
d1fb625d | 2530 | return 1; |
1fd5e000 CF |
2531 | |
2532 | command[0] = "sh"; | |
2533 | command[1] = "-c"; | |
2534 | command[2] = cmdstring; | |
2535 | command[3] = (const char *) NULL; | |
2536 | ||
48befd83 | 2537 | if ((res = spawnvp (_P_SYSTEM, "/bin/sh", command)) == -1) |
1fd5e000 CF |
2538 | { |
2539 | // when exec fails, return value should be as if shell | |
2540 | // executed exit (127) | |
2541 | res = 127; | |
2542 | } | |
2543 | ||
1fd5e000 CF |
2544 | return res; |
2545 | } | |
2546 | ||
4e2a97b2 | 2547 | extern "C" int |
1fd5e000 CF |
2548 | setdtablesize (int size) |
2549 | { | |
0381fec6 | 2550 | if (size <= (int)cygheap->fdtab.size || cygheap->fdtab.extend (size - cygheap->fdtab.size)) |
4e2a97b2 CF |
2551 | return 0; |
2552 | ||
2553 | return -1; | |
1fd5e000 CF |
2554 | } |
2555 | ||
90fe7739 | 2556 | extern "C" int |
1fd5e000 CF |
2557 | getdtablesize () |
2558 | { | |
1457739a | 2559 | return cygheap->fdtab.size > OPEN_MAX ? cygheap->fdtab.size : OPEN_MAX; |
1fd5e000 CF |
2560 | } |
2561 | ||
7b9f3ce0 | 2562 | extern "C" int |
1fd5e000 CF |
2563 | getpagesize () |
2564 | { | |
c29e6933 | 2565 | return (size_t) wincap.allocation_granularity (); |
1fd5e000 CF |
2566 | } |
2567 | ||
2568 | /* FIXME: not all values are correct... */ | |
90fe7739 | 2569 | extern "C" long int |
1fd5e000 CF |
2570 | fpathconf (int fd, int v) |
2571 | { | |
df63bd49 CF |
2572 | cygheap_fdget cfd (fd); |
2573 | if (cfd < 0) | |
2574 | return -1; | |
86bc8fad | 2575 | return cfd->fpathconf (v); |
1fd5e000 CF |
2576 | } |
2577 | ||
90fe7739 | 2578 | extern "C" long int |
1fd5e000 CF |
2579 | pathconf (const char *file, int v) |
2580 | { | |
86bc8fad | 2581 | fhandler_base *fh; |
7a9f309b | 2582 | long ret = -1; |
86bc8fad | 2583 | |
893ac8e0 CF |
2584 | myfault efault; |
2585 | if (efault.faulted (EFAULT)) | |
2586 | return -1; | |
7a9f309b | 2587 | |
893ac8e0 CF |
2588 | if (!*file) |
2589 | { | |
2590 | set_errno (ENOENT); | |
2591 | return -1; | |
2592 | } | |
4ee93264 | 2593 | if (!(fh = build_fh_name (file, PC_SYM_FOLLOW, stat_suffixes))) |
86bc8fad CV |
2594 | return -1; |
2595 | if (!fh->exists ()) | |
7a9f309b CV |
2596 | set_errno (ENOENT); |
2597 | else | |
2598 | ret = fh->fpathconf (v); | |
2599 | delete fh; | |
2600 | return ret; | |
1fd5e000 CF |
2601 | } |
2602 | ||
93d66ddc CV |
2603 | extern "C" int |
2604 | ttyname_r (int fd, char *buf, size_t buflen) | |
2605 | { | |
2606 | int ret = 0; | |
893ac8e0 CF |
2607 | myfault efault; |
2608 | if (efault.faulted ()) | |
2609 | ret = EFAULT; | |
93d66ddc CV |
2610 | else |
2611 | { | |
2612 | cygheap_fdget cfd (fd, true); | |
2613 | if (cfd < 0) | |
2614 | ret = EBADF; | |
2615 | else if (!cfd->is_tty ()) | |
2616 | ret = ENOTTY; | |
2617 | else if (buflen < strlen (cfd->ttyname ()) + 1) | |
2618 | ret = ERANGE; | |
2619 | else | |
e3778517 | 2620 | strcpy (buf, cfd->ttyname ()); |
93d66ddc CV |
2621 | } |
2622 | debug_printf ("returning %d tty: %s", ret, ret ? "NULL" : buf); | |
2623 | return ret; | |
2624 | } | |
2625 | ||
b0e82b74 CF |
2626 | extern "C" char * |
2627 | ttyname (int fd) | |
2628 | { | |
d8a5f046 CV |
2629 | static char name[TTY_NAME_MAX]; |
2630 | int ret = ttyname_r (fd, name, TTY_NAME_MAX); | |
93d66ddc CV |
2631 | if (ret) |
2632 | { | |
2633 | set_errno (ret); | |
2634 | return NULL; | |
2635 | } | |
3c1e8187 | 2636 | return name; |
b0e82b74 CF |
2637 | } |
2638 | ||
84c7d409 | 2639 | extern "C" char * |
1fd5e000 CF |
2640 | ctermid (char *str) |
2641 | { | |
1fd5e000 | 2642 | if (str == NULL) |
44d2fc0a CF |
2643 | str = _my_tls.locals.ttybuf; |
2644 | if (myself->ctty < 0) | |
2645 | strcpy (str, "no tty"); | |
1fd5e000 | 2646 | else |
44d2fc0a CF |
2647 | { |
2648 | device d; | |
2649 | d.parse (myself->ctty); | |
2650 | strcpy (str, d.name); | |
2651 | } | |
1fd5e000 CF |
2652 | return str; |
2653 | } | |
2654 | ||
56cd25ee | 2655 | /* Tells stdio if it should do the cr/lf conversion for this file */ |
b0e82b74 | 2656 | extern "C" int |
56cd25ee DD |
2657 | _cygwin_istext_for_stdio (int fd) |
2658 | { | |
56cd25ee DD |
2659 | if (CYGWIN_VERSION_OLD_STDIO_CRLF_HANDLING) |
2660 | { | |
5e0f482f | 2661 | syscall_printf ("fd %d: old API", fd); |
56cd25ee DD |
2662 | return 0; /* we do it for old apps, due to getc/putc macros */ |
2663 | } | |
2664 | ||
0f4db8cb | 2665 | cygheap_fdget cfd (fd, false, false); |
df63bd49 | 2666 | if (cfd < 0) |
56cd25ee | 2667 | { |
5e0f482f | 2668 | syscall_printf ("fd %d: not open", fd); |
56cd25ee DD |
2669 | return 0; |
2670 | } | |
2671 | ||
5e0f482f | 2672 | #if 0 |
7ac61736 | 2673 | if (cfd->get_device () != FH_FS) |
56cd25ee | 2674 | { |
5e0f482f | 2675 | syscall_printf ("fd not disk file. Defaulting to binary."); |
56cd25ee DD |
2676 | return 0; |
2677 | } | |
5e0f482f | 2678 | #endif |
56cd25ee | 2679 | |
56551a9b | 2680 | if (cfd->wbinary () || cfd->rbinary ()) |
56cd25ee | 2681 | { |
5e0f482f | 2682 | syscall_printf ("fd %d: opened as binary", fd); |
56cd25ee DD |
2683 | return 0; |
2684 | } | |
2685 | ||
5e0f482f | 2686 | syscall_printf ("fd %d: defaulting to text", fd); |
56cd25ee DD |
2687 | return 1; |
2688 | } | |
2689 | ||
ed8387fb | 2690 | /* internal newlib function */ |
0312ede4 | 2691 | extern "C" int _fwalk (struct _reent *ptr, int (*function) (FILE *)); |
ed8387fb | 2692 | |
ed8387fb DD |
2693 | static int |
2694 | setmode_helper (FILE *f) | |
2695 | { | |
9a917772 | 2696 | if (fileno (f) != _my_tls.locals.setmode_file) |
5e0f482f | 2697 | { |
9a917772 | 2698 | syscall_printf ("improbable, but %d != %d", fileno (f), _my_tls.locals.setmode_file); |
5e0f482f CF |
2699 | return 0; |
2700 | } | |
2701 | syscall_printf ("file was %s now %s", f->_flags & __SCLE ? "text" : "binary", | |
9a917772 CF |
2702 | _my_tls.locals.setmode_mode & O_TEXT ? "text" : "binary"); |
2703 | if (_my_tls.locals.setmode_mode & O_TEXT) | |
ed8387fb DD |
2704 | f->_flags |= __SCLE; |
2705 | else | |
2706 | f->_flags &= ~__SCLE; | |
2707 | return 0; | |
2708 | } | |
2709 | ||
bd4ec496 CF |
2710 | extern "C" int |
2711 | getmode (int fd) | |
2712 | { | |
df63bd49 CF |
2713 | cygheap_fdget cfd (fd); |
2714 | if (cfd < 0) | |
2715 | return -1; | |
bd4ec496 | 2716 | |
df63bd49 | 2717 | return cfd->get_flags () & (O_BINARY | O_TEXT); |
bd4ec496 CF |
2718 | } |
2719 | ||
1fd5e000 CF |
2720 | /* Set a file descriptor into text or binary mode, returning the |
2721 | previous mode. */ | |
2722 | ||
bd4ec496 | 2723 | extern "C" int |
1fd5e000 CF |
2724 | setmode (int fd, int mode) |
2725 | { | |
df63bd49 CF |
2726 | cygheap_fdget cfd (fd); |
2727 | if (cfd < 0) | |
2728 | return -1; | |
f940c5b1 | 2729 | if (mode != O_BINARY && mode != O_TEXT && mode != 0) |
1fd5e000 CF |
2730 | { |
2731 | set_errno (EINVAL); | |
2732 | return -1; | |
2733 | } | |
2734 | ||
1fd5e000 CF |
2735 | /* Note that we have no way to indicate the case that writes are |
2736 | binary but not reads, or vice-versa. These cases can arise when | |
2737 | using the tty or console interface. People using those | |
2738 | interfaces should not use setmode. */ | |
2739 | ||
2740 | int res; | |
56551a9b | 2741 | if (cfd->wbinary () && cfd->rbinary ()) |
1fd5e000 | 2742 | res = O_BINARY; |
56551a9b | 2743 | else if (cfd->wbinset () && cfd->rbinset ()) |
9cf9c146 | 2744 | res = O_TEXT; /* Specifically set O_TEXT */ |
1fd5e000 | 2745 | else |
9cf9c146 | 2746 | res = 0; |
1fd5e000 | 2747 | |
f3ea62a8 | 2748 | if (!mode) |
df63bd49 | 2749 | cfd->reset_to_open_binmode (); |
f3ea62a8 | 2750 | else |
e35f391f | 2751 | cfd->set_flags ((cfd->get_flags () & ~(O_TEXT | O_BINARY)) | mode); |
1fd5e000 | 2752 | |
244191b6 | 2753 | syscall_printf ("(%d<%S>, %p) returning %s", fd, |
c4bd8377 CV |
2754 | cfd->pc.get_nt_native_path (), mode, |
2755 | res & O_TEXT ? "text" : "binary"); | |
1fd5e000 CF |
2756 | return res; |
2757 | } | |
2758 | ||
9a917772 CF |
2759 | extern "C" int |
2760 | cygwin_setmode (int fd, int mode) | |
2761 | { | |
2762 | int res = setmode (fd, mode); | |
2763 | if (res != -1) | |
2764 | { | |
2765 | _my_tls.locals.setmode_file = fd; | |
2766 | if (_cygwin_istext_for_stdio (fd)) | |
2767 | _my_tls.locals.setmode_mode = O_TEXT; | |
2768 | else | |
2769 | _my_tls.locals.setmode_mode = O_BINARY; | |
2770 | _fwalk (_GLOBAL_REENT, setmode_helper); | |
2771 | } | |
2772 | return res; | |
2773 | } | |
2774 | ||
7636b585 CV |
2775 | extern "C" int |
2776 | posix_fadvise (int fd, _off64_t offset, _off64_t len, int advice) | |
2777 | { | |
2778 | int res = -1; | |
2779 | cygheap_fdget cfd (fd); | |
2780 | if (cfd >= 0) | |
2781 | res = cfd->fadvise (offset, len, advice); | |
2782 | else | |
2783 | set_errno (EBADF); | |
b9aa8149 | 2784 | syscall_printf ("%R = posix_fadvice(%d, %D, %D, %d)", |
a10c6f03 | 2785 | res, fd, offset, len, advice); |
7636b585 CV |
2786 | return res; |
2787 | } | |
2788 | ||
2789 | extern "C" int | |
2790 | posix_fallocate (int fd, _off64_t offset, _off64_t len) | |
2791 | { | |
2792 | int res = -1; | |
2793 | if (offset < 0 || len == 0) | |
2794 | set_errno (EINVAL); | |
2795 | else | |
2796 | { | |
2797 | cygheap_fdget cfd (fd); | |
2798 | if (cfd >= 0) | |
2799 | res = cfd->ftruncate (offset + len, false); | |
2800 | else | |
2801 | set_errno (EBADF); | |
2802 | } | |
b9aa8149 | 2803 | syscall_printf ("%R = posix_fallocate(%d, %D, %D)", res, fd, offset, len); |
7636b585 CV |
2804 | return res; |
2805 | } | |
2806 | ||
b0e82b74 | 2807 | extern "C" int |
1727fba0 | 2808 | ftruncate64 (int fd, _off64_t length) |
1fd5e000 | 2809 | { |
3fd68a6a CV |
2810 | int res = -1; |
2811 | cygheap_fdget cfd (fd); | |
2812 | if (cfd >= 0) | |
7636b585 | 2813 | res = cfd->ftruncate (length, true); |
1fd5e000 | 2814 | else |
3fd68a6a | 2815 | set_errno (EBADF); |
b9aa8149 | 2816 | syscall_printf ("%R = ftruncate(%d, %D)", res, fd, length); |
1fd5e000 CF |
2817 | return res; |
2818 | } | |
2819 | ||
acb56175 CV |
2820 | /* ftruncate: P96 5.6.7.1 */ |
2821 | extern "C" int | |
1727fba0 | 2822 | ftruncate (int fd, _off_t length) |
acb56175 | 2823 | { |
1727fba0 | 2824 | return ftruncate64 (fd, (_off64_t)length); |
acb56175 CV |
2825 | } |
2826 | ||
1fd5e000 | 2827 | /* truncate: Provided by SVR4 and 4.3+BSD. Not part of POSIX.1 or XPG3 */ |
b0e82b74 | 2828 | extern "C" int |
1727fba0 | 2829 | truncate64 (const char *pathname, _off64_t length) |
1fd5e000 CF |
2830 | { |
2831 | int fd; | |
2832 | int res = -1; | |
2833 | ||
2834 | fd = open (pathname, O_RDWR); | |
2835 | ||
576bfeec | 2836 | if (fd != -1) |
1fd5e000 | 2837 | { |
59149930 | 2838 | res = ftruncate64 (fd, length); |
1fd5e000 CF |
2839 | close (fd); |
2840 | } | |
b9aa8149 | 2841 | syscall_printf ("%R = truncate(%s, %D)", res, pathname, length); |
1fd5e000 CF |
2842 | |
2843 | return res; | |
2844 | } | |
2845 | ||
acb56175 CV |
2846 | /* truncate: Provided by SVR4 and 4.3+BSD. Not part of POSIX.1 or XPG3 */ |
2847 | extern "C" int | |
1727fba0 | 2848 | truncate (const char *pathname, _off_t length) |
acb56175 | 2849 | { |
1727fba0 | 2850 | return truncate64 (pathname, (_off64_t)length); |
acb56175 CV |
2851 | } |
2852 | ||
b0e82b74 | 2853 | extern "C" long |
1fd5e000 CF |
2854 | get_osfhandle (int fd) |
2855 | { | |
df63bd49 | 2856 | long res; |
1fd5e000 | 2857 | |
df63bd49 CF |
2858 | cygheap_fdget cfd (fd); |
2859 | if (cfd >= 0) | |
2860 | res = (long) cfd->get_handle (); | |
1fd5e000 | 2861 | else |
df63bd49 | 2862 | res = -1; |
1fd5e000 | 2863 | |
b9aa8149 | 2864 | syscall_printf ("%R = get_osfhandle(%d)", res, fd); |
1fd5e000 CF |
2865 | return res; |
2866 | } | |
2867 | ||
b0e82b74 | 2868 | extern "C" int |
3323df7e | 2869 | fstatvfs (int fd, struct statvfs *sfs) |
1fd5e000 | 2870 | { |
893ac8e0 CF |
2871 | myfault efault; |
2872 | if (efault.faulted (EFAULT)) | |
a652e6d5 CV |
2873 | return -1; |
2874 | ||
3323df7e CV |
2875 | cygheap_fdget cfd (fd); |
2876 | if (cfd < 0) | |
2877 | return -1; | |
2878 | return cfd->fstatvfs (sfs); | |
2879 | } | |
56551a9b | 2880 | |
3323df7e CV |
2881 | extern "C" int |
2882 | statvfs (const char *name, struct statvfs *sfs) | |
2883 | { | |
2884 | int res = -1; | |
2885 | fhandler_base *fh = NULL; | |
1fd5e000 | 2886 | |
3323df7e CV |
2887 | myfault efault; |
2888 | if (efault.faulted (EFAULT)) | |
2889 | goto error; | |
1fd5e000 | 2890 | |
dd442880 | 2891 | if (!(fh = build_fh_name (name, PC_SYM_FOLLOW, stat_suffixes))) |
3323df7e | 2892 | goto error; |
ada0376f | 2893 | |
3323df7e | 2894 | if (fh->error ()) |
ec3b136a | 2895 | { |
3323df7e CV |
2896 | debug_printf ("got %d error from build_fh_name", fh->error ()); |
2897 | set_errno (fh->error ()); | |
a80229fd | 2898 | } |
3323df7e CV |
2899 | else if (fh->exists ()) |
2900 | { | |
2901 | debug_printf ("(%s, %p), file_attributes %d", name, sfs, (DWORD) *fh); | |
2902 | res = fh->fstatvfs (sfs); | |
2903 | } | |
2904 | else | |
2905 | set_errno (ENOENT); | |
a80229fd | 2906 | |
3323df7e CV |
2907 | delete fh; |
2908 | error: | |
2909 | MALLOC_CHECK; | |
b9aa8149 | 2910 | syscall_printf ("%R = statvfs(%s,%p)", res, name, sfs); |
3323df7e | 2911 | return res; |
1fd5e000 CF |
2912 | } |
2913 | ||
a652e6d5 | 2914 | extern "C" int |
3323df7e | 2915 | fstatfs (int fd, struct statfs *sfs) |
a652e6d5 | 2916 | { |
3323df7e CV |
2917 | struct statvfs vfs; |
2918 | int ret = fstatvfs (fd, &vfs); | |
2919 | if (!ret) | |
2920 | { | |
2921 | sfs->f_type = vfs.f_flag; | |
2922 | sfs->f_bsize = vfs.f_bsize; | |
2923 | sfs->f_blocks = vfs.f_blocks; | |
2924 | sfs->f_bavail = vfs.f_bavail; | |
2925 | sfs->f_bfree = vfs.f_bfree; | |
2926 | sfs->f_files = -1; | |
2927 | sfs->f_ffree = -1; | |
2928 | sfs->f_fsid = vfs.f_fsid; | |
2929 | sfs->f_namelen = vfs.f_namemax; | |
2930 | } | |
2931 | return ret; | |
a652e6d5 CV |
2932 | } |
2933 | ||
2934 | extern "C" int | |
2935 | statfs (const char *fname, struct statfs *sfs) | |
2936 | { | |
a652e6d5 CV |
2937 | struct statvfs vfs; |
2938 | int ret = statvfs (fname, &vfs); | |
2939 | if (!ret) | |
2940 | { | |
2941 | sfs->f_type = vfs.f_flag; | |
2942 | sfs->f_bsize = vfs.f_bsize; | |
2943 | sfs->f_blocks = vfs.f_blocks; | |
2944 | sfs->f_bavail = vfs.f_bavail; | |
2945 | sfs->f_bfree = vfs.f_bfree; | |
2946 | sfs->f_files = -1; | |
2947 | sfs->f_ffree = -1; | |
2948 | sfs->f_fsid = vfs.f_fsid; | |
2949 | sfs->f_namelen = vfs.f_namemax; | |
2950 | } | |
2951 | return ret; | |
2952 | } | |
2953 | ||
1fd5e000 | 2954 | /* setpgid: POSIX 4.3.3.1 */ |
b0e82b74 | 2955 | extern "C" int |
1fd5e000 CF |
2956 | setpgid (pid_t pid, pid_t pgid) |
2957 | { | |
2958 | int res = -1; | |
2959 | if (pid == 0) | |
2960 | pid = getpid (); | |
2961 | if (pgid == 0) | |
2962 | pgid = pid; | |
2963 | ||
2964 | if (pgid < 0) | |
893ac8e0 | 2965 | set_errno (EINVAL); |
1fd5e000 CF |
2966 | else |
2967 | { | |
276448cf | 2968 | pinfo p (pid, PID_MAP_RW); |
84c7d409 | 2969 | if (!p) |
893ac8e0 CF |
2970 | set_errno (ESRCH); |
2971 | else if (p->pgid == pgid) | |
2972 | res = 0; | |
84c7d409 | 2973 | /* A process may only change the process group of itself and its children */ |
893ac8e0 CF |
2974 | else if (p != myself && p->ppid != myself->pid) |
2975 | set_errno (EPERM); | |
2976 | else | |
84c7d409 CF |
2977 | { |
2978 | p->pgid = pgid; | |
f8f9b12e CF |
2979 | if (p->pid != p->pgid) |
2980 | p->set_has_pgid_children (0); | |
84c7d409 | 2981 | res = 0; |
84c7d409 | 2982 | } |
1fd5e000 | 2983 | } |
893ac8e0 | 2984 | |
1fd5e000 CF |
2985 | syscall_printf ("pid %d, pgid %d, res %d", pid, pgid, res); |
2986 | return res; | |
2987 | } | |
2988 | ||
b0e82b74 | 2989 | extern "C" pid_t |
1fd5e000 CF |
2990 | getpgid (pid_t pid) |
2991 | { | |
2992 | if (pid == 0) | |
2993 | pid = getpid (); | |
2994 | ||
84c7d409 | 2995 | pinfo p (pid); |
1fd5e000 CF |
2996 | if (p == 0) |
2997 | { | |
2998 | set_errno (ESRCH); | |
2999 | return -1; | |
3000 | } | |
3001 | return p->pgid; | |
3002 | } | |
3003 | ||
b0e82b74 | 3004 | extern "C" int |
1fd5e000 CF |
3005 | setpgrp (void) |
3006 | { | |
3007 | return setpgid (0, 0); | |
3008 | } | |
3009 | ||
b0e82b74 | 3010 | extern "C" pid_t |
1fd5e000 CF |
3011 | getpgrp (void) |
3012 | { | |
3013 | return getpgid (0); | |
3014 | } | |
3015 | ||
b0e82b74 | 3016 | extern "C" char * |
1fd5e000 CF |
3017 | ptsname (int fd) |
3018 | { | |
65a6152f CF |
3019 | static char buf[TTY_NAME_MAX]; |
3020 | return ptsname_r (fd, buf, sizeof (buf)) == 0 ? buf : NULL; | |
3021 | } | |
3022 | ||
3023 | extern "C" int | |
3024 | ptsname_r (int fd, char *buf, size_t buflen) | |
3025 | { | |
3026 | if (!buf) | |
3027 | { | |
3028 | set_errno (EINVAL); | |
3029 | return EINVAL; | |
3030 | } | |
3031 | ||
df63bd49 CF |
3032 | cygheap_fdget cfd (fd); |
3033 | if (cfd < 0) | |
3034 | return 0; | |
65a6152f | 3035 | return cfd->ptsname_r (buf, buflen); |
1fd5e000 CF |
3036 | } |
3037 | ||
7ac61736 CF |
3038 | static int __stdcall |
3039 | mknod_worker (const char *path, mode_t type, mode_t mode, _major_t major, | |
3040 | _minor_t minor) | |
3041 | { | |
752b16ce | 3042 | char buf[sizeof (":\\00000000:00000000:00000000") + PATH_MAX]; |
341d2954 | 3043 | sprintf (buf, ":\\%x:%x:%x", major, minor, |
7ac61736 CF |
3044 | type | (mode & (S_IRWXU | S_IRWXG | S_IRWXO))); |
3045 | return symlink_worker (buf, path, true, true); | |
3046 | } | |
3047 | ||
b0e82b74 | 3048 | extern "C" int |
7ac61736 | 3049 | mknod32 (const char *path, mode_t mode, __dev32_t dev) |
1fd5e000 | 3050 | { |
893ac8e0 CF |
3051 | myfault efault; |
3052 | if (efault.faulted (EFAULT)) | |
7ac61736 | 3053 | return -1; |
893ac8e0 CF |
3054 | if (!*path) |
3055 | { | |
3056 | set_errno (ENOENT); | |
3057 | return -1; | |
3058 | } | |
7ac61736 | 3059 | |
752b16ce | 3060 | if (strlen (path) >= PATH_MAX) |
7ac61736 CF |
3061 | return -1; |
3062 | ||
063f1df2 | 3063 | path_conv w32path (path, PC_SYM_NOFOLLOW); |
7ac61736 CF |
3064 | if (w32path.exists ()) |
3065 | { | |
3066 | set_errno (EEXIST); | |
3067 | return -1; | |
3068 | } | |
3069 | ||
3070 | mode_t type = mode & S_IFMT; | |
3071 | _major_t major = _major (dev); | |
3072 | _minor_t minor = _minor (dev); | |
3073 | switch (type) | |
3074 | { | |
3075 | case S_IFCHR: | |
3076 | case S_IFBLK: | |
3077 | break; | |
3078 | ||
3079 | case S_IFIFO: | |
3080 | major = _major (FH_FIFO); | |
3081 | minor = _minor (FH_FIFO); | |
3082 | break; | |
3083 | ||
3084 | case 0: | |
3085 | case S_IFREG: | |
3086 | { | |
3087 | int fd = open (path, O_CREAT, mode); | |
3088 | if (fd < 0) | |
3089 | return -1; | |
3090 | close (fd); | |
3091 | return 0; | |
3092 | } | |
3093 | ||
3094 | default: | |
3095 | set_errno (EINVAL); | |
3096 | return -1; | |
3097 | } | |
3098 | ||
29992bf3 | 3099 | return mknod_worker (w32path.get_win32 (), type, mode, major, minor); |
1fd5e000 CF |
3100 | } |
3101 | ||
b1aae492 CV |
3102 | extern "C" int |
3103 | mknod (const char *_path, mode_t mode, __dev16_t dev) | |
3104 | { | |
3105 | return mknod32 (_path, mode, (__dev32_t) dev); | |
3106 | } | |
3107 | ||
82d4a5d4 | 3108 | extern "C" int |
0cd9f74f | 3109 | mkfifo (const char *path, mode_t mode) |
82d4a5d4 | 3110 | { |
0cd9f74f | 3111 | return mknod32 (path, (mode & ~S_IFMT) | S_IFIFO, 0); |
82d4a5d4 CF |
3112 | } |
3113 | ||
64b30629 | 3114 | /* seteuid: standards? */ |
b0e82b74 | 3115 | extern "C" int |
a8d7ae61 | 3116 | seteuid32 (__uid32_t uid) |
1fd5e000 | 3117 | { |
f40d6122 | 3118 | debug_printf ("uid: %u myself->uid: %u myself->gid: %u", |
05726ddd | 3119 | uid, myself->uid, myself->gid); |
75bf2931 | 3120 | |
0191627a CV |
3121 | /* Same uid as we're just running under is usually a no-op. |
3122 | ||
3123 | Except we have an external token which is a restricted token. Or, | |
3124 | the external token is NULL, but the current impersonation token is | |
3125 | a restricted token. This allows to restrict user rights temporarily | |
3126 | like this: | |
3127 | ||
3128 | cygwin_internal(CW_SET_EXTERNAL_TOKEN, restricted_token, | |
b86f999a | 3129 | CW_TOKEN_RESTRICTED); |
0191627a CV |
3130 | setuid (getuid ()); |
3131 | [...do stuff with restricted rights...] | |
3132 | cygwin_internal(CW_SET_EXTERNAL_TOKEN, INVALID_HANDLE_VALUE, | |
b86f999a | 3133 | CW_TOKEN_RESTRICTED); |
0191627a CV |
3134 | setuid (getuid ()); |
3135 | ||
3136 | Note that using the current uid is a requirement! Starting with Windows | |
3137 | Vista, we have restricted tokens galore (UAC), so this is really just | |
3138 | a special case to restict your own processes to lesser rights. */ | |
3139 | bool request_restricted_uid_switch = (uid == myself->uid | |
3140 | && cygheap->user.ext_token_is_restricted); | |
3141 | if (uid == myself->uid && !cygheap->user.groups.ischanged | |
3142 | && !request_restricted_uid_switch) | |
75bf2931 | 3143 | { |
e968058f | 3144 | debug_printf ("Nothing happens"); |
75bf2931 CV |
3145 | return 0; |
3146 | } | |
3147 | ||
5519d543 CV |
3148 | cygsid usersid; |
3149 | user_groups &groups = cygheap->user.groups; | |
f4a1f8a1 | 3150 | HANDLE new_token = INVALID_HANDLE_VALUE; |
9a771b29 | 3151 | struct passwd * pw_new; |
e70bea19 | 3152 | bool token_is_internal, issamesid = false; |
9a4d574b | 3153 | |
d6ffc075 | 3154 | pw_new = internal_getpwuid (uid); |
5519d543 | 3155 | if (!usersid.getfrompw (pw_new)) |
f06a3648 | 3156 | { |
75bf2931 CV |
3157 | set_errno (EINVAL); |
3158 | return -1; | |
3159 | } | |
d4217d56 | 3160 | |
f4a1f8a1 | 3161 | cygheap->user.deimpersonate (); |
70249d56 CV |
3162 | |
3163 | /* Verify if the process token is suitable. */ | |
0191627a CV |
3164 | /* First of all, skip all checks if a switch to a restricted token has been |
3165 | requested, or if trying to switch back from it. */ | |
3166 | if (request_restricted_uid_switch) | |
3167 | { | |
3168 | if (cygheap->user.external_token != NO_IMPERSONATION) | |
3169 | { | |
3170 | debug_printf ("Switch to restricted token"); | |
3171 | new_token = cygheap->user.external_token; | |
3172 | } | |
3173 | else | |
3174 | { | |
3175 | debug_printf ("Switch back from restricted token"); | |
3176 | new_token = hProcToken; | |
3177 | cygheap->user.ext_token_is_restricted = false; | |
3178 | } | |
3179 | } | |
51303cbd CV |
3180 | /* TODO, CV 2008-11-25: The check against saved_sid is a kludge and a |
3181 | shortcut. We must check if it's really feasible in the long run. | |
3182 | The reason to add this shortcut is this: sshd switches back to the | |
3183 | privileged user running sshd at least twice in the process of | |
3184 | authentication. It calls seteuid first, then setegid. Due to this | |
3185 | order, the setgroups group list is still active when calling seteuid | |
3186 | and verify_token treats the original token of the privileged user as | |
3187 | insufficient. This in turn results in creating a new user token for | |
3188 | the privileged user instead of using the orignal token. This can have | |
3189 | unfortunate side effects. The created token has different group | |
3190 | memberships, different user rights, and misses possible network | |
025c1fac | 3191 | credentials. |
51303cbd CV |
3192 | Therefore we try this shortcut now. When switching back to the |
3193 | privileged user, we probably always want a correct (aka original) | |
3194 | user token for this privileged user, not only in sshd. */ | |
0191627a CV |
3195 | else if ((uid == cygheap->user.saved_uid |
3196 | && usersid == cygheap->user.saved_sid ()) | |
3197 | || verify_token (hProcToken, usersid, groups)) | |
f4a1f8a1 | 3198 | new_token = hProcToken; |
ea3ba114 | 3199 | /* Verify if the external token is suitable */ |
53197923 | 3200 | else if (cygheap->user.external_token != NO_IMPERSONATION |
70249d56 | 3201 | && verify_token (cygheap->user.external_token, usersid, groups)) |
ea3ba114 CV |
3202 | new_token = cygheap->user.external_token; |
3203 | /* Verify if the current token (internal or former external) is suitable */ | |
f4a1f8a1 CV |
3204 | else if (cygheap->user.curr_primary_token != NO_IMPERSONATION |
3205 | && cygheap->user.curr_primary_token != cygheap->user.external_token | |
3206 | && verify_token (cygheap->user.curr_primary_token, usersid, groups, | |
ea3ba114 | 3207 | &token_is_internal)) |
f4a1f8a1 | 3208 | new_token = cygheap->user.curr_primary_token; |
ea3ba114 | 3209 | /* Verify if the internal token is suitable */ |
53197923 | 3210 | else if (cygheap->user.internal_token != NO_IMPERSONATION |
f4a1f8a1 | 3211 | && cygheap->user.internal_token != cygheap->user.curr_primary_token |
70249d56 CV |
3212 | && verify_token (cygheap->user.internal_token, usersid, groups, |
3213 | &token_is_internal)) | |
ea3ba114 | 3214 | new_token = cygheap->user.internal_token; |
f489e86b | 3215 | |
ea3ba114 | 3216 | debug_printf ("Found token %d", new_token); |
1fcc912f | 3217 | |
51303cbd CV |
3218 | /* If no impersonation token is available, try to authenticate using |
3219 | LSA private data stored password, LSA authentication using our own | |
3220 | LSA module, or, as last chance, NtCreateToken. */ | |
ea3ba114 | 3221 | if (new_token == INVALID_HANDLE_VALUE) |
75bf2931 | 3222 | { |
51303cbd CV |
3223 | new_token = lsaprivkeyauth (pw_new); |
3224 | if (new_token) | |
025c1fac | 3225 | { |
51303cbd CV |
3226 | /* We have to verify this token since settings in /etc/group |
3227 | might render it unusable im terms of group membership. */ | |
3228 | if (!verify_token (new_token, usersid, groups)) | |
3229 | { | |
3230 | CloseHandle (new_token); | |
3231 | new_token = NULL; | |
3232 | } | |
3233 | } | |
3234 | if (!new_token) | |
025c1fac | 3235 | { |
51303cbd CV |
3236 | debug_printf ("lsaprivkeyauth failed, try lsaauth."); |
3237 | if (!(new_token = lsaauth (usersid, groups, pw_new))) | |
f4a1f8a1 | 3238 | { |
51303cbd CV |
3239 | debug_printf ("lsaauth failed, try create_token."); |
3240 | new_token = create_token (usersid, groups, pw_new); | |
3241 | if (new_token == INVALID_HANDLE_VALUE) | |
3242 | { | |
3243 | debug_printf ("create_token failed, bail out of here"); | |
3244 | cygheap->user.reimpersonate (); | |
3245 | return -1; | |
3246 | } | |
f4a1f8a1 | 3247 | } |
70e476d2 | 3248 | } |
b825c587 | 3249 | |
ea3ba114 | 3250 | /* Keep at most one internal token */ |
53197923 | 3251 | if (cygheap->user.internal_token != NO_IMPERSONATION) |
ea3ba114 CV |
3252 | CloseHandle (cygheap->user.internal_token); |
3253 | cygheap->user.internal_token = new_token; | |
75bf2931 | 3254 | } |
53197923 | 3255 | |
f4a1f8a1 | 3256 | if (new_token != hProcToken) |
75bf2931 | 3257 | { |
12eac211 CV |
3258 | NTSTATUS status; |
3259 | ||
0191627a CV |
3260 | if (!request_restricted_uid_switch) |
3261 | { | |
3262 | /* Avoid having HKCU use default user */ | |
3263 | WCHAR name[128]; | |
3264 | load_registry_hive (usersid.string (name)); | |
3265 | } | |
9a4d574b | 3266 | |
ea3ba114 | 3267 | /* Try setting owner to same value as user. */ |
12eac211 CV |
3268 | status = NtSetInformationToken (new_token, TokenOwner, |
3269 | &usersid, sizeof usersid); | |
3270 | if (!NT_SUCCESS (status)) | |
3271 | debug_printf ("NtSetInformationToken (user.token, TokenOwner), %p", | |
3272 | status); | |
ea3ba114 | 3273 | /* Try setting primary group in token to current group */ |
12eac211 CV |
3274 | status = NtSetInformationToken (new_token, TokenPrimaryGroup, |
3275 | &groups.pgsid, sizeof (cygsid)); | |
3276 | if (!NT_SUCCESS (status)) | |
3277 | debug_printf ("NtSetInformationToken (user.token, TokenPrimaryGroup)," | |
3278 | "%p", status); | |
9808b5c4 | 3279 | /* Try setting default DACL */ |
7311cc1f CV |
3280 | PACL dacl_buf = (PACL) alloca (MAX_DACL_LEN (5)); |
3281 | if (sec_acl (dacl_buf, true, true, usersid)) | |
05726ddd | 3282 | { |
7311cc1f | 3283 | TOKEN_DEFAULT_DACL tdacl = { dacl_buf }; |
12eac211 CV |
3284 | status = NtSetInformationToken (new_token, TokenDefaultDacl, |
3285 | &tdacl, sizeof (tdacl)); | |
3286 | if (!NT_SUCCESS (status)) | |
3287 | debug_printf ("NtSetInformationToken (TokenDefaultDacl), %p", | |
3288 | status); | |
f4a1f8a1 | 3289 | } |
75bf2931 | 3290 | } |
70249d56 | 3291 | |
9a4d574b | 3292 | issamesid = (usersid == cygheap->user.sid ()); |
ea3ba114 | 3293 | cygheap->user.set_sid (usersid); |
f4a1f8a1 CV |
3294 | cygheap->user.curr_primary_token = new_token == hProcToken ? NO_IMPERSONATION |
3295 | : new_token; | |
0191627a CV |
3296 | cygheap->user.curr_token_is_restricted = false; |
3297 | cygheap->user.setuid_to_restricted = false; | |
77ee8805 | 3298 | if (cygheap->user.curr_imp_token != NO_IMPERSONATION) |
f4a1f8a1 | 3299 | { |
77ee8805 CV |
3300 | CloseHandle (cygheap->user.curr_imp_token); |
3301 | cygheap->user.curr_imp_token = NO_IMPERSONATION; | |
f4a1f8a1 CV |
3302 | } |
3303 | if (cygheap->user.curr_primary_token != NO_IMPERSONATION) | |
3304 | { | |
0191627a CV |
3305 | /* HANDLE_FLAG_INHERIT may be missing in external token. */ |
3306 | if (!SetHandleInformation (cygheap->user.curr_primary_token, | |
3307 | HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT) | |
3308 | || !DuplicateTokenEx (cygheap->user.curr_primary_token, | |
3309 | MAXIMUM_ALLOWED, &sec_none, | |
3310 | SecurityImpersonation, TokenImpersonation, | |
3311 | &cygheap->user.curr_imp_token)) | |
f4a1f8a1 CV |
3312 | { |
3313 | __seterrno (); | |
3314 | cygheap->user.curr_primary_token = NO_IMPERSONATION; | |
3315 | return -1; | |
3316 | } | |
0191627a | 3317 | cygheap->user.curr_token_is_restricted = request_restricted_uid_switch; |
dfbf36f8 | 3318 | set_cygwin_privileges (cygheap->user.curr_primary_token); |
77ee8805 | 3319 | set_cygwin_privileges (cygheap->user.curr_imp_token); |
f4a1f8a1 CV |
3320 | } |
3321 | if (!cygheap->user.reimpersonate ()) | |
3322 | { | |
3323 | __seterrno (); | |
3324 | return -1; | |
3325 | } | |
7119fc0d | 3326 | |
4f7e12dd | 3327 | cygheap->user.set_name (pw_new->pw_name); |
9a771b29 | 3328 | myself->uid = uid; |
5519d543 | 3329 | groups.ischanged = FALSE; |
a6888c92 | 3330 | if (!issamesid) |
038af334 | 3331 | /* Recreate and fill out the user shared region for a new user. */ |
cef5dfd7 | 3332 | user_info::create (true); |
9a771b29 | 3333 | return 0; |
1fd5e000 CF |
3334 | } |
3335 | ||
a8d7ae61 CV |
3336 | extern "C" int |
3337 | seteuid (__uid16_t uid) | |
3338 | { | |
3339 | return seteuid32 (uid16touid32 (uid)); | |
3340 | } | |
3341 | ||
3342 | /* setuid: POSIX 4.2.2.1 */ | |
3343 | extern "C" int | |
3344 | setuid32 (__uid32_t uid) | |
3345 | { | |
3346 | int ret = seteuid32 (uid); | |
3347 | if (!ret) | |
0191627a CV |
3348 | { |
3349 | cygheap->user.real_uid = myself->uid; | |
3350 | /* If restricted token, forget original privileges on exec (). */ | |
3351 | cygheap->user.setuid_to_restricted = cygheap->user.curr_token_is_restricted; | |
3352 | } | |
a8d7ae61 CV |
3353 | debug_printf ("real: %d, effective: %d", cygheap->user.real_uid, myself->uid); |
3354 | return ret; | |
3355 | } | |
3356 | ||
3357 | extern "C" int | |
3358 | setuid (__uid16_t uid) | |
3359 | { | |
3360 | return setuid32 (uid16touid32 (uid)); | |
3361 | } | |
3362 | ||
7d33eefa CV |
3363 | extern "C" int |
3364 | setreuid32 (__uid32_t ruid, __uid32_t euid) | |
3365 | { | |
3366 | int ret = 0; | |
3367 | bool tried = false; | |
3368 | __uid32_t old_euid = myself->uid; | |
3369 | ||
3370 | if (ruid != ILLEGAL_UID && cygheap->user.real_uid != ruid && euid != ruid) | |
3371 | tried = !(ret = seteuid32 (ruid)); | |
3372 | if (!ret && euid != ILLEGAL_UID) | |
3373 | ret = seteuid32 (euid); | |
3374 | if (tried && (ret || euid == ILLEGAL_UID) && seteuid32 (old_euid)) | |
3375 | system_printf ("Cannot restore original euid %u", old_euid); | |
3376 | if (!ret && ruid != ILLEGAL_UID) | |
3377 | cygheap->user.real_uid = ruid; | |
3378 | debug_printf ("real: %u, effective: %u", cygheap->user.real_uid, myself->uid); | |
3379 | return ret; | |
3380 | } | |
3381 | ||
3382 | extern "C" int | |
3383 | setreuid (__uid16_t ruid, __uid16_t euid) | |
3384 | { | |
3385 | return setreuid32 (uid16touid32 (ruid), uid16touid32 (euid)); | |
3386 | } | |
3387 | ||
1fd5e000 | 3388 | /* setegid: from System V. */ |
90fe7739 | 3389 | extern "C" int |
57196405 | 3390 | setegid32 (__gid32_t gid) |
1fd5e000 | 3391 | { |
4f7e12dd | 3392 | debug_printf ("new egid: %u current: %u", gid, myself->gid); |
75bf2931 | 3393 | |
a76877e9 | 3394 | if (gid == myself->gid) |
8934e470 | 3395 | { |
4f7e12dd CV |
3396 | myself->gid = gid; |
3397 | return 0; | |
8934e470 CV |
3398 | } |
3399 | ||
12eac211 | 3400 | NTSTATUS status; |
5519d543 | 3401 | user_groups * groups = &cygheap->user.groups; |
75bf2931 | 3402 | cygsid gsid; |
d6ffc075 | 3403 | struct __group32 * gr = internal_getgrgid (gid); |
4f7e12dd CV |
3404 | |
3405 | if (!gsid.getfromgr (gr)) | |
64b30629 | 3406 | { |
75bf2931 CV |
3407 | set_errno (EINVAL); |
3408 | return -1; | |
3409 | } | |
3410 | myself->gid = gid; | |
d551169a | 3411 | |
5519d543 | 3412 | groups->update_pgrp (gsid); |
e40670ee | 3413 | if (cygheap->user.issetuid ()) |
75bf2931 | 3414 | { |
f4a1f8a1 | 3415 | /* If impersonated, update impersonation token... */ |
12eac211 CV |
3416 | status = NtSetInformationToken (cygheap->user.primary_token (), |
3417 | TokenPrimaryGroup, &gsid, sizeof gsid); | |
3418 | if (!NT_SUCCESS (status)) | |
3419 | debug_printf ("NtSetInformationToken (primary_token, " | |
3420 | "TokenPrimaryGroup), %p", status); | |
3421 | status = NtSetInformationToken (cygheap->user.imp_token (), | |
3422 | TokenPrimaryGroup, &gsid, sizeof gsid); | |
3423 | if (!NT_SUCCESS (status)) | |
3424 | debug_printf ("NtSetInformationToken (token, TokenPrimaryGroup), %p", | |
3425 | status); | |
f4a1f8a1 CV |
3426 | } |
3427 | cygheap->user.deimpersonate (); | |
12eac211 CV |
3428 | status = NtSetInformationToken (hProcToken, TokenPrimaryGroup, |
3429 | &gsid, sizeof gsid); | |
3430 | if (!NT_SUCCESS (status)) | |
3431 | debug_printf ("NtSetInformationToken (hProcToken, TokenPrimaryGroup), %p", | |
3432 | status); | |
5684cfeb | 3433 | clear_procimptoken (); |
f4a1f8a1 | 3434 | cygheap->user.reimpersonate (); |
64b30629 | 3435 | return 0; |
1fd5e000 CF |
3436 | } |
3437 | ||
57196405 CV |
3438 | extern "C" int |
3439 | setegid (__gid16_t gid) | |
3440 | { | |
a8d7ae61 | 3441 | return setegid32 (gid16togid32 (gid)); |
57196405 CV |
3442 | } |
3443 | ||
3444 | /* setgid: POSIX 4.2.2.1 */ | |
3445 | extern "C" int | |
3446 | setgid32 (__gid32_t gid) | |
3447 | { | |
3448 | int ret = setegid32 (gid); | |
3449 | if (!ret) | |
3450 | cygheap->user.real_gid = myself->gid; | |
3451 | return ret; | |
3452 | } | |
3453 | ||
3454 | extern "C" int | |
3455 | setgid (__gid16_t gid) | |
3456 | { | |
a8d7ae61 | 3457 | int ret = setegid32 (gid16togid32 (gid)); |
57196405 CV |
3458 | if (!ret) |
3459 | cygheap->user.real_gid = myself->gid; | |
3460 | return ret; | |
3461 | } | |
3462 | ||
7d33eefa CV |
3463 | extern "C" int |
3464 | setregid32 (__gid32_t rgid, __gid32_t egid) | |
3465 | { | |
3466 | int ret = 0; | |
3467 | bool tried = false; | |
3468 | __gid32_t old_egid = myself->gid; | |
3469 | ||
3470 | if (rgid != ILLEGAL_GID && cygheap->user.real_gid != rgid && egid != rgid) | |
3471 | tried = !(ret = setegid32 (rgid)); | |
3472 | if (!ret && egid != ILLEGAL_GID) | |
3473 | ret = setegid32 (egid); | |
3474 | if (tried && (ret || egid == ILLEGAL_GID) && setegid32 (old_egid)) | |
3475 | system_printf ("Cannot restore original egid %u", old_egid); | |
3476 | if (!ret && rgid != ILLEGAL_GID) | |
3477 | cygheap->user.real_gid = rgid; | |
3478 | debug_printf ("real: %u, effective: %u", cygheap->user.real_gid, myself->gid); | |
3479 | return ret; | |
3480 | } | |
3481 | ||
3482 | extern "C" int | |
3483 | setregid (__gid16_t rgid, __gid16_t egid) | |
3484 | { | |
3485 | return setregid32 (gid16togid32 (rgid), gid16togid32 (egid)); | |
3486 | } | |
3487 | ||
1fd5e000 | 3488 | /* chroot: privileged Unix system call. */ |
a67f4165 | 3489 | /* FIXME: Not privileged here. How should this be done? */ |
90fe7739 | 3490 | extern "C" int |
a67f4165 | 3491 | chroot (const char *newroot) |
1fd5e000 | 3492 | { |
063f1df2 | 3493 | path_conv path (newroot, PC_SYM_FOLLOW | PC_POSIX); |
f489e86b | 3494 | |
ac300315 | 3495 | int ret = -1; |
a67f4165 | 3496 | if (path.error) |
ac300315 | 3497 | set_errno (path.error); |
47063f00 | 3498 | else if (!path.exists ()) |
ac300315 | 3499 | set_errno (ENOENT); |
47063f00 | 3500 | else if (!path.isdir ()) |
ac300315 | 3501 | set_errno (ENOTDIR); |
7b8b467d CV |
3502 | else if (path.isspecial ()) |
3503 | set_errno (EPERM); | |
47063f00 CF |
3504 | else |
3505 | { | |
ac300315 | 3506 | getwinenv("PATH="); /* Save the native PATH */ |
e4b57503 CV |
3507 | cygheap->root.set (path.normalized_path, path.get_win32 (), |
3508 | !!path.objcaseinsensitive ()); | |
47063f00 | 3509 | ret = 0; |
a67f4165 | 3510 | } |
a67f4165 | 3511 | |
b9aa8149 | 3512 | syscall_printf ("%R = chroot(%s)", ret, newroot ?: "NULL"); |
a67f4165 | 3513 | return ret; |
1fd5e000 CF |
3514 | } |
3515 | ||
90fe7739 | 3516 | extern "C" int |
1fd5e000 CF |
3517 | creat (const char *path, mode_t mode) |
3518 | { | |
3519 | return open (path, O_WRONLY | O_CREAT | O_TRUNC, mode); | |
3520 | } | |
3521 | ||
90fe7739 | 3522 | extern "C" void |
1fd5e000 CF |
3523 | __assertfail () |
3524 | { | |
3525 | exit (99); | |
3526 | } | |
3527 | ||
90fe7739 | 3528 | extern "C" int |
1fd5e000 CF |
3529 | vhangup () |
3530 | { | |
3531 | set_errno (ENOSYS); | |
3532 | return -1; | |
3533 | } | |
3534 | ||
90fe7739 | 3535 | extern "C" int |
72c1491b | 3536 | setpriority (int which, id_t who, int value) |
1fd5e000 | 3537 | { |
0f72b906 CV |
3538 | DWORD prio = nice_to_winprio (value); |
3539 | int error = 0; | |
3540 | ||
3541 | switch (which) | |
1fd5e000 | 3542 | { |
0f72b906 CV |
3543 | case PRIO_PROCESS: |
3544 | if (!who) | |
3545 | who = myself->pid; | |
3546 | if ((pid_t) who == myself->pid) | |
3547 | { | |
f16706de | 3548 | if (!SetPriorityClass (GetCurrentProcess (), prio)) |
0f72b906 CV |
3549 | { |
3550 | set_errno (EACCES); | |
3551 | return -1; | |
3552 | } | |
3553 | myself->nice = value; | |
3554 | debug_printf ("Set nice to %d", myself->nice); | |
3555 | return 0; | |
3556 | } | |
3557 | break; | |
3558 | case PRIO_PGRP: | |
3559 | if (!who) | |
3560 | who = myself->pgid; | |
3561 | break; | |
3562 | case PRIO_USER: | |
3563 | if (!who) | |
3564 | who = myself->uid; | |
3565 | break; | |
3566 | default: | |
72c1491b CV |
3567 | set_errno (EINVAL); |
3568 | return -1; | |
3569 | } | |
0f72b906 CV |
3570 | winpids pids ((DWORD) PID_MAP_RW); |
3571 | for (DWORD i = 0; i < pids.npids; ++i) | |
1fd5e000 | 3572 | { |
0f72b906 CV |
3573 | _pinfo *p = pids[i]; |
3574 | if (p) | |
3575 | { | |
3576 | switch (which) | |
3577 | { | |
3578 | case PRIO_PROCESS: | |
3579 | if ((pid_t) who != p->pid) | |
3580 | continue; | |
3581 | break; | |
3582 | case PRIO_PGRP: | |
3583 | if ((pid_t) who != p->pgid) | |
3584 | continue; | |
3585 | break; | |
3586 | case PRIO_USER: | |
3587 | if ((__uid32_t) who != p->uid) | |
3588 | continue; | |
3589 | break; | |
3590 | } | |
3591 | HANDLE proc_h = OpenProcess (PROCESS_SET_INFORMATION, FALSE, | |
3592 | p->dwProcessId); | |
3593 | if (!proc_h) | |
3594 | error = EPERM; | |
3595 | else | |
3596 | { | |
3597 | if (!SetPriorityClass (proc_h, prio)) | |
3598 | error = EACCES; | |
3599 | else | |
3600 | p->nice = value; | |
3601 | CloseHandle (proc_h); | |
3602 | } | |
3603 | } | |
3604 | } | |
3605 | pids.reset (); | |
3606 | if (error) | |
3607 | { | |
3608 | set_errno (error); | |
72c1491b | 3609 | return -1; |
1fd5e000 | 3610 | } |
72c1491b CV |
3611 | return 0; |
3612 | } | |
1fd5e000 | 3613 | |
72c1491b CV |
3614 | extern "C" int |
3615 | getpriority (int which, id_t who) | |
3616 | { | |
0f72b906 CV |
3617 | int nice = NZERO * 2; /* Illegal value */ |
3618 | ||
3619 | switch (which) | |
1fd5e000 | 3620 | { |
0f72b906 CV |
3621 | case PRIO_PROCESS: |
3622 | if (!who) | |
3623 | who = myself->pid; | |
3624 | if ((pid_t) who == myself->pid) | |
3625 | return myself->nice; | |
3626 | break; | |
3627 | case PRIO_PGRP: | |
3628 | if (!who) | |
3629 | who = myself->pgid; | |
3630 | break; | |
3631 | case PRIO_USER: | |
3632 | if (!who) | |
3633 | who = myself->uid; | |
3634 | break; | |
3635 | default: | |
72c1491b | 3636 | set_errno (EINVAL); |
1fd5e000 CF |
3637 | return -1; |
3638 | } | |
0f72b906 CV |
3639 | winpids pids ((DWORD) 0); |
3640 | for (DWORD i = 0; i < pids.npids; ++i) | |
3641 | { | |
3642 | _pinfo *p = pids[i]; | |
3643 | if (p) | |
05726ddd | 3644 | switch (which) |
0f72b906 CV |
3645 | { |
3646 | case PRIO_PROCESS: | |
3647 | if ((pid_t) who == p->pid) | |
3648 | { | |
3649 | nice = p->nice; | |
3650 | goto out; | |
3651 | } | |
3652 | break; | |
3653 | case PRIO_PGRP: | |
3654 | if ((pid_t) who == p->pgid && p->nice < nice) | |
3655 | nice = p->nice; | |
3656 | break; | |
3657 | case PRIO_USER: | |
3658 | if ((__uid32_t) who == p->uid && p->nice < nice) | |
3659 | nice = p->nice; | |
3660 | break; | |
3661 | } | |
3662 | } | |
3663 | out: | |
3664 | pids.reset (); | |
3665 | if (nice == NZERO * 2) | |
3666 | { | |
3667 | set_errno (ESRCH); | |
3668 | return -1; | |
3669 | } | |
3670 | return nice; | |
72c1491b | 3671 | } |
1fd5e000 | 3672 | |
72c1491b CV |
3673 | extern "C" int |
3674 | nice (int incr) | |
3675 | { | |
3676 | return setpriority (PRIO_PROCESS, myself->pid, myself->nice + incr); | |
1fd5e000 CF |
3677 | } |
3678 | ||
3679 | /* | |
3680 | * Find the first bit set in I. | |
3681 | */ | |
3682 | ||
90fe7739 | 3683 | extern "C" int |
1fd5e000 CF |
3684 | ffs (int i) |
3685 | { | |
3686 | static const unsigned char table[] = | |
3687 | { | |
3688 | 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, | |
3689 | 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, | |
3690 | 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, | |
3691 | 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, | |
3692 | 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, | |
3693 | 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, | |
3694 | 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, | |
3695 | 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 | |
3696 | }; | |
3697 | unsigned long int a; | |
3698 | unsigned long int x = i & -i; | |
3699 | ||
3700 | a = x <= 0xffff ? (x <= 0xff ? 0 : 8) : (x <= 0xffffff ? 16 : 24); | |
3701 | ||
3702 | return table[x >> a] + a; | |
3703 | } | |
3704 | ||
2e3ff06d CV |
3705 | static void |
3706 | locked_append (int fd, const void * buf, size_t size) | |
3707 | { | |
3708 | struct __flock64 lock_buffer = {F_WRLCK, SEEK_SET, 0, 0, 0}; | |
3709 | int count = 0; | |
3710 | ||
3711 | do | |
0cb6fc5d | 3712 | if ((lock_buffer.l_start = lseek64 (fd, 0, SEEK_END)) != (_off64_t) -1 |
fabfb1a1 | 3713 | && fcntl64 (fd, F_SETLKW, &lock_buffer) != -1) |
2e3ff06d | 3714 | { |
0cb6fc5d | 3715 | if (lseek64 (fd, 0, SEEK_END) != (_off64_t) -1) |
2e3ff06d CV |
3716 | write (fd, buf, size); |
3717 | lock_buffer.l_type = F_UNLCK; | |
fabfb1a1 | 3718 | fcntl64 (fd, F_SETLK, &lock_buffer); |
2e3ff06d CV |
3719 | break; |
3720 | } | |
3721 | while (count++ < 1000 | |
3722 | && (errno == EACCES || errno == EAGAIN) | |
3723 | && !usleep (1000)); | |
3724 | } | |
3725 | ||
90fe7739 | 3726 | extern "C" void |
34a1d63d | 3727 | updwtmp (const char *wtmp_file, const struct utmp *ut) |
1fd5e000 | 3728 | { |
34a1d63d CV |
3729 | int fd; |
3730 | ||
2e3ff06d | 3731 | if ((fd = open (wtmp_file, O_WRONLY | O_BINARY, 0)) >= 0) |
1fd5e000 | 3732 | { |
2e3ff06d | 3733 | locked_append (fd, ut, sizeof *ut); |
8304de2e CV |
3734 | close (fd); |
3735 | } | |
1fd5e000 CF |
3736 | } |
3737 | ||
8304de2e CV |
3738 | static int utmp_fd = -1; |
3739 | static bool utmp_readonly = false; | |
971ec8d3 CV |
3740 | static char *utmp_file = (char *) _PATH_UTMP; |
3741 | ||
8304de2e CV |
3742 | static void |
3743 | internal_setutent (bool force_readwrite) | |
971ec8d3 | 3744 | { |
8304de2e CV |
3745 | if (force_readwrite && utmp_readonly) |
3746 | endutent (); | |
3747 | if (utmp_fd < 0) | |
3748 | { | |
3749 | utmp_fd = open (utmp_file, O_RDWR | O_BINARY); | |
3750 | /* If open fails, we assume an unprivileged process (who?). In this | |
3751 | case we try again for reading only unless the process calls | |
3752 | pututline() (==force_readwrite) in which case opening just fails. */ | |
3753 | if (utmp_fd < 0 && !force_readwrite) | |
df04ae29 | 3754 | { |
8304de2e CV |
3755 | utmp_fd = open (utmp_file, O_RDONLY | O_BINARY); |
3756 | if (utmp_fd >= 0) | |
3757 | utmp_readonly = true; | |
df04ae29 | 3758 | } |
8304de2e | 3759 | } |
762520f3 CF |
3760 | else |
3761 | lseek (utmp_fd, 0, SEEK_SET); | |
971ec8d3 CV |
3762 | } |
3763 | ||
8304de2e CV |
3764 | extern "C" void |
3765 | setutent () | |
3766 | { | |
3767 | internal_setutent (false); | |
3768 | } | |
3769 | ||
971ec8d3 CV |
3770 | extern "C" void |
3771 | endutent () | |
3772 | { | |
8304de2e | 3773 | if (utmp_fd >= 0) |
4248a1d7 CF |
3774 | { |
3775 | close (utmp_fd); | |
8304de2e CV |
3776 | utmp_fd = -1; |
3777 | utmp_readonly = false; | |
4248a1d7 | 3778 | } |
971ec8d3 CV |
3779 | } |
3780 | ||
3781 | extern "C" void | |
72c1491b | 3782 | utmpname (const char *file) |
971ec8d3 | 3783 | { |
893ac8e0 CF |
3784 | myfault efault; |
3785 | if (efault.faulted () || !*file) | |
971ec8d3 CV |
3786 | { |
3787 | debug_printf ("Invalid file"); | |
3788 | return; | |
3789 | } | |
4248a1d7 | 3790 | endutent (); |
971ec8d3 CV |
3791 | utmp_file = strdup (file); |
3792 | debug_printf ("New UTMP file: %s", utmp_file); | |
3793 | } | |
12374d7d | 3794 | |
6b76b0c5 | 3795 | EXPORT_ALIAS (utmpname, utmpxname) |
971ec8d3 | 3796 | |
4423d924 CF |
3797 | /* Note: do not make NO_COPY */ |
3798 | static struct utmp utmp_data_buf[16]; | |
3799 | static unsigned utix = 0; | |
3800 | #define nutdbuf (sizeof (utmp_data_buf) / sizeof (utmp_data_buf[0])) | |
3801 | #define utmp_data ({ \ | |
2daa7e07 | 3802 | if (utix >= nutdbuf) \ |
4423d924 CF |
3803 | utix = 0; \ |
3804 | utmp_data_buf + utix++; \ | |
3805 | }) | |
3806 | ||
2ef89b22 CV |
3807 | static struct utmpx * |
3808 | copy_ut_to_utx (struct utmp *ut, struct utmpx *utx) | |
3809 | { | |
3810 | if (!ut) | |
3811 | return NULL; | |
3812 | memcpy (utx, ut, sizeof *ut); | |
3813 | utx->ut_tv.tv_sec = ut->ut_time; | |
3814 | utx->ut_tv.tv_usec = 0; | |
3815 | return utx; | |
3816 | } | |
3817 | ||
971ec8d3 CV |
3818 | extern "C" struct utmp * |
3819 | getutent () | |
3820 | { | |
8304de2e CV |
3821 | if (utmp_fd < 0) |
3822 | { | |
3823 | internal_setutent (false); | |
3824 | if (utmp_fd < 0) | |
df04ae29 | 3825 | return NULL; |
8304de2e | 3826 | } |
4423d924 CF |
3827 | |
3828 | utmp *ut = utmp_data; | |
3829 | if (read (utmp_fd, ut, sizeof *ut) != sizeof *ut) | |
971ec8d3 | 3830 | return NULL; |
4423d924 | 3831 | return ut; |
971ec8d3 CV |
3832 | } |
3833 | ||
3834 | extern "C" struct utmp * | |
a010e6ab | 3835 | getutid (const struct utmp *id) |
971ec8d3 | 3836 | { |
893ac8e0 CF |
3837 | myfault efault; |
3838 | if (efault.faulted (EFAULT)) | |
971ec8d3 | 3839 | return NULL; |
8304de2e CV |
3840 | if (utmp_fd < 0) |
3841 | { | |
3842 | internal_setutent (false); | |
3843 | if (utmp_fd < 0) | |
df04ae29 | 3844 | return NULL; |
8304de2e | 3845 | } |
4423d924 CF |
3846 | |
3847 | utmp *ut = utmp_data; | |
3848 | while (read (utmp_fd, ut, sizeof *ut) == sizeof *ut) | |
971ec8d3 CV |
3849 | { |
3850 | switch (id->ut_type) | |
3851 | { | |
971ec8d3 CV |
3852 | case RUN_LVL: |
3853 | case BOOT_TIME: | |
3854 | case OLD_TIME: | |
3855 | case NEW_TIME: | |
4423d924 CF |
3856 | if (id->ut_type == ut->ut_type) |
3857 | return ut; | |
971ec8d3 | 3858 | break; |
971ec8d3 CV |
3859 | case INIT_PROCESS: |
3860 | case LOGIN_PROCESS: | |
3861 | case USER_PROCESS: | |
3862 | case DEAD_PROCESS: | |
4423d924 CF |
3863 | if (strncmp (id->ut_id, ut->ut_id, UT_IDLEN) == 0) |
3864 | return ut; | |
971ec8d3 CV |
3865 | break; |
3866 | default: | |
3867 | return NULL; | |
3868 | } | |
3869 | } | |
3870 | return NULL; | |
3871 | } | |
3872 | ||
3873 | extern "C" struct utmp * | |
a010e6ab | 3874 | getutline (const struct utmp *line) |
971ec8d3 | 3875 | { |
893ac8e0 CF |
3876 | myfault efault; |
3877 | if (efault.faulted (EFAULT)) | |
971ec8d3 | 3878 | return NULL; |
8304de2e CV |
3879 | if (utmp_fd < 0) |
3880 | { | |
3881 | internal_setutent (false); | |
3882 | if (utmp_fd < 0) | |
df04ae29 | 3883 | return NULL; |
8304de2e | 3884 | } |
4423d924 CF |
3885 | |
3886 | utmp *ut = utmp_data; | |
3887 | while (read (utmp_fd, ut, sizeof *ut) == sizeof *ut) | |
3888 | if ((ut->ut_type == LOGIN_PROCESS || | |
3889 | ut->ut_type == USER_PROCESS) && | |
3890 | !strncmp (ut->ut_line, line->ut_line, sizeof (ut->ut_line))) | |
3891 | return ut; | |
3892 | ||
971ec8d3 CV |
3893 | return NULL; |
3894 | } | |
4248a1d7 | 3895 | |
2ef89b22 | 3896 | extern "C" struct utmp * |
a010e6ab | 3897 | pututline (const struct utmp *ut) |
4248a1d7 | 3898 | { |
893ac8e0 CF |
3899 | myfault efault; |
3900 | if (efault.faulted (EFAULT)) | |
2ef89b22 | 3901 | return NULL; |
8304de2e CV |
3902 | internal_setutent (true); |
3903 | if (utmp_fd < 0) | |
4423d924 CF |
3904 | { |
3905 | debug_printf ("error: utmp_fd %d", utmp_fd); | |
2ef89b22 | 3906 | return NULL; |
4423d924 CF |
3907 | } |
3908 | debug_printf ("ut->ut_type %d, ut->ut_pid %d, ut->ut_line '%s', ut->ut_id '%s'\n", | |
3909 | ut->ut_type, ut->ut_pid, ut->ut_line, ut->ut_id); | |
3910 | debug_printf ("ut->ut_user '%s', ut->ut_host '%s'\n", | |
3911 | ut->ut_user, ut->ut_host); | |
2e3ff06d | 3912 | |
4248a1d7 CF |
3913 | struct utmp *u; |
3914 | if ((u = getutid (ut))) | |
8304de2e | 3915 | { |
2e3ff06d CV |
3916 | lseek (utmp_fd, -sizeof *ut, SEEK_CUR); |
3917 | write (utmp_fd, ut, sizeof *ut); | |
8304de2e | 3918 | } |
2e3ff06d CV |
3919 | else |
3920 | locked_append (utmp_fd, ut, sizeof *ut); | |
a010e6ab CF |
3921 | /* The documentation says to return a pointer to this which implies that |
3922 | this has to be cast from a const. That doesn't seem right but the | |
3923 | documentation seems pretty clear on this. */ | |
3924 | return (struct utmp *) ut; | |
2ef89b22 CV |
3925 | } |
3926 | ||
3927 | extern "C" void | |
3928 | setutxent () | |
3929 | { | |
3930 | internal_setutent (false); | |
3931 | } | |
3932 | ||
3933 | extern "C" void | |
3934 | endutxent () | |
3935 | { | |
3936 | endutent (); | |
3937 | } | |
3938 | ||
3939 | extern "C" struct utmpx * | |
3940 | getutxent () | |
3941 | { | |
a010e6ab | 3942 | /* UGH. Not thread safe. */ |
2ef89b22 CV |
3943 | static struct utmpx utx; |
3944 | return copy_ut_to_utx (getutent (), &utx); | |
3945 | } | |
3946 | ||
3947 | extern "C" struct utmpx * | |
3948 | getutxid (const struct utmpx *id) | |
3949 | { | |
a010e6ab | 3950 | /* UGH. Not thread safe. */ |
2ef89b22 CV |
3951 | static struct utmpx utx; |
3952 | ||
893ac8e0 CF |
3953 | myfault efault; |
3954 | if (efault.faulted (EFAULT)) | |
2ef89b22 CV |
3955 | return NULL; |
3956 | ((struct utmpx *)id)->ut_time = id->ut_tv.tv_sec; | |
3957 | return copy_ut_to_utx (getutid ((struct utmp *) id), &utx); | |
3958 | } | |
3959 | ||
3960 | extern "C" struct utmpx * | |
3961 | getutxline (const struct utmpx *line) | |
3962 | { | |
a010e6ab | 3963 | /* UGH. Not thread safe. */ |
2ef89b22 CV |
3964 | static struct utmpx utx; |
3965 | ||
893ac8e0 CF |
3966 | myfault efault; |
3967 | if (efault.faulted (EFAULT)) | |
2ef89b22 CV |
3968 | return NULL; |
3969 | ((struct utmpx *)line)->ut_time = line->ut_tv.tv_sec; | |
3970 | return copy_ut_to_utx (getutline ((struct utmp *) line), &utx); | |
3971 | } | |
3972 | ||
3973 | extern "C" struct utmpx * | |
3974 | pututxline (const struct utmpx *utmpx) | |
3975 | { | |
a010e6ab | 3976 | /* UGH. Not thread safe. */ |
2ef89b22 CV |
3977 | static struct utmpx utx; |
3978 | ||
893ac8e0 CF |
3979 | myfault efault; |
3980 | if (efault.faulted (EFAULT)) | |
2ef89b22 CV |
3981 | return NULL; |
3982 | ((struct utmpx *)utmpx)->ut_time = utmpx->ut_tv.tv_sec; | |
3983 | return copy_ut_to_utx (pututline ((struct utmp *) utmpx), &utx); | |
4248a1d7 | 3984 | } |
c448f78f | 3985 | |
c0b0336f CV |
3986 | extern "C" void |
3987 | updwtmpx (const char *wtmpx_file, const struct utmpx *utmpx) | |
3988 | { | |
3989 | ((struct utmpx *)utmpx)->ut_time = utmpx->ut_tv.tv_sec; | |
3990 | updwtmp (wtmpx_file, (const struct utmp *) utmpx); | |
3991 | } | |
3992 | ||
b18cb86b CV |
3993 | extern "C" long |
3994 | gethostid (void) | |
c448f78f | 3995 | { |
5ad6d0a4 CV |
3996 | /* Fetch the globally unique MachineGuid value from |
3997 | HKLM/Software/Microsoft/Cryptography and hash it. */ | |
3998 | ||
3999 | /* Caution: sizeof long might become > 4 when we go 64 bit, but gethostid | |
4000 | is supposed to return a 32 bit value, despite the return type long. | |
4001 | That's why hostid is *not* long here. */ | |
4002 | int32_t hostid = 0x40291372; /* Choose a nice start value */ | |
4003 | WCHAR wguid[38]; | |
4004 | ||
4005 | reg_key key (HKEY_LOCAL_MACHINE, | |
4006 | KEY_READ | (wincap.is_wow64() ? KEY_WOW64_64KEY : 0), | |
4007 | L"SOFTWARE", L"Microsoft", L"Cryptography", NULL); | |
4008 | key.get_string (L"MachineGuid", wguid, 38, | |
4009 | L"00000000-0000-0000-0000-000000000000"); | |
4010 | /* SDBM hash */ | |
4011 | for (PWCHAR wp = wguid; *wp; ++wp) | |
4012 | hostid = *wp + (hostid << 6) + (hostid << 16) - hostid; | |
4013 | debug_printf ("hostid 0x%08x from MachineGuid %W", hostid, wguid); | |
4014 | return (int32_t) hostid; /* Avoid sign extension. */ | |
c448f78f | 4015 | } |
24efca14 CV |
4016 | |
4017 | #define ETC_SHELLS "/etc/shells" | |
4018 | static int shell_index; | |
932a40e8 | 4019 | static struct __sFILE64 *shell_fp; |
24efca14 CV |
4020 | |
4021 | extern "C" char * | |
4022 | getusershell () | |
4023 | { | |
4024 | /* List of default shells if no /etc/shells exists, defined as on Linux. | |
4025 | FIXME: SunOS has a far longer list, containing all shells which | |
4026 | might be shipped with the OS. Should we do the same for the Cygwin | |
4027 | distro, adding bash, tcsh, ksh, pdksh and zsh? */ | |
4028 | static NO_COPY const char *def_shells[] = { | |
4029 | "/bin/sh", | |
4030 | "/bin/csh", | |
4031 | "/usr/bin/sh", | |
4032 | "/usr/bin/csh", | |
4033 | NULL | |
4034 | }; | |
752b16ce | 4035 | static char buf[PATH_MAX]; |
24efca14 CV |
4036 | int ch, buf_idx; |
4037 | ||
932a40e8 | 4038 | if (!shell_fp && !(shell_fp = fopen64 (ETC_SHELLS, "rt"))) |
24efca14 CV |
4039 | { |
4040 | if (def_shells[shell_index]) | |
9a4d574b | 4041 | return strcpy (buf, def_shells[shell_index++]); |
24efca14 CV |
4042 | return NULL; |
4043 | } | |
4044 | /* Skip white space characters. */ | |
4045 | while ((ch = getc (shell_fp)) != EOF && isspace (ch)) | |
4046 | ; | |
4047 | /* Get each non-whitespace character as part of the shell path as long as | |
4048 | it fits in buf. */ | |
4049 | for (buf_idx = 0; | |
752b16ce | 4050 | ch != EOF && !isspace (ch) && buf_idx < PATH_MAX; |
24efca14 CV |
4051 | buf_idx++, ch = getc (shell_fp)) |
4052 | buf[buf_idx] = ch; | |
4053 | /* Skip any trailing non-whitespace character not fitting in buf. If the | |
752b16ce | 4054 | path is longer than PATH_MAX, it's invalid anyway. */ |
24efca14 CV |
4055 | while (ch != EOF && !isspace (ch)) |
4056 | ch = getc (shell_fp); | |
4057 | if (buf_idx) | |
4058 | { | |
4059 | buf[buf_idx] = '\0'; | |
4060 | return buf; | |
4061 | } | |
4062 | return NULL; | |
4063 | } | |
4064 | ||
4065 | extern "C" void | |
4066 | setusershell () | |
4067 | { | |
4068 | if (shell_fp) | |
4069 | fseek (shell_fp, 0L, SEEK_SET); | |
4070 | shell_index = 0; | |
4071 | } | |
4072 | ||
4073 | extern "C" void | |
4074 | endusershell () | |
4075 | { | |
4076 | if (shell_fp) | |
986c841b CV |
4077 | { |
4078 | fclose (shell_fp); | |
4079 | shell_fp = NULL; | |
4080 | } | |
24efca14 CV |
4081 | shell_index = 0; |
4082 | } | |
68509b30 CV |
4083 | |
4084 | extern "C" void | |
4085 | flockfile (FILE *file) | |
4086 | { | |
4087 | _flockfile (file); | |
4088 | } | |
4089 | ||
4090 | extern "C" int | |
4091 | ftrylockfile (FILE *file) | |
4092 | { | |
4093 | return _ftrylockfile (file); | |
4094 | } | |
4095 | ||
4096 | extern "C" void | |
4097 | funlockfile (FILE *file) | |
4098 | { | |
4099 | _funlockfile (file); | |
4100 | } | |
c16548b2 CF |
4101 | |
4102 | extern "C" FILE * | |
a10c6f03 | 4103 | popen (const char *command, const char *in_type) |
c16548b2 | 4104 | { |
a10c6f03 CF |
4105 | const char *type = in_type; |
4106 | char rw = *type++; | |
c16548b2 | 4107 | |
d5c44ae2 | 4108 | /* Sanity check in_type */ |
a10c6f03 CF |
4109 | if (*type == 'b' || *type == 't') |
4110 | type++; | |
4111 | if ((rw != 'r' && rw != 'w') || (*type != '\0')) | |
c16548b2 CF |
4112 | { |
4113 | set_errno (EINVAL); | |
4114 | return NULL; | |
4115 | } | |
a10c6f03 | 4116 | |
a10c6f03 CF |
4117 | int fds[2]; |
4118 | if (pipe (fds) < 0) | |
4119 | return NULL; | |
4120 | ||
d5c44ae2 | 4121 | int myix = rw == 'r' ? 0 : 1; |
d5c44ae2 CF |
4122 | |
4123 | lock_process now; | |
4124 | FILE *fp = fdopen (fds[myix], in_type); | |
4125 | if (fp) | |
4126 | { | |
4127 | /* If fds are in the range of stdin/stdout/stderr, move them | |
4128 | out of the way (possibly temporarily). Otherwise, spawn_guts | |
4129 | will be confused. We do this here rather than adding logic to | |
4130 | spawn_guts because spawn_guts is likely to be a more frequently | |
4131 | used routine and having stdin/stdout/stderr closed and reassigned | |
4132 | to pipe handles is an unlikely event. */ | |
4315703a | 4133 | int orig_fds[2] = {fds[0], fds[1]}; |
d5c44ae2 CF |
4134 | for (int i = 0; i < 2; i++) |
4135 | if (fds[i] <= 2) | |
4136 | { | |
4137 | cygheap_fdnew newfd(3); | |
4138 | cygheap->fdtab.move_fd (fds[i], newfd); | |
4139 | fds[i] = newfd; | |
4140 | } | |
c16548b2 | 4141 | |
d5c44ae2 CF |
4142 | int myfd = fds[myix]; /* myfd - convenience variable for manipulation |
4143 | of the "parent" end of the pipe. */ | |
4144 | int stdchild = myix ^ 1; /* stdchild denotes the index into fd for the | |
4145 | handle which will be redirected to | |
4146 | stdin/stdout */ | |
4315703a CF |
4147 | int __std[2]; |
4148 | __std[myix] = -1; /* -1 means don't pass this fd to the child | |
4149 | process */ | |
4150 | __std[stdchild] = fds[stdchild]; /* Do pass this as the std handle */ | |
c16548b2 | 4151 | |
d5c44ae2 CF |
4152 | const char *argv[4] = |
4153 | { | |
4154 | "/bin/sh", | |
4155 | "-c", | |
4156 | command, | |
4157 | NULL | |
4158 | }; | |
4159 | ||
4160 | /* Don't pass our end of the pipe to the child process */ | |
4161 | int fd_state = fcntl64 (myfd, F_GETFD, 0); | |
4162 | fcntl64 (myfd, F_SETFD, fd_state | FD_CLOEXEC); | |
4163 | ||
4164 | /* Also don't pass the file handle currently associated with stdin/stdout | |
4165 | to the child. This function may actually fail if the stdchild fd | |
4166 | is closed. But that's ok. */ | |
4167 | int stdchild_state = fcntl64 (stdchild, F_GETFD, 0); | |
4168 | fcntl64 (stdchild, F_SETFD, stdchild_state | FD_CLOEXEC); | |
4169 | ||
4170 | /* Start a shell process to run the given command without forking. */ | |
56a19715 CF |
4171 | pid_t pid = ch_spawn.worker ("/bin/sh", argv, cur_environ (), _P_NOWAIT, |
4172 | __std[0], __std[1]); | |
d5c44ae2 CF |
4173 | |
4174 | /* Reinstate the close-on-exec state */ | |
4175 | fcntl64 (stdchild, F_SETFD, stdchild_state); | |
4176 | ||
4177 | /* If pid >= 0 then spawn_guts succeeded. */ | |
4178 | if (pid >= 0) | |
4179 | { | |
4180 | close (fds[stdchild]); /* Close the child end of the pipe. */ | |
4181 | /* Move the fd back to its original slot if it has been moved since | |
4182 | we're always supposed to open the lowest numbered available fd | |
4183 | and, if fds[mix] != orig_fds[myix] then orig_fds[myix] is | |
4184 | presumably lower. */ | |
4185 | if (fds[myix] != orig_fds[myix]) | |
4186 | cygheap->fdtab.move_fd (fds[myix], myfd = orig_fds[myix]); | |
4187 | fhandler_pipe *fh = (fhandler_pipe *) cygheap->fdtab[myfd]; | |
4188 | /* Flag that this handle is associated with popen and then reset | |
4189 | the handle's original close-on-exec state. */ | |
4190 | fh->set_popen_pid (pid); | |
4191 | fcntl64 (myfd, F_SETFD, fd_state); | |
4192 | return fp; | |
4193 | } | |
7b9e380f | 4194 | } |
c16548b2 | 4195 | |
d5c44ae2 CF |
4196 | /* If we reach here we've seen an error but the pipe handles are open. |
4197 | Close them and return NULL. */ | |
c16548b2 CF |
4198 | int save_errno = get_errno (); |
4199 | close (fds[0]); | |
4200 | close (fds[1]); | |
4201 | set_errno (save_errno); | |
4202 | return NULL; | |
4203 | } | |
4204 | ||
4205 | int | |
4206 | pclose (FILE *fp) | |
4207 | { | |
4208 | fhandler_pipe *fh = (fhandler_pipe *) cygheap->fdtab[fileno(fp)]; | |
4209 | ||
4210 | if (fh->get_device () != FH_PIPEW && fh->get_device () != FH_PIPER) | |
4211 | { | |
4212 | set_errno (EBADF); | |
4213 | return -1; | |
4214 | } | |
4215 | ||
4216 | int pid = fh->get_popen_pid (); | |
4217 | if (!pid) | |
4218 | { | |
4219 | set_errno (ECHILD); | |
4220 | return -1; | |
4221 | } | |
4222 | ||
4223 | if (fclose (fp)) | |
4224 | return -1; | |
4225 | ||
4226 | int status; | |
4227 | while (1) | |
4228 | if (waitpid (pid, &status, 0) == pid) | |
4229 | break; | |
4230 | else if (get_errno () == EINTR) | |
4231 | continue; | |
4232 | else | |
4233 | return -1; | |
4234 | ||
4235 | return status; | |
4236 | } | |
c57b57e5 CV |
4237 | |
4238 | /* Preliminary(?) implementation of the openat family of functions. */ | |
4239 | ||
4240 | static int | |
b7291813 CV |
4241 | gen_full_path_at (char *path_ret, int dirfd, const char *pathname, |
4242 | bool null_pathname_allowed = false) | |
c57b57e5 | 4243 | { |
b7291813 CV |
4244 | /* Set null_pathname_allowed to true to allow GLIBC compatible behaviour |
4245 | for NULL pathname. Only used by futimesat. */ | |
4246 | if (!pathname && !null_pathname_allowed) | |
c57b57e5 | 4247 | { |
b7291813 | 4248 | set_errno (EFAULT); |
c57b57e5 CV |
4249 | return -1; |
4250 | } | |
b7291813 | 4251 | if (pathname) |
c57b57e5 | 4252 | { |
b7291813 CV |
4253 | if (!*pathname) |
4254 | { | |
4255 | set_errno (ENOENT); | |
4256 | return -1; | |
4257 | } | |
4258 | if (strlen (pathname) >= PATH_MAX) | |
4259 | { | |
4260 | set_errno (ENAMETOOLONG); | |
4261 | return -1; | |
4262 | } | |
c57b57e5 | 4263 | } |
1348f65b | 4264 | if (pathname && isabspath (pathname)) |
c57b57e5 CV |
4265 | stpcpy (path_ret, pathname); |
4266 | else | |
4267 | { | |
4268 | char *p; | |
4269 | ||
4270 | if (dirfd == AT_FDCWD) | |
b6c6ea43 CV |
4271 | { |
4272 | cwdstuff::cwd_lock.acquire (); | |
4273 | p = stpcpy (path_ret, cygheap->cwd.get_posix ()); | |
4274 | cwdstuff::cwd_lock.release (); | |
4275 | } | |
c57b57e5 | 4276 | else |
025c1fac | 4277 | { |
c57b57e5 CV |
4278 | cygheap_fdget cfd (dirfd); |
4279 | if (cfd < 0) | |
4280 | return -1; | |
4281 | if (!cfd->pc.isdir ()) | |
4282 | { | |
4283 | set_errno (ENOTDIR); | |
4284 | return -1; | |
4285 | } | |
4286 | p = stpcpy (path_ret, cfd->get_name ()); | |
4287 | } | |
4288 | if (!p) | |
025c1fac | 4289 | { |
c57b57e5 CV |
4290 | set_errno (ENOTDIR); |
4291 | return -1; | |
4292 | } | |
b7291813 CV |
4293 | if (pathname) |
4294 | { | |
4295 | if (p[-1] != '/') | |
4296 | *p++ = '/'; | |
4297 | stpcpy (p, pathname); | |
4298 | } | |
c57b57e5 CV |
4299 | } |
4300 | return 0; | |
4301 | } | |
4302 | ||
4303 | extern "C" int | |
4304 | openat (int dirfd, const char *pathname, int flags, ...) | |
4305 | { | |
b7291813 | 4306 | tmp_pathbuf tp; |
c57b57e5 CV |
4307 | myfault efault; |
4308 | if (efault.faulted (EFAULT)) | |
4309 | return -1; | |
c57b57e5 CV |
4310 | char *path = tp.c_get (); |
4311 | if (gen_full_path_at (path, dirfd, pathname)) | |
4312 | return -1; | |
025c1fac | 4313 | |
c57b57e5 CV |
4314 | va_list ap; |
4315 | mode_t mode; | |
4316 | ||
4317 | va_start (ap, flags); | |
4318 | mode = va_arg (ap, mode_t); | |
4319 | va_end (ap); | |
4320 | return open (path, flags, mode); | |
4321 | } | |
4322 | ||
4323 | extern "C" int | |
4324 | faccessat (int dirfd, const char *pathname, int mode, int flags) | |
4325 | { | |
b7291813 | 4326 | tmp_pathbuf tp; |
c57b57e5 CV |
4327 | myfault efault; |
4328 | if (efault.faulted (EFAULT)) | |
4329 | return -1; | |
4330 | ||
4331 | int res = -1; | |
c57b57e5 CV |
4332 | char *path = tp.c_get (); |
4333 | if (!gen_full_path_at (path, dirfd, pathname)) | |
4334 | { | |
74a67d01 EB |
4335 | if ((mode & ~(F_OK|R_OK|W_OK|X_OK)) |
4336 | || (flags & ~(AT_SYMLINK_NOFOLLOW|AT_EACCESS))) | |
c57b57e5 CV |
4337 | set_errno (EINVAL); |
4338 | else | |
025c1fac | 4339 | { |
d32e0bfc CV |
4340 | fhandler_base *fh = build_fh_name (path, (flags & AT_SYMLINK_NOFOLLOW |
4341 | ? PC_SYM_NOFOLLOW | |
4342 | : PC_SYM_FOLLOW) | |
4343 | | PC_KEEP_HANDLE, | |
025c1fac | 4344 | stat_suffixes); |
c57b57e5 CV |
4345 | if (fh) |
4346 | { | |
d32e0bfc | 4347 | res = fh->fhaccess (mode, !!(flags & AT_EACCESS)); |
c57b57e5 CV |
4348 | delete fh; |
4349 | } | |
4350 | } | |
4351 | } | |
4352 | debug_printf ("returning %d", res); | |
4353 | return res; | |
4354 | } | |
4355 | ||
4356 | extern "C" int | |
4357 | fchmodat (int dirfd, const char *pathname, mode_t mode, int flags) | |
4358 | { | |
b7291813 | 4359 | tmp_pathbuf tp; |
c57b57e5 CV |
4360 | myfault efault; |
4361 | if (efault.faulted (EFAULT)) | |
4362 | return -1; | |
2bf78f09 | 4363 | if (flags) |
74a67d01 | 4364 | { |
2bf78f09 EB |
4365 | /* BSD has lchmod, but Linux does not. POSIX says |
4366 | AT_SYMLINK_NOFOLLOW is allowed to fail on symlinks; but Linux | |
4367 | blindly fails even for non-symlinks. */ | |
4368 | set_errno ((flags & ~AT_SYMLINK_NOFOLLOW) ? EINVAL : EOPNOTSUPP); | |
74a67d01 EB |
4369 | return -1; |
4370 | } | |
c57b57e5 CV |
4371 | char *path = tp.c_get (); |
4372 | if (gen_full_path_at (path, dirfd, pathname)) | |
4373 | return -1; | |
4374 | return chmod (path, mode); | |
4375 | } | |
4376 | ||
4377 | extern "C" int | |
4378 | fchownat (int dirfd, const char *pathname, __uid32_t uid, __gid32_t gid, | |
4379 | int flags) | |
4380 | { | |
b7291813 | 4381 | tmp_pathbuf tp; |
c57b57e5 CV |
4382 | myfault efault; |
4383 | if (efault.faulted (EFAULT)) | |
4384 | return -1; | |
74a67d01 EB |
4385 | if (flags & ~AT_SYMLINK_NOFOLLOW) |
4386 | { | |
4387 | set_errno (EINVAL); | |
4388 | return -1; | |
4389 | } | |
c57b57e5 CV |
4390 | char *path = tp.c_get (); |
4391 | if (gen_full_path_at (path, dirfd, pathname)) | |
4392 | return -1; | |
4393 | return chown_worker (path, (flags & AT_SYMLINK_NOFOLLOW) | |
4394 | ? PC_SYM_NOFOLLOW : PC_SYM_FOLLOW, uid, gid); | |
4395 | } | |
4396 | ||
4397 | extern "C" int | |
4398 | fstatat (int dirfd, const char *pathname, struct __stat64 *st, int flags) | |
4399 | { | |
b7291813 | 4400 | tmp_pathbuf tp; |
c57b57e5 CV |
4401 | myfault efault; |
4402 | if (efault.faulted (EFAULT)) | |
4403 | return -1; | |
74a67d01 EB |
4404 | if (flags & ~AT_SYMLINK_NOFOLLOW) |
4405 | { | |
4406 | set_errno (EINVAL); | |
4407 | return -1; | |
4408 | } | |
c57b57e5 CV |
4409 | char *path = tp.c_get (); |
4410 | if (gen_full_path_at (path, dirfd, pathname)) | |
4411 | return -1; | |
396561aa CV |
4412 | path_conv pc (path, ((flags & AT_SYMLINK_NOFOLLOW) |
4413 | ? PC_SYM_NOFOLLOW : PC_SYM_FOLLOW) | |
4414 | | PC_POSIX | PC_KEEP_HANDLE, stat_suffixes); | |
4415 | return stat_worker (pc, st); | |
c57b57e5 CV |
4416 | } |
4417 | ||
eba32ec8 CV |
4418 | extern int utimens_worker (path_conv &, const struct timespec *); |
4419 | ||
4420 | extern "C" int | |
4421 | utimensat (int dirfd, const char *pathname, const struct timespec *times, | |
4422 | int flags) | |
4423 | { | |
b7291813 | 4424 | tmp_pathbuf tp; |
eba32ec8 CV |
4425 | myfault efault; |
4426 | if (efault.faulted (EFAULT)) | |
4427 | return -1; | |
eba32ec8 | 4428 | char *path = tp.c_get (); |
74a67d01 EB |
4429 | if (flags & ~AT_SYMLINK_NOFOLLOW) |
4430 | { | |
4431 | set_errno (EINVAL); | |
4432 | return -1; | |
4433 | } | |
eba32ec8 CV |
4434 | if (gen_full_path_at (path, dirfd, pathname)) |
4435 | return -1; | |
4436 | path_conv win32 (path, PC_POSIX | ((flags & AT_SYMLINK_NOFOLLOW) | |
4437 | ? PC_SYM_NOFOLLOW : PC_SYM_FOLLOW), | |
4438 | stat_suffixes); | |
4439 | return utimens_worker (win32, times); | |
4440 | } | |
4441 | ||
c57b57e5 CV |
4442 | extern "C" int |
4443 | futimesat (int dirfd, const char *pathname, const struct timeval *times) | |
4444 | { | |
b7291813 | 4445 | tmp_pathbuf tp; |
c57b57e5 CV |
4446 | myfault efault; |
4447 | if (efault.faulted (EFAULT)) | |
4448 | return -1; | |
c57b57e5 | 4449 | char *path = tp.c_get (); |
b7291813 | 4450 | if (gen_full_path_at (path, dirfd, pathname, true)) |
c57b57e5 CV |
4451 | return -1; |
4452 | return utimes (path, times); | |
4453 | } | |
4454 | ||
4455 | extern "C" int | |
4456 | linkat (int olddirfd, const char *oldpathname, | |
4457 | int newdirfd, const char *newpathname, | |
4458 | int flags) | |
4459 | { | |
b7291813 | 4460 | tmp_pathbuf tp; |
c57b57e5 CV |
4461 | myfault efault; |
4462 | if (efault.faulted (EFAULT)) | |
4463 | return -1; | |
74a67d01 EB |
4464 | if (flags & ~AT_SYMLINK_FOLLOW) |
4465 | { | |
4466 | set_errno (EINVAL); | |
4467 | return -1; | |
4468 | } | |
c57b57e5 CV |
4469 | char *oldpath = tp.c_get (); |
4470 | if (gen_full_path_at (oldpath, olddirfd, oldpathname)) | |
4471 | return -1; | |
4472 | char *newpath = tp.c_get (); | |
4473 | if (gen_full_path_at (newpath, newdirfd, newpathname)) | |
4474 | return -1; | |
4475 | if (flags & AT_SYMLINK_FOLLOW) | |
4476 | { | |
4477 | path_conv old_name (oldpath, PC_SYM_FOLLOW | PC_POSIX, stat_suffixes); | |
4478 | if (old_name.error) | |
4479 | { | |
4480 | set_errno (old_name.error); | |
4481 | return -1; | |
4482 | } | |
4483 | strcpy (oldpath, old_name.normalized_path); | |
4484 | } | |
4485 | return link (oldpath, newpath); | |
4486 | } | |
4487 | ||
4488 | extern "C" int | |
4489 | mkdirat (int dirfd, const char *pathname, mode_t mode) | |
4490 | { | |
b7291813 | 4491 | tmp_pathbuf tp; |
c57b57e5 CV |
4492 | myfault efault; |
4493 | if (efault.faulted (EFAULT)) | |
4494 | return -1; | |
c57b57e5 CV |
4495 | char *path = tp.c_get (); |
4496 | if (gen_full_path_at (path, dirfd, pathname)) | |
4497 | return -1; | |
4498 | return mkdir (path, mode); | |
4499 | } | |
4500 | ||
4501 | extern "C" int | |
4502 | mkfifoat (int dirfd, const char *pathname, mode_t mode) | |
4503 | { | |
b7291813 | 4504 | tmp_pathbuf tp; |
c57b57e5 CV |
4505 | myfault efault; |
4506 | if (efault.faulted (EFAULT)) | |
4507 | return -1; | |
c57b57e5 CV |
4508 | char *path = tp.c_get (); |
4509 | if (gen_full_path_at (path, dirfd, pathname)) | |
4510 | return -1; | |
4511 | return mkfifo (path, mode); | |
4512 | } | |
4513 | ||
4514 | extern "C" int | |
4515 | mknodat (int dirfd, const char *pathname, mode_t mode, __dev32_t dev) | |
4516 | { | |
b7291813 | 4517 | tmp_pathbuf tp; |
c57b57e5 CV |
4518 | myfault efault; |
4519 | if (efault.faulted (EFAULT)) | |
4520 | return -1; | |
c57b57e5 CV |
4521 | char *path = tp.c_get (); |
4522 | if (gen_full_path_at (path, dirfd, pathname)) | |
4523 | return -1; | |
4524 | return mknod32 (path, mode, dev); | |
4525 | } | |
4526 | ||
4527 | extern "C" ssize_t | |
4528 | readlinkat (int dirfd, const char *pathname, char *buf, size_t bufsize) | |
4529 | { | |
b7291813 | 4530 | tmp_pathbuf tp; |
c57b57e5 CV |
4531 | myfault efault; |
4532 | if (efault.faulted (EFAULT)) | |
4533 | return -1; | |
c57b57e5 CV |
4534 | char *path = tp.c_get (); |
4535 | if (gen_full_path_at (path, dirfd, pathname)) | |
4536 | return -1; | |
4537 | return readlink (path, buf, bufsize); | |
4538 | } | |
4539 | ||
4540 | extern "C" int | |
4541 | renameat (int olddirfd, const char *oldpathname, | |
4542 | int newdirfd, const char *newpathname) | |
4543 | { | |
b7291813 | 4544 | tmp_pathbuf tp; |
c57b57e5 CV |
4545 | myfault efault; |
4546 | if (efault.faulted (EFAULT)) | |
4547 | return -1; | |
c57b57e5 CV |
4548 | char *oldpath = tp.c_get (); |
4549 | if (gen_full_path_at (oldpath, olddirfd, oldpathname)) | |
4550 | return -1; | |
4551 | char *newpath = tp.c_get (); | |
4552 | if (gen_full_path_at (newpath, newdirfd, newpathname)) | |
4553 | return -1; | |
4554 | return rename (oldpath, newpath); | |
4555 | } | |
4556 | ||
e587f14b YS |
4557 | extern "C" int |
4558 | scandirat (int dirfd, const char *pathname, struct dirent ***namelist, | |
4559 | int (*select) (const struct dirent *), | |
4560 | int (*compar) (const struct dirent **, const struct dirent **)) | |
4561 | { | |
4562 | tmp_pathbuf tp; | |
4563 | myfault efault; | |
4564 | if (efault.faulted (EFAULT)) | |
4565 | return -1; | |
4566 | char *path = tp.c_get (); | |
4567 | if (gen_full_path_at (path, dirfd, pathname)) | |
4568 | return -1; | |
4569 | return scandir (pathname, namelist, select, compar); | |
4570 | } | |
4571 | ||
c57b57e5 CV |
4572 | extern "C" int |
4573 | symlinkat (const char *oldpath, int newdirfd, const char *newpathname) | |
4574 | { | |
b7291813 | 4575 | tmp_pathbuf tp; |
c57b57e5 CV |
4576 | myfault efault; |
4577 | if (efault.faulted (EFAULT)) | |
4578 | return -1; | |
c57b57e5 CV |
4579 | char *newpath = tp.c_get (); |
4580 | if (gen_full_path_at (newpath, newdirfd, newpathname)) | |
4581 | return -1; | |
4582 | return symlink (oldpath, newpath); | |
4583 | } | |
4584 | ||
4585 | extern "C" int | |
4586 | unlinkat (int dirfd, const char *pathname, int flags) | |
4587 | { | |
b7291813 | 4588 | tmp_pathbuf tp; |
c57b57e5 CV |
4589 | myfault efault; |
4590 | if (efault.faulted (EFAULT)) | |
4591 | return -1; | |
74a67d01 EB |
4592 | if (flags & ~AT_REMOVEDIR) |
4593 | { | |
4594 | set_errno (EINVAL); | |
4595 | return -1; | |
4596 | } | |
c57b57e5 CV |
4597 | char *path = tp.c_get (); |
4598 | if (gen_full_path_at (path, dirfd, pathname)) | |
4599 | return -1; | |
4600 | return (flags & AT_REMOVEDIR) ? rmdir (path) : unlink (path); | |
4601 | } |