Cygwin: strptime: add release note
[newlib-cygwin.git] / winsup / cygwin / syscalls.cc
blob600c6c54e343ccf608c930b6a9c5439ed0f4acf1
1 /* syscalls.cc: syscalls
3 This file is part of Cygwin.
5 This software is a copyrighted work licensed under the terms of the
6 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
7 details. */
9 #include "winsup.h"
10 #include "miscfuncs.h"
11 #include <sys/stat.h>
12 #include <sys/vfs.h> /* needed for statfs */
13 #include <sys/statvfs.h> /* needed for statvfs */
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <process.h>
17 #include <utmp.h>
18 #include <utmpx.h>
19 #include <sys/uio.h>
20 #include <ctype.h>
21 #include <wctype.h>
22 #include <unistd.h>
23 #include <sys/wait.h>
24 #include <dirent.h>
25 #include <ntsecapi.h>
26 #include <iptypes.h>
27 #include "ntdll.h"
29 #include <cygwin/version.h>
30 #include "cygerrno.h"
31 #include "perprocess.h"
32 #include "security.h"
33 #include "path.h"
34 #include "fhandler.h"
35 #include "dtable.h"
36 #include "sigproc.h"
37 #include "pinfo.h"
38 #include "shared_info.h"
39 #include "cygheap.h"
40 #include "registry.h"
41 #include "environ.h"
42 #include "tls_pbuf.h"
43 #include "sync.h"
44 #include "child_info.h"
45 #include <cygwin/fs.h> /* needed for RENAME_NOREPLACE */
46 #include <sys/reent.h> /* needed for _fwalk_sglue() declaration */
48 static int mknod_worker (path_conv &, mode_t, _major_t, _minor_t);
50 /* Close all files and process any queued deletions.
51 Lots of unix style applications will open a tmp file, unlink it,
52 but never call close. This function is called by _exit to
53 ensure we don't leave any such files lying around. */
55 void
56 close_all_files (bool norelease)
58 cygheap->fdtab.lock ();
60 semaphore::terminate ();
62 HANDLE h = NULL;
64 for (int i = 0; i < (int) cygheap->fdtab.size; i++)
66 cygheap_fdget cfd (i, false, false);
67 if (cfd >= 0)
69 debug_only_printf ("closing fd %d", i);
70 if (i == 2 && cfd->get_dev () != FH_PIPEW)
71 DuplicateHandle (GetCurrentProcess (), cfd->get_output_handle (),
72 GetCurrentProcess (), &h,
73 0, false, DUPLICATE_SAME_ACCESS);
74 cfd->close_with_arch ();
75 if (!norelease)
76 cfd.release ();
80 if (!have_execed && cygheap->ctty)
81 cygheap->close_ctty ();
83 if (h)
84 SetStdHandle (STD_ERROR_HANDLE, h);
85 cygheap->fdtab.unlock ();
88 /* Close or set the close-on-exec flag for all open file descriptors
89 from firstfd to lastfd. CLOSE_RANGE_UNSHARE is not supported.
90 Available on FreeBSD since 13 and Linux since 5.9 */
91 extern "C" int
92 close_range (unsigned int firstfd, unsigned int lastfd, int flags)
94 pthread_testcancel ();
96 if (!(firstfd <= lastfd && !(flags & ~CLOSE_RANGE_CLOEXEC)))
98 set_errno (EINVAL);
99 return -1;
102 cygheap->fdtab.lock ();
104 unsigned int size = (lastfd < cygheap->fdtab.size ? lastfd + 1 :
105 cygheap->fdtab.size);
107 for (unsigned int i = firstfd; i < size; i++)
109 cygheap_fdget cfd ((int) i, false, false);
110 if (cfd < 0)
111 continue;
113 if (flags & CLOSE_RANGE_CLOEXEC)
115 syscall_printf ("set FD_CLOEXEC on fd %u", i);
116 cfd->fcntl (F_SETFD, FD_CLOEXEC);
118 else
120 syscall_printf ("closing fd %u", i);
121 cfd->close_with_arch ();
122 cfd.release ();
126 cygheap->fdtab.unlock ();
127 return 0;
130 extern "C" int
131 dup (int fd)
133 int res;
134 cygheap_fdnew newfd;
135 if (newfd < 0)
136 res = -1;
137 else
138 res = cygheap->fdtab.dup3 (fd, newfd, 0);
139 syscall_printf ("%R = dup(%d)", res, fd);
140 return res;
143 inline int
144 dup_finish (int oldfd, int newfd, int flags)
146 int res;
147 if ((res = cygheap->fdtab.dup3 (oldfd, newfd, flags | O_EXCL)) == newfd)
149 cygheap_fdget (newfd)->inc_refcnt ();
150 cygheap->fdtab.unlock (); /* dup3 exits with lock set on success */
152 return res;
155 extern "C" int
156 dup2 (int oldfd, int newfd)
158 int res;
159 if (newfd >= OPEN_MAX || newfd < 0)
161 set_errno (EBADF);
162 res = -1;
164 else if (newfd == oldfd)
166 cygheap_fdget cfd (oldfd);
167 res = (cfd >= 0) ? oldfd : -1;
169 else
170 res = dup_finish (oldfd, newfd, 0);
172 syscall_printf ("%R = dup2(%d, %d)", res, oldfd, newfd);
173 return res;
176 extern "C" int
177 dup3 (int oldfd, int newfd, int flags)
179 int res;
180 if (newfd >= OPEN_MAX)
182 set_errno (EBADF);
183 res = -1;
185 else if (newfd == oldfd)
187 cygheap_fdget cfd (oldfd, false, false);
188 set_errno (cfd < 0 ? EBADF : EINVAL);
189 res = -1;
191 else
192 res = dup_finish (oldfd, newfd, flags);
194 syscall_printf ("%R = dup3(%d, %d, %y)", res, oldfd, newfd, flags);
195 return res;
198 static const char desktop_ini[] =
199 "[.ShellClassInfo]\r\n"
200 "CLSID={645FF040-5081-101B-9F08-00AA002F954E}\r\n"
201 "LocalizedResourceName=@%SystemRoot%\\system32\\shell32.dll,-8964\r\n";
203 enum bin_status
205 dont_move,
206 move_to_bin,
207 has_been_moved,
208 dir_not_empty
211 /* Typically the recycler on drive C has been created at installation
212 time. The name is then written in camel back. On any other drive,
213 the recycler is created on first usage. shell32.dll then creates
214 the recycler in all upper case. That's only important if the entire
215 operation is running case sensitive. */
216 static WCHAR recycler_basename_drive_c[] = L"\\$Recycle.Bin\\";
217 static WCHAR recycler_basename_other[] = L"\\$RECYCLE.BIN\\";
219 static bin_status
220 try_to_bin (path_conv &pc, HANDLE &fh, ACCESS_MASK access, ULONG flags)
222 bin_status bin_stat = move_to_bin;
223 NTSTATUS status;
224 OBJECT_ATTRIBUTES attr;
225 IO_STATUS_BLOCK io;
226 HANDLE rootdir = NULL, recyclerdir = NULL, tmp_fh = NULL;
227 USHORT recycler_base_len = 0, recycler_user_len = 0;
228 UNICODE_STRING root, recycler, fname;
229 PWCHAR recycler_basename = NULL;
230 WCHAR recyclerbuf[NAME_MAX + 1]; /* Enough for recycler + SID + filename */
231 PFILE_NAME_INFORMATION pfni;
232 PFILE_INTERNAL_INFORMATION pfii;
233 PFILE_RENAME_INFORMATION pfri;
234 ULONG frisiz;
235 FILE_DISPOSITION_INFORMATION disp = { TRUE };
236 bool fs_has_per_user_recycler = pc.fs_is_ntfs () || pc.fs_is_refs ();
238 tmp_pathbuf tp;
239 PBYTE infobuf = (PBYTE) tp.w_get ();
241 pfni = (PFILE_NAME_INFORMATION) infobuf;
242 status = NtQueryInformationFile (fh, &io, pfni, NT_MAX_PATH * sizeof (WCHAR),
243 FileNameInformation);
244 if (!NT_SUCCESS (status))
246 debug_printf ("NtQueryInformationFile (%S, FileNameInformation) "
247 "failed, status = %y", pc.get_nt_native_path (), status);
248 goto out;
250 /* The filename could change, the parent dir not. So we split both paths
251 and take the prefix. However, there are two special cases:
252 - The handle refers to the root dir of the volume.
253 - The handle refers to the recycler or a subdir.
254 Both cases are handled by just returning and not even trying to move
255 them into the recycler. */
256 if (pfni->FileNameLength == 2) /* root dir. */
257 goto out;
258 /* Initialize recycler path. */
259 RtlInitEmptyUnicodeString (&recycler, recyclerbuf, sizeof recyclerbuf);
260 if (!pc.isremote ())
262 recycler_basename = wcsncmp (pc.get_nt_native_path ()->Buffer,
263 L"\\??\\C:\\", 7)
264 ? recycler_basename_other
265 : recycler_basename_drive_c;
266 RtlAppendUnicodeToString (&recycler, recycler_basename);
267 RtlInitCountedUnicodeString(&fname, pfni->FileName, pfni->FileNameLength);
268 /* Is the file a subdir of the recycler? */
269 if (RtlEqualUnicodePathPrefix (&fname, &recycler, TRUE))
270 goto out;
271 /* Is fname the recycler? Temporarily hide trailing backslash. */
272 recycler.Length -= sizeof (WCHAR);
273 if (RtlEqualUnicodeString (&fname, &recycler, TRUE))
274 goto out;
275 /* Is fname really a subcomponent of the full path? If not, there's
276 a high probability we're accessing the file via a virtual drive
277 created with "subst". Check and accommodate it. Note that we
278 only get here if the virtual drive is really pointing to a local
279 drive. Otherwise pc.isremote () returns "true". */
280 if (!RtlEqualUnicodePathSuffix (pc.get_nt_native_path (), &fname, TRUE))
282 WCHAR drive[3] = { pc.get_nt_native_path ()->Buffer[4], ':', '\0' };
283 PWCHAR tgt = tp.w_get ();
285 if (QueryDosDeviceW (drive, tgt, NT_MAX_PATH)
286 && !wcsncmp (tgt, L"\\??\\", 4))
288 UNICODE_STRING new_path;
290 wcsncpy (tgt + 6, fname.Buffer, fname.Length / sizeof (WCHAR));
291 RtlInitCountedUnicodeString(&new_path, tgt,
292 6 * sizeof (WCHAR) + fname.Length);
293 pc.set_nt_native_path (&new_path);
296 /* Create root dir path from file name information. */
297 RtlSplitUnicodePath (&fname, &fname, NULL);
298 RtlSplitUnicodePath (pc.get_nt_native_path (), &root, NULL);
299 root.Length -= fname.Length - sizeof (WCHAR);
301 /* Open root directory. All recycler bin ops are caseinsensitive. */
302 InitializeObjectAttributes (&attr, &root, OBJ_CASE_INSENSITIVE,
303 NULL, NULL);
304 status = NtOpenFile (&rootdir, FILE_TRAVERSE, &attr, &io,
305 FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT);
306 if (!NT_SUCCESS (status))
308 debug_printf ("NtOpenFile (%S) failed, status = %y", &root, status);
309 goto out;
312 /* Strip leading backslash */
313 ++recycler.Buffer;
314 recycler.Length -= sizeof (WCHAR);
315 /* Store length of recycler base dir, if it's necessary to create it. */
316 recycler_base_len = recycler.Length;
317 /* On NTFS or ReFS the recycler dir contains user specific subdirs, which
318 are the actual recycle bins per user. The name of this dir is the
319 string representation of the user SID. */
320 if (fs_has_per_user_recycler)
322 UNICODE_STRING sid;
323 WCHAR sidbuf[128];
324 /* Unhide trailing backslash. */
325 recycler.Length += sizeof (WCHAR);
326 RtlInitEmptyUnicodeString (&sid, sidbuf, sizeof sidbuf);
327 RtlConvertSidToUnicodeString (&sid, cygheap->user.sid (), FALSE);
328 RtlAppendUnicodeStringToString (&recycler, &sid);
329 recycler_user_len = recycler.Length;
331 RtlAppendUnicodeToString (&recycler, L"\\");
333 if (pc.file_attributes () & FILE_ATTRIBUTE_TEMPORARY)
335 UNICODE_STRING basename;
337 RtlSplitUnicodePath (pc.get_nt_native_path (), NULL, &basename);
338 RtlAppendUnicodeToString (&recycler, basename.Buffer);
340 else
342 /* Create unique filename. Start with a dot, followed by "cyg"
343 transposed into the Unicode low surrogate area (U+dc00) on file
344 systems supporting Unicode (except Samba), followed by the inode
345 number in hex, followed by a path hash in hex. The combination
346 allows to remove multiple hardlinks to the same file. */
347 RtlAppendUnicodeToString (&recycler,
348 (pc.fs_flags () & FILE_UNICODE_ON_DISK
349 && !pc.fs_is_samba ())
350 ? L".\xdc63\xdc79\xdc67" : L".cyg");
351 pfii = (PFILE_INTERNAL_INFORMATION) infobuf;
352 status = NtQueryInformationFile (fh, &io, pfii, sizeof *pfii,
353 FileInternalInformation);
354 if (!NT_SUCCESS (status))
356 debug_printf ("NtQueryInformationFile (%S, FileInternalInformation) "
357 "failed, status = %y",
358 pc.get_nt_native_path (), status);
359 goto out;
361 RtlInt64ToHexUnicodeString (pfii->IndexNumber.QuadPart, &recycler, TRUE);
362 RtlInt64ToHexUnicodeString (hash_path_name (0, pc.get_nt_native_path ()),
363 &recycler, TRUE);
365 /* Shoot. */
366 pfri = (PFILE_RENAME_INFORMATION) infobuf;
367 pfri->ReplaceIfExists = TRUE;
368 pfri->RootDirectory = rootdir;
369 pfri->FileNameLength = recycler.Length;
370 memcpy (pfri->FileName, recycler.Buffer, recycler.Length);
371 frisiz = sizeof *pfri + pfri->FileNameLength - sizeof (WCHAR);
373 status = NtSetInformationFile (fh, &io, pfri, frisiz, FileRenameInformation);
374 if (status == STATUS_OBJECT_PATH_NOT_FOUND && !pc.isremote ())
376 /* The recycler and/or the recycler/SID directory don't exist, or the
377 case of recycler dir has changed and the rename op is case sensitive.
378 First reopen root dir with permission to create subdirs. */
379 NtClose (rootdir);
380 InitializeObjectAttributes (&attr, &root, OBJ_CASE_INSENSITIVE,
381 NULL, NULL);
382 status = NtOpenFile (&rootdir, FILE_ADD_SUBDIRECTORY, &attr, &io,
383 FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT);
384 if (!NT_SUCCESS (status))
386 debug_printf ("NtOpenFile (%S) failed, status = %y",
387 &recycler, status);
388 goto out;
390 /* Correct the rootdir HANDLE in pfri after reopening the dir. */
391 pfri->RootDirectory = rootdir;
392 /* Then check if recycler exists by opening and potentially creating it.
393 Yes, we can really do that. Typically the recycle bin is created
394 by the first user actually using the bin. */
395 InitializeObjectAttributes (&attr, &recycler, OBJ_CASE_INSENSITIVE,
396 rootdir, recycler_sd (true, true));
397 recycler.Length = recycler_base_len;
398 status = NtCreateFile (&recyclerdir,
399 READ_CONTROL
400 | (fs_has_per_user_recycler ? 0 : FILE_ADD_FILE),
401 &attr, &io, NULL,
402 FILE_ATTRIBUTE_DIRECTORY
403 | FILE_ATTRIBUTE_SYSTEM
404 | FILE_ATTRIBUTE_HIDDEN,
405 FILE_SHARE_VALID_FLAGS, FILE_OPEN_IF,
406 FILE_DIRECTORY_FILE, NULL, 0);
407 if (!NT_SUCCESS (status))
409 debug_printf ("NtCreateFile (%S) failed, status = %y",
410 &recycler, status);
411 goto out;
413 /* If we opened the recycler (in contrast to creating it) and our
414 rename op is case sensitive, fetch the actual case of the recycler
415 and store the name in recycler_basename, as well as in pfri->FileName
416 for the below 2nd try to rename the file. */
417 if (io.Information == FILE_OPENED && !pc.objcaseinsensitive ())
419 pfni = (PFILE_NAME_INFORMATION) tp.w_get ();
420 status = NtQueryInformationFile (recyclerdir, &io, pfni,
421 NT_MAX_PATH * sizeof (WCHAR),
422 FileNameInformation);
423 if (NT_SUCCESS (status))
425 size_t len = pfni->FileNameLength / sizeof (WCHAR) - 1;
426 PWCHAR p = pfni->FileName + 1;
427 p[len] = L'\0';
428 wcpncpy (pfri->FileName, p, len);
429 wcpncpy (recycler_basename + 1, p, len);
433 /* Next, if necessary, check if the recycler/SID dir exists and
434 create it if not. */
435 if (fs_has_per_user_recycler)
437 NtClose (recyclerdir);
438 recycler.Length = recycler_user_len;
439 InitializeObjectAttributes (&attr, &recycler, OBJ_CASE_INSENSITIVE,
440 rootdir, recycler_sd (false, true));
441 status = NtCreateFile (&recyclerdir, READ_CONTROL | FILE_ADD_FILE,
442 &attr, &io, NULL, FILE_ATTRIBUTE_DIRECTORY
443 | FILE_ATTRIBUTE_SYSTEM
444 | FILE_ATTRIBUTE_HIDDEN,
445 FILE_SHARE_VALID_FLAGS, FILE_OPEN_IF,
446 FILE_DIRECTORY_FILE, NULL, 0);
447 if (!NT_SUCCESS (status))
449 debug_printf ("NtCreateFile (%S) failed, status = %y",
450 &recycler, status);
451 goto out;
454 /* The desktop.ini file is expected by Windows Explorer. Otherwise,
455 the created bin is treated as corrupted */
456 if (io.Information == FILE_CREATED)
458 RtlInitUnicodeString (&fname, L"desktop.ini");
459 InitializeObjectAttributes (&attr, &fname, OBJ_CASE_INSENSITIVE,
460 recyclerdir, recycler_sd (false, false));
461 status = NtCreateFile (&tmp_fh, FILE_GENERIC_WRITE, &attr, &io, NULL,
462 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
463 FILE_SHARE_VALID_FLAGS, FILE_CREATE,
464 FILE_SYNCHRONOUS_IO_NONALERT
465 | FILE_NON_DIRECTORY_FILE, NULL, 0);
466 if (!NT_SUCCESS (status))
467 debug_printf ("NtCreateFile (%S) failed, status = %y",
468 &recycler, status);
469 else
471 status = NtWriteFile (tmp_fh, NULL, NULL, NULL, &io,
472 (PVOID) desktop_ini, sizeof desktop_ini - 1,
473 NULL, NULL);
474 if (!NT_SUCCESS (status))
475 debug_printf ("NtWriteFile (%S) failed, status = %y",
476 &fname, status);
477 NtClose (tmp_fh);
480 NtClose (recyclerdir);
481 /* Shoot again. */
482 status = NtSetInformationFile (fh, &io, pfri, frisiz,
483 FileRenameInformation);
485 if (!NT_SUCCESS (status))
487 debug_printf ("Move %S to %S failed, status = %y",
488 pc.get_nt_native_path (), &recycler, status);
489 goto out;
491 /* Moving to the bin worked. */
492 bin_stat = has_been_moved;
493 /* If we're only moving a just created O_TMPFILE, we're done here. */
494 if (pc.file_attributes () & FILE_ATTRIBUTE_TEMPORARY)
495 goto out;
496 /* Now we try to set the delete disposition. If that worked, we're done.
497 We try this here first, as long as we still have the open handle.
498 Otherwise the below code closes the handle to allow replacing the file. */
499 status = NtSetInformationFile (fh, &io, &disp, sizeof disp,
500 FileDispositionInformation);
501 switch (status)
503 case STATUS_SUCCESS:
504 break;
505 case STATUS_DIRECTORY_NOT_EMPTY:
506 /* Uh oh! This was supposed to be avoided by the check_dir_not_empty
507 test in unlink_nt, but given that the test isn't atomic, this *can*
508 happen. Try to move the dir back ASAP. */
509 pfri->RootDirectory = NULL;
510 pfri->FileNameLength = pc.get_nt_native_path ()->Length;
511 memcpy (pfri->FileName, pc.get_nt_native_path ()->Buffer,
512 pc.get_nt_native_path ()->Length);
513 frisiz = sizeof *pfri + pfri->FileNameLength - sizeof (WCHAR);
514 if (NT_SUCCESS (NtSetInformationFile (fh, &io, pfri, frisiz,
515 FileRenameInformation)))
517 /* Give notice to unlink_nt and leave immediately. This avoids
518 closing the handle, which might still be used if called from
519 the rm -r workaround code. */
520 bin_stat = dir_not_empty;
521 goto out;
523 debug_printf ("Renaming dir %S back to %S failed, status = %y",
524 &recycler, pc.get_nt_native_path (), status);
525 break;
526 case STATUS_FILE_RENAMED:
527 /* On NFS, the subsequent request to set the delete disposition fails
528 with STATUS_FILE_RENAMED. We have to reopen the file, close the
529 original handle, and set the delete disposition on the reopened
530 handle to make sure setting delete disposition works. */
531 InitializeObjectAttributes (&attr, &ro_u_empty, 0, fh, NULL);
532 status = NtOpenFile (&tmp_fh, access, &attr, &io,
533 FILE_SHARE_VALID_FLAGS, flags);
534 if (!NT_SUCCESS (status))
535 debug_printf ("NtOpenFile (%S) for reopening in renamed case failed, "
536 "status = %y", pc.get_nt_native_path (), status);
537 else
539 NtClose (fh);
540 fh = tmp_fh;
541 status = NtSetInformationFile (fh, &io, &disp, sizeof disp,
542 FileDispositionInformation);
543 if (!NT_SUCCESS (status))
544 debug_printf ("Setting delete disposition %S (%S) in renamed "
545 "case failed, status = %y",
546 &recycler, pc.get_nt_native_path (), status);
548 break;
549 default:
550 debug_printf ("Setting delete disposition on %S (%S) failed, status = %y",
551 &recycler, pc.get_nt_native_path (), status);
552 break;
554 /* In case of success, restore R/O attribute to accommodate hardlinks.
555 That leaves potentially hardlinks around with the R/O bit suddenly
556 off if setting the delete disposition failed, but please, keep in
557 mind this is really a border case only. */
558 if ((access & FILE_WRITE_ATTRIBUTES) && NT_SUCCESS (status) && !pc.isdir ())
559 NtSetAttributesFile (fh, pc.file_attributes ());
560 NtClose (fh);
561 fh = NULL; /* So unlink_nt doesn't close the handle twice. */
562 /* On success or when trying to unlink a directory we just return here.
563 The below code only works for files. It also fails on NFS. */
564 if (NT_SUCCESS (status) || pc.isdir () || pc.fs_is_nfs ())
565 goto out;
566 /* The final trick. We create a temporary file with delete-on-close
567 semantic and rename that file to the file just moved to the bin.
568 This typically overwrites the original file and we get rid of it,
569 even if neither setting the delete dispostion, nor setting
570 delete-on-close on the original file succeeds. There are still
571 cases in which this fails, for instance, when trying to delete a
572 hardlink to a DLL used by the unlinking application itself. */
573 if (pc.isremote ())
575 /* In the remote case we need the full path, but recycler is only
576 a relative path. Convert to absolute path. */
577 RtlInitEmptyUnicodeString (&fname, tp.w_get (),
578 (NT_MAX_PATH - 1) * sizeof (WCHAR));
579 RtlCopyUnicodeString (&fname, pc.get_nt_native_path ());
580 RtlSplitUnicodePath (&fname, &fname, NULL);
581 /* Reset max length, overwritten by RtlSplitUnicodePath. */
582 fname.MaximumLength = (NT_MAX_PATH - 1) * sizeof (WCHAR); /* reset */
583 RtlAppendUnicodeStringToString (&fname, &recycler);
585 else
586 fname = recycler;
587 RtlAppendUnicodeToString (&fname, L"X");
588 InitializeObjectAttributes (&attr, &fname, 0, rootdir, NULL);
589 status = NtCreateFile (&tmp_fh, DELETE, &attr, &io, NULL,
590 FILE_ATTRIBUTE_NORMAL, 0, FILE_SUPERSEDE,
591 FILE_NON_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE,
592 NULL, 0);
593 if (!NT_SUCCESS (status))
595 debug_printf ("Creating file %S for overwriting %S (%S) failed, "
596 "status = %y", &fname, &recycler, pc.get_nt_native_path (),
597 status);
598 goto out;
600 status = NtSetInformationFile (tmp_fh, &io, pfri, frisiz,
601 FileRenameInformation);
602 NtClose (tmp_fh);
603 if (!NT_SUCCESS (status))
604 debug_printf ("Overwriting %S (%S) with %S failed, status = %y",
605 &recycler, pc.get_nt_native_path (), &fname, status);
607 out:
608 if (rootdir)
609 NtClose (rootdir);
610 debug_printf ("%S, return bin_status %d", pc.get_nt_native_path (), bin_stat);
611 return bin_stat;
614 static NTSTATUS
615 check_dir_not_empty (HANDLE dir, path_conv &pc)
617 IO_STATUS_BLOCK io;
618 const ULONG bufsiz = 3 * sizeof (FILE_NAMES_INFORMATION)
619 + 3 * NAME_MAX * sizeof (WCHAR);
620 PFILE_NAMES_INFORMATION pfni = (PFILE_NAMES_INFORMATION)
621 alloca (bufsiz);
622 NTSTATUS status = NtQueryDirectoryFile (dir, NULL, NULL, 0, &io, pfni,
623 bufsiz, FileNamesInformation,
624 FALSE, NULL, TRUE);
625 if (!NT_SUCCESS (status))
627 debug_printf ("Checking if directory %S is empty failed, status = %y",
628 pc.get_nt_native_path (), status);
629 return status;
631 int cnt = 1;
634 while (pfni->NextEntryOffset)
636 if (++cnt > 2)
638 UNICODE_STRING fname;
639 OBJECT_ATTRIBUTES attr;
640 FILE_BASIC_INFORMATION fbi;
642 pfni = (PFILE_NAMES_INFORMATION)
643 ((caddr_t) pfni + pfni->NextEntryOffset);
644 RtlInitCountedUnicodeString(&fname, pfni->FileName,
645 pfni->FileNameLength);
646 InitializeObjectAttributes (&attr, &fname, 0, dir, NULL);
647 status = NtQueryAttributesFile (&attr, &fbi);
648 /* Intensive testing shows that sometimes directories, for which
649 the delete disposition has already been set, and the deleting
650 handle is already closed, can linger in the parent dir for a
651 couple of ms for no apparent reason (Windows Defender or other
652 real-time scanners are suspect).
654 A fast rm -r is capable to exploit this problem. Setting the
655 delete disposition of the parent dir then fails with
656 STATUS_DIRECTORY_NOT_EMPTY. Examining the content of the
657 affected dir can then show either that the dir is empty, or it
658 can contain a lingering subdir. Calling NtQueryAttributesFile
659 on that subdir returns with STATUS_DELETE_PENDING, or it
660 disappeared before that call.
662 That's what we do here. If NtQueryAttributesFile succeeded,
663 or if the error code does not indicate an already deleted
664 entry, STATUS_DIRECTORY_NOT_EMPTY is returned.
666 Otherwise STATUS_SUCCESS is returned. Read on in unlink_nt. */
667 if (status != STATUS_DELETE_PENDING
668 && status != STATUS_OBJECT_NAME_NOT_FOUND
669 && status != STATUS_OBJECT_PATH_NOT_FOUND)
671 debug_printf ("Directory %S not empty, found file <%S>, "
672 "query status = %y",
673 pc.get_nt_native_path (), &fname, status);
674 return STATUS_DIRECTORY_NOT_EMPTY;
677 pfni = (PFILE_NAMES_INFORMATION) ((caddr_t) pfni + pfni->NextEntryOffset);
680 while (NT_SUCCESS (NtQueryDirectoryFile (dir, NULL, NULL, 0, &io, pfni,
681 bufsiz, FileNamesInformation,
682 FALSE, NULL, FALSE)));
683 return STATUS_SUCCESS;
686 static inline NTSTATUS
687 _unlink_nt_post_dir_check (NTSTATUS status, POBJECT_ATTRIBUTES attr, const path_conv &pc)
689 /* Check for existence of remote dirs after trying to delete them.
690 Two reasons:
691 - Sometimes SMB indicates failure when it really succeeds.
692 - Removing a directory on a Samba drive using an old Samba version
693 sometimes doesn't return an error, if the directory can't be removed
694 because it's not empty. */
695 if (pc.isremote ())
697 FILE_BASIC_INFORMATION fbi;
698 NTSTATUS q_status;
700 q_status = NtQueryAttributesFile (attr, &fbi);
701 if (!NT_SUCCESS (status) && q_status == STATUS_OBJECT_NAME_NOT_FOUND)
702 status = STATUS_SUCCESS;
703 else if (pc.fs_is_samba ()
704 && NT_SUCCESS (status) && NT_SUCCESS (q_status))
705 status = STATUS_DIRECTORY_NOT_EMPTY;
707 return status;
710 NTSTATUS
711 unlink_nt (path_conv &pc, bool shareable)
713 NTSTATUS status;
714 HANDLE fh, fh_ro = NULL;
715 OBJECT_ATTRIBUTES attr;
716 IO_STATUS_BLOCK io;
717 ACCESS_MASK access = DELETE;
718 ULONG flags = FILE_OPEN_FOR_BACKUP_INTENT;
719 HANDLE old_trans = NULL, trans = NULL;
720 ULONG num_links = 1;
721 FILE_DISPOSITION_INFORMATION disp = { TRUE };
722 int reopened = 0;
723 bin_status bin_stat = dont_move;
725 syscall_printf ("Trying to delete %S, isdir = %d",
726 pc.get_nt_native_path (), pc.isdir ());
728 /* Add the reparse point flag to known reparse points, otherwise we remove
729 the target, not the reparse point. */
730 if (pc.is_known_reparse_point ())
731 flags |= FILE_OPEN_REPARSE_POINT;
733 pc.get_object_attr (attr, sec_none_nih);
735 /* First check if we can use POSIX unlink semantics: W10 1709+, local NTFS.
736 For the OPEN_BY_FILE_ID flag, see MINIMAL_WIN_NTFS_FLAGS comment in
737 fs_info::update. With POSIX unlink semantics the entire job gets MUCH
738 easier and faster. Just try to do it and if it fails, it fails. */
739 if (wincap.has_posix_unlink_semantics ()
740 && !pc.isremote () && pc.fs_is_ntfs ()
741 && pc.has_attribute (FILE_SUPPORTS_OPEN_BY_FILE_ID))
743 FILE_DISPOSITION_INFORMATION_EX fdie;
745 /* POSIX unlink semantics are nice, but they still fail if the file has
746 the R/O attribute set. If so, ignoring might be an option: W10 1809+
747 Removing the file is very much a safe bet afterwards, so, no
748 transaction. */
749 if ((pc.file_attributes () & FILE_ATTRIBUTE_READONLY)
750 && !wincap.has_posix_unlink_semantics_with_ignore_readonly ())
751 access |= FILE_WRITE_ATTRIBUTES;
752 status = NtOpenFile (&fh, access, &attr, &io, FILE_SHARE_VALID_FLAGS,
753 flags);
754 if (!NT_SUCCESS (status))
755 goto out;
756 if (access & FILE_WRITE_ATTRIBUTES)
758 status = NtSetAttributesFile (fh, pc.file_attributes ()
759 & ~FILE_ATTRIBUTE_READONLY);
760 if (!NT_SUCCESS (status))
762 NtClose (fh);
763 goto out;
766 fdie.Flags = FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS;
767 if (wincap.has_posix_unlink_semantics_with_ignore_readonly ())
768 fdie.Flags |= FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE;
769 status = NtSetInformationFile (fh, &io, &fdie, sizeof fdie,
770 FileDispositionInformationEx);
771 /* Restore R/O attribute in case we have multiple hardlinks. */
772 if (access & FILE_WRITE_ATTRIBUTES)
773 NtSetAttributesFile (fh, pc.file_attributes ());
774 NtClose (fh);
775 /* Trying to delete in-use executables and DLLs using
776 FILE_DISPOSITION_POSIX_SEMANTICS returns STATUS_CANNOT_DELETE.
777 Fall back to the default method. */
778 /* Additionaly that returns STATUS_INVALID_PARAMETER
779 on a bind mounted fs in hyper-v container. Falling back too. */
780 if (status != STATUS_CANNOT_DELETE
781 && status != STATUS_INVALID_PARAMETER)
783 debug_printf ("NtSetInformationFile returns %y "
784 "with posix semantics. Disable it and retry.", status);
785 goto out;
789 /* If the R/O attribute is set, we have to open the file with
790 FILE_WRITE_ATTRIBUTES to be able to remove this flags before trying
791 to delete it. We do this separately because there are filesystems
792 out there (MVFS), which refuse a request to open a file for DELETE
793 if the DOS R/O attribute is set for the file. After removing the R/O
794 attribute, just re-open the file for DELETE and go ahead. */
795 if (pc.file_attributes () & FILE_ATTRIBUTE_READONLY)
797 FILE_STANDARD_INFORMATION fsi;
799 /* If possible, hide the non-atomicity of the "remove R/O flag, remove
800 link to file" operation behind a transaction. */
801 if ((pc.fs_flags () & FILE_SUPPORTS_TRANSACTIONS))
802 start_transaction (old_trans, trans);
803 retry_open:
804 status = NtOpenFile (&fh_ro,
805 FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
806 &attr, &io, FILE_SHARE_VALID_FLAGS, flags);
807 if (NT_SUCCESS (status))
809 debug_printf ("Opening %S for removing R/O succeeded",
810 pc.get_nt_native_path ());
811 NTSTATUS status2 = NtSetAttributesFile (fh_ro,
812 pc.file_attributes ()
813 & ~FILE_ATTRIBUTE_READONLY);
814 if (!NT_SUCCESS (status2))
815 debug_printf ("Removing R/O on %S failed, status = %y",
816 pc.get_nt_native_path (), status2);
817 pc.init_reopen_attr (attr, fh_ro);
819 else
821 debug_printf ("Opening %S for removing R/O failed, status = %y",
822 pc.get_nt_native_path (), status);
823 if (NT_TRANSACTIONAL_ERROR (status) && trans)
825 /* If NtOpenFile fails due to transactional problems, stop
826 transaction and go ahead without. */
827 stop_transaction (status, old_trans, trans);
828 debug_printf ("Transaction failure. Retry open.");
829 goto retry_open;
832 if (pc.is_lnk_symlink ())
834 status = NtQueryInformationFile (fh_ro, &io, &fsi, sizeof fsi,
835 FileStandardInformation);
836 if (NT_SUCCESS (status))
837 num_links = fsi.NumberOfLinks;
839 access |= FILE_WRITE_ATTRIBUTES;
841 /* First try to open the file with only allowing sharing for delete. If
842 the file has an open handle on it, other than just for deletion, this
843 will fail. That indicates that the file has to be moved to the recycle
844 bin so that it actually disappears from its directory even though its
845 in use. Otherwise, if opening doesn't fail, the file is not in use and
846 we can go straight to setting the delete disposition flag.
847 However, while we have the file open with FILE_SHARE_DELETE, using
848 this file via another hardlink for anything other than DELETE by
849 concurrent processes fails. The 'shareable' argument is to prevent this.
851 NOTE: The missing sharing modes FILE_SHARE_READ and FILE_SHARE_WRITE do
852 NOT result in a STATUS_SHARING_VIOLATION, if another handle is
853 opened for reading/writing metadata only. In other words, if
854 another handle is open, but does not have the file open with
855 FILE_READ_DATA or FILE_WRITE_DATA, the following NtOpenFile call
856 will succeed. So, apparently there is no reliable way to find out
857 if a file is already open elsewhere for other purposes than
858 reading and writing data. */
859 if (shareable)
860 status = STATUS_SHARING_VIOLATION;
861 else
862 status = NtOpenFile (&fh, access, &attr, &io, FILE_SHARE_DELETE, flags);
863 /* STATUS_SHARING_VIOLATION is what we expect. STATUS_LOCK_NOT_GRANTED can
864 be generated under not quite clear circumstances when trying to open a
865 file on NFS with FILE_SHARE_DELETE only. This has been observed with
866 SFU 3.5 if the NFS share has been mounted under a drive letter. It's
867 not generated for all files, but only for some. If it's generated once
868 for a file, it will be generated all the time. It looks as if wrong file
869 state information is stored within the NFS client which never times out.
870 Opening the file with FILE_SHARE_VALID_FLAGS will work, though, and it
871 is then possible to delete the file quite normally. */
872 if (status == STATUS_SHARING_VIOLATION || status == STATUS_LOCK_NOT_GRANTED)
874 debug_printf ("Sharing violation when opening %S",
875 pc.get_nt_native_path ());
876 /* We never call try_to_bin on NetApp. Netapp filesystems don't
877 understand the "move and delete" method at all and have all kinds
878 of weird effects. Just setting the delete dispositon usually
879 works fine, though.
881 NFS implements its own mechanism to remove in-use files, which looks
882 quite similar to what we do in try_to_bin for remote files. However,
883 apparently it doesn't work as desired in all cases. This has been
884 observed when running the gawk 4.1.62++ testcase "testext.awk" under
885 Windows 10. So for NFS we still call try_to_bin to rename the file,
886 at least to make room for subsequent creation of a file with the
887 same filename. */
888 if (!pc.fs_is_netapp ())
889 bin_stat = move_to_bin;
890 /* If the file is not a directory, of if we didn't set the move_to_bin
891 flag, just proceed with the FILE_SHARE_VALID_FLAGS set. */
892 if (!pc.isdir () || bin_stat == dont_move)
893 status = NtOpenFile (&fh, access, &attr, &io,
894 FILE_SHARE_VALID_FLAGS, flags);
895 else
897 /* Otherwise it's getting tricky. The directory is opened in some
898 process, so we're supposed to move it to the recycler and mark it
899 for deletion. But what if the directory is not empty? The move
900 will work, but the subsequent delete will fail. So we would
901 have to move it back. While we do that in try_to_bin, it's bad,
902 because the move results in a temporary inconsistent state.
903 So, we test first if the directory is empty. If not, we bail
904 out with STATUS_DIRECTORY_NOT_EMPTY. This avoids most of the
905 problems. */
906 status = NtOpenFile (&fh, access | FILE_LIST_DIRECTORY | SYNCHRONIZE,
907 &attr, &io, FILE_SHARE_VALID_FLAGS,
908 flags | FILE_SYNCHRONOUS_IO_NONALERT);
909 if (NT_SUCCESS (status))
911 status = check_dir_not_empty (fh, pc);
912 if (!NT_SUCCESS (status))
914 NtClose (fh);
915 if (fh_ro)
916 NtClose (fh_ro);
917 goto out;
922 if (fh_ro)
923 NtClose (fh_ro);
924 if (!NT_SUCCESS (status))
926 if (status == STATUS_DELETE_PENDING)
928 debug_printf ("Delete %S already pending", pc.get_nt_native_path ());
929 status = STATUS_SUCCESS;
930 goto out;
932 debug_printf ("Opening %S for delete failed, status = %y",
933 pc.get_nt_native_path (), status);
934 goto out;
936 /* Try to move to bin if a sharing violation occured. If that worked,
937 we're done. */
938 if (bin_stat == move_to_bin
939 && (bin_stat = try_to_bin (pc, fh, access, flags)) >= has_been_moved)
941 if (bin_stat == has_been_moved)
942 status = STATUS_SUCCESS;
943 else
945 status = STATUS_DIRECTORY_NOT_EMPTY;
946 NtClose (fh);
948 goto out;
951 try_again:
952 /* Try to set delete disposition. */
953 status = NtSetInformationFile (fh, &io, &disp, sizeof disp,
954 FileDispositionInformation);
955 if (!NT_SUCCESS (status))
957 debug_printf ("Setting delete disposition on %S failed, status = %y",
958 pc.get_nt_native_path (), status);
959 if (status == STATUS_DIRECTORY_NOT_EMPTY)
961 NTSTATUS status2 = STATUS_SUCCESS;
963 if (!reopened)
965 /* Have to close and reopen the file from scratch, otherwise
966 we collide with the delete-only sharing mode. */
967 pc.get_object_attr (attr, sec_none_nih);
968 NtClose (fh);
969 status2 = NtOpenFile (&fh, access | FILE_LIST_DIRECTORY
970 | SYNCHRONIZE,
971 &attr, &io, FILE_SHARE_VALID_FLAGS,
972 flags | FILE_SYNCHRONOUS_IO_NONALERT);
974 if (NT_SUCCESS (status2) && reopened < 20)
976 /* Workaround rm -r problem:
978 Sometimes a deleted directory lingers in its parent dir
979 after the deleting handle has already been closed. This
980 can break deleting the parent dir. See the comment in
981 check_dir_not_empty for more information.
983 What we do here is this: If check_dir_not_empty returns
984 STATUS_SUCCESS, the dir is either empty, or only inhabited
985 by already deleted entries. If so, we try to move the dir
986 into the bin. This usually works.
988 However, if we're on a filesystem which doesn't support
989 the try_to_bin method, or if moving to the bin doesn't work
990 for some reason, just try to delete the directory again,
991 with a very short grace period to free the CPU for a while.
992 This gives the OS time to clean up. 5ms is enough in my
993 testing to make sure that we don't have to try more than
994 once in practically all cases.
995 While this is an extrem bordercase, we don't want to hang
996 infinitely in case a file in the directory is in the "delete
997 pending" state but an application holds an open handle to it
998 for a longer time. So we don't try this more than 20 times,
999 which means a process time of 100-120ms. */
1000 if (check_dir_not_empty (fh, pc) == STATUS_SUCCESS)
1002 if (bin_stat == dont_move)
1004 bin_stat = move_to_bin;
1005 if (!pc.fs_is_nfs () && !pc.fs_is_netapp ())
1007 debug_printf ("Try-to-bin %S",
1008 pc.get_nt_native_path ());
1009 bin_stat = try_to_bin (pc, fh, access, flags);
1012 /* Do NOT handle bin_stat == dir_not_empty here! */
1013 if (bin_stat == has_been_moved)
1014 status = STATUS_SUCCESS;
1015 else
1017 debug_printf ("Try %S again", pc.get_nt_native_path ());
1018 ++reopened;
1019 Sleep (5L);
1020 goto try_again;
1024 else if (status2 != STATUS_OBJECT_PATH_NOT_FOUND
1025 && status2 != STATUS_OBJECT_NAME_NOT_FOUND)
1027 fh = NULL;
1028 debug_printf ("Opening dir %S for check_dir_not_empty failed, "
1029 "status = %y", pc.get_nt_native_path (), status2);
1031 else /* Directory disappeared between NtClose and NtOpenFile. */
1032 status = STATUS_SUCCESS;
1034 /* Trying to delete a hardlink to a file in use by the system in some
1035 way (for instance, font files) by setting the delete disposition fails
1036 with STATUS_CANNOT_DELETE. Strange enough, deleting these hardlinks
1037 using delete-on-close semantic works... most of the time.
1039 Don't use delete-on-close on remote shares. If two processes
1040 have open handles on a file and one of them calls unlink, the
1041 file is removed from the remote share even though the other
1042 process still has an open handle. That process than gets Win32
1043 error 59, ERROR_UNEXP_NET_ERR when trying to access the file.
1044 Microsoft KB 837665 describes this problem as a bug in 2K3, but
1045 I have reproduced it on other systems. */
1046 else if (status == STATUS_CANNOT_DELETE
1047 && (!pc.isremote () || pc.fs_is_ncfsd ()))
1049 HANDLE fh2;
1051 debug_printf ("Cannot delete %S, try delete-on-close",
1052 pc.get_nt_native_path ());
1053 /* Re-open from handle so we open the correct file no matter if it
1054 has been moved to the bin or not. */
1055 status = NtOpenFile (&fh2, DELETE,
1056 pc.init_reopen_attr (attr, fh), &io,
1057 bin_stat == move_to_bin ? FILE_SHARE_VALID_FLAGS
1058 : FILE_SHARE_DELETE,
1059 flags | FILE_DELETE_ON_CLOSE);
1060 if (!NT_SUCCESS (status))
1062 debug_printf ("Setting delete-on-close on %S failed, status = %y",
1063 pc.get_nt_native_path (), status);
1064 /* This is really the last chance. If it hasn't been moved
1065 to the bin already, try it now. If moving to the bin
1066 succeeds, we got rid of the file in some way, even if
1067 unlinking didn't work. */
1068 if (bin_stat == dont_move)
1069 bin_stat = try_to_bin (pc, fh, access, flags);
1070 if (bin_stat >= has_been_moved)
1071 status = bin_stat == has_been_moved
1072 ? STATUS_SUCCESS
1073 : STATUS_DIRECTORY_NOT_EMPTY;
1075 else
1076 NtClose (fh2);
1079 if (fh)
1081 if (access & FILE_WRITE_ATTRIBUTES)
1083 /* Restore R/O attribute if setting the delete disposition failed. */
1084 if (!NT_SUCCESS (status))
1085 NtSetAttributesFile (fh, pc.file_attributes ());
1086 /* If we succeeded, restore R/O attribute to accommodate hardlinks.
1087 Only ever try to do this for our own winsymlinks, because there's
1088 a problem with setting the delete disposition:
1089 http://msdn.microsoft.com/en-us/library/ff545765%28VS.85%29.aspx
1090 "Subsequently, the only legal operation by such a caller is
1091 to close the open file handle."
1093 FIXME? We could use FILE_HARD_LINK_INFORMATION to find all
1094 hardlinks and use one of them to restore the R/O bit, after the
1095 NtClose, but before we stop the transaction. This avoids the
1096 aforementioned problem entirely . */
1097 else if (pc.is_lnk_symlink () && num_links > 1)
1098 NtSetAttributesFile (fh, pc.file_attributes ());
1101 NtClose (fh);
1104 out:
1105 /* Stop transaction if we started one. */
1106 if (trans)
1107 stop_transaction (status, old_trans, trans);
1109 if (pc.isdir ())
1110 status = _unlink_nt_post_dir_check (status, &attr, pc);
1112 syscall_printf ("%S, return status = %y", pc.get_nt_native_path (), status);
1113 return status;
1116 extern "C" int
1117 unlink (const char *ourname)
1119 int res = -1;
1120 dev_t devn;
1121 NTSTATUS status;
1123 path_conv win32_name (ourname, PC_SYM_NOFOLLOW, stat_suffixes);
1125 if (win32_name.error)
1127 set_errno (win32_name.error);
1128 goto done;
1131 devn = win32_name.get_device ();
1132 if (isproc_dev (devn))
1134 set_errno (EROFS);
1135 goto done;
1137 if (isdevfd_dev (devn) || (win32_name.isdevice () && !win32_name.issocket ()))
1139 set_errno (EPERM);
1140 goto done;
1142 if (!win32_name.exists ())
1144 debug_printf ("unlinking a nonexistent file");
1145 set_errno (ENOENT);
1146 goto done;
1148 else if (win32_name.isdir ())
1150 debug_printf ("unlinking a directory");
1151 set_errno (EISDIR);
1152 goto done;
1155 status = unlink_nt (win32_name, false);
1156 if (NT_SUCCESS (status))
1157 res = 0;
1158 else
1159 __seterrno_from_nt_status (status);
1161 done:
1162 syscall_printf ("%R = unlink(%s)", res, ourname);
1163 return res;
1166 extern "C" int
1167 _remove_r (struct _reent *, const char *ourname)
1169 path_conv win32_name (ourname, PC_SYM_NOFOLLOW);
1171 if (win32_name.error)
1173 set_errno (win32_name.error);
1174 syscall_printf ("%R = remove(%s)",-1, ourname);
1175 return -1;
1178 int res = win32_name.isdir () ? rmdir (ourname) : unlink (ourname);
1179 syscall_printf ("%R = remove(%s)", res, ourname);
1180 return res;
1183 extern "C" int
1184 remove (const char *ourname)
1186 return _remove_r (_REENT, ourname);
1189 extern "C" pid_t
1190 getpid ()
1192 syscall_printf ("%d = getpid()", myself->pid);
1193 return myself->pid;
1196 extern "C" pid_t
1197 _getpid_r (struct _reent *)
1199 return getpid ();
1202 /* getppid: POSIX 4.1.1.1 */
1203 extern "C" pid_t
1204 getppid ()
1206 syscall_printf ("%d = getppid()", myself->ppid);
1207 return myself->ppid;
1210 /* setsid: POSIX 4.3.2.1 */
1211 extern "C" pid_t
1212 setsid (void)
1214 if (myself->pgid == myself->pid)
1215 syscall_printf ("hmm. pgid %d pid %d", myself->pgid, myself->pid);
1216 else
1218 myself->ctty = CTTY_RELEASED;
1219 myself->sid = myself->pid;
1220 myself->pgid = myself->pid;
1221 if (cygheap->ctty)
1222 cygheap->close_ctty ();
1223 syscall_printf ("sid %d, pgid %d, %s", myself->sid, myself->pgid, myctty ());
1224 return myself->sid;
1227 set_errno (EPERM);
1228 return -1;
1231 extern "C" pid_t
1232 getsid (pid_t pid)
1234 pid_t res;
1235 if (!pid)
1236 res = myself->sid;
1237 else
1239 pinfo p (pid);
1240 if (p)
1241 res = p->sid;
1242 else
1244 set_errno (ESRCH);
1245 res = -1;
1248 syscall_printf ("%R = getsid(%d)", pid);
1249 return res;
1252 extern "C" ssize_t
1253 read (int fd, void *ptr, size_t len)
1255 size_t res = (size_t) -1;
1257 pthread_testcancel ();
1259 __try
1261 cygheap_fdget cfd (fd);
1262 if (cfd < 0)
1263 __leave;
1265 if ((cfd->get_flags () & O_PATH)
1266 || (cfd->get_flags () & O_ACCMODE) == O_WRONLY)
1268 set_errno (EBADF);
1269 __leave;
1272 /* Could block, so let user know we at least got here. */
1273 syscall_printf ("read(%d, %p, %d) %sblocking",
1274 fd, ptr, len, cfd->is_nonblocking () ? "non" : "");
1276 cfd->read (ptr, len);
1277 res = len;
1279 __except (EFAULT) {}
1280 __endtry
1281 syscall_printf ("%lR = read(%d, %p, %d)", res, fd, ptr, len);
1282 return (ssize_t) res;
1285 EXPORT_ALIAS (read, _read)
1287 extern "C" ssize_t
1288 readv (int fd, const struct iovec *const iov, const int iovcnt)
1290 ssize_t res = -1;
1292 pthread_testcancel ();
1294 __try
1296 const ssize_t tot = check_iovec_for_read (iov, iovcnt);
1298 cygheap_fdget cfd (fd);
1299 if (cfd < 0)
1300 __leave;
1302 if (tot <= 0)
1304 res = tot;
1305 __leave;
1308 if ((cfd->get_flags () & O_PATH)
1309 || (cfd->get_flags () & O_ACCMODE) == O_WRONLY)
1311 set_errno (EBADF);
1312 __leave;
1315 /* Could block, so let user know we at least got here. */
1316 syscall_printf ("readv(%d, %p, %d) %sblocking",
1317 fd, iov, iovcnt, cfd->is_nonblocking () ? "non" : "");
1319 res = cfd->readv (iov, iovcnt, tot);
1321 __except (EFAULT) {}
1322 __endtry
1323 syscall_printf ("%lR = readv(%d, %p, %d)", res, fd, iov, iovcnt);
1324 return res;
1327 extern "C" ssize_t
1328 pread (int fd, void *ptr, size_t len, off_t off)
1330 ssize_t res;
1332 pthread_testcancel ();
1334 cygheap_fdget cfd (fd);
1335 if (cfd < 0)
1336 res = -1;
1337 else if (cfd->get_flags () & O_PATH)
1339 set_errno (EBADF);
1340 res = -1;
1342 else
1343 res = cfd->pread (ptr, len, off);
1345 syscall_printf ("%lR = pread(%d, %p, %d, %d)", res, fd, ptr, len, off);
1346 return res;
1349 extern "C" ssize_t
1350 write (int fd, const void *ptr, size_t len)
1352 ssize_t res = -1;
1354 pthread_testcancel ();
1356 __try
1358 cygheap_fdget cfd (fd);
1359 if (cfd < 0)
1360 __leave;
1362 if ((cfd->get_flags () & O_PATH)
1363 || (cfd->get_flags () & O_ACCMODE) == O_RDONLY)
1365 set_errno (EBADF);
1366 __leave;
1369 /* Could block, so let user know we at least got here. */
1370 if (fd == 1 || fd == 2)
1371 paranoid_printf ("write(%d, %p, %d)", fd, ptr, len);
1372 else
1373 syscall_printf ("write(%d, %p, %d)", fd, ptr, len);
1375 res = cfd->write (ptr, len);
1377 __except (EFAULT) {}
1378 __endtry
1379 syscall_printf ("%lR = write(%d, %p, %d)", res, fd, ptr, len);
1380 return res;
1383 EXPORT_ALIAS (write, _write)
1385 extern "C" ssize_t
1386 writev (const int fd, const struct iovec *const iov, const int iovcnt)
1388 ssize_t res = -1;
1390 pthread_testcancel ();
1392 __try
1394 const ssize_t tot = check_iovec_for_write (iov, iovcnt);
1396 cygheap_fdget cfd (fd);
1397 if (cfd < 0)
1398 __leave;
1400 if (tot <= 0)
1402 res = tot;
1403 __leave;
1406 if ((cfd->get_flags () & O_PATH)
1407 || (cfd->get_flags () & O_ACCMODE) == O_RDONLY)
1409 set_errno (EBADF);
1410 __leave;
1413 /* Could block, so let user know we at least got here. */
1414 if (fd == 1 || fd == 2)
1415 paranoid_printf ("writev(%d, %p, %d)", fd, iov, iovcnt);
1416 else
1417 syscall_printf ("writev(%d, %p, %d)", fd, iov, iovcnt);
1419 res = cfd->writev (iov, iovcnt, tot);
1421 __except (EFAULT) {}
1422 __endtry
1423 if (fd == 1 || fd == 2)
1424 paranoid_printf ("%lR = writev(%d, %p, %d)", res, fd, iov, iovcnt);
1425 else
1426 syscall_printf ("%lR = writev(%d, %p, %d)", res, fd, iov, iovcnt);
1427 return res;
1430 extern "C" ssize_t
1431 pwrite (int fd, const void *ptr, size_t len, off_t off)
1433 pthread_testcancel ();
1435 ssize_t res;
1436 cygheap_fdget cfd (fd);
1437 if (cfd < 0)
1438 res = -1;
1439 else if (cfd->get_flags () & O_PATH)
1441 set_errno (EBADF);
1442 res = -1;
1444 else
1445 res = cfd->pwrite (const_cast<void *> (ptr), len, off);
1447 syscall_printf ("%lR = pwrite(%d, %p, %d, %d)", res, fd, ptr, len, off);
1448 return res;
1451 /* _open */
1452 /* newlib's fcntl.h defines _open as taking variable args so we must
1453 correspond. The third arg if it exists is: mode_t mode. */
1454 extern "C" int
1455 open (const char *unix_path, int flags, ...)
1457 int res = -1;
1458 va_list ap;
1459 mode_t mode = 0;
1460 fhandler_base *fh = NULL;
1461 fhandler_base *fh_file = NULL;
1463 pthread_testcancel ();
1465 __try
1467 syscall_printf ("open(%s, %y)", unix_path, flags);
1468 if (!*unix_path)
1470 set_errno (ENOENT);
1471 __leave;
1474 /* check for optional mode argument */
1475 va_start (ap, flags);
1476 mode = va_arg (ap, mode_t);
1477 va_end (ap);
1479 cygheap_fdnew fd;
1481 if (fd < 0)
1482 __leave; /* errno already set */
1484 /* When O_PATH is specified in flags, flag bits other than O_CLOEXEC,
1485 O_DIRECTORY, and O_NOFOLLOW are ignored. */
1486 if (flags & O_PATH)
1487 flags &= (O_PATH | O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW);
1489 int opt = PC_OPEN | PC_SYM_NOFOLLOW_PROCFD;
1490 opt |= (flags & (O_NOFOLLOW | O_EXCL)) ? PC_SYM_NOFOLLOW
1491 : PC_SYM_FOLLOW;
1493 /* If we're opening a FIFO, we will call device_access_denied
1494 below. This leads to a call to fstat, which can use the
1495 path_conv handle. */
1496 opt |= PC_KEEP_HANDLE;
1497 if (!(fh = build_fh_name (unix_path, opt, stat_suffixes)))
1498 __leave; /* errno already set */
1499 opt &= ~PC_KEEP_HANDLE;
1500 if (!fh->isfifo ())
1501 fh->pc.close_conv_handle ();
1502 if ((flags & O_NOFOLLOW) && fh->issymlink () && !(flags & O_PATH))
1504 set_errno (ELOOP);
1505 __leave;
1507 if ((flags & O_DIRECTORY) && fh->exists () && !fh->pc.isdir ())
1509 set_errno (ENOTDIR);
1510 __leave;
1512 if (((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) && fh->exists ())
1514 set_errno (EEXIST);
1515 __leave;
1517 if (flags & O_TMPFILE)
1519 if ((flags & O_ACCMODE) != O_WRONLY && (flags & O_ACCMODE) != O_RDWR)
1521 set_errno (EINVAL);
1522 __leave;
1524 if (!fh->pc.isdir ())
1526 set_errno (fh->exists () ? ENOTDIR : ENOENT);
1527 __leave;
1529 /* Unfortunately Windows does not allow to create a nameless file.
1530 So create unique filename instead. It starts with ".cyg_tmp_",
1531 followed by an 8 byte unique hex number, followed by an 8 byte
1532 random hex number. */
1533 int64_t rnd;
1534 char *new_path;
1536 new_path = (char *) malloc (strlen (fh->get_name ())
1537 + 1 /* slash */
1538 + 10 /* prefix */
1539 + 16 /* 64 bit unique id as hex*/
1540 + 16 /* 64 bit random number as hex */
1541 + 1 /* trailing NUL */);
1542 if (!new_path)
1543 __leave;
1544 fh->set_unique_id ();
1545 RtlGenRandom (&rnd, sizeof rnd);
1546 __small_sprintf (new_path, "%s/%s%016X%016X",
1547 fh->get_name (), ".cyg_tmp_",
1548 fh->get_unique_id (), rnd);
1550 if (!(fh_file = build_fh_name (new_path, opt, NULL)))
1552 free (new_path);
1553 __leave; /* errno already set */
1555 delete fh;
1556 fh = fh_file;
1559 if (fh->dev () == FH_PROCESSFD && fh->pc.follow_fd_symlink ())
1561 /* Reopen file by descriptor */
1562 fh_file = fh->fd_reopen (flags, mode & 07777);
1563 if (!fh_file)
1564 __leave;
1565 delete fh;
1566 fh = fh_file;
1568 else
1570 if (fh->is_fs_special ())
1572 if (fh->device_access_denied (flags))
1573 __leave; /* errno already set */
1574 else if (fh->isfifo ())
1575 fh->pc.close_conv_handle ();
1577 if (!fh->open_with_arch (flags, mode & 07777))
1578 __leave; /* errno already set */
1580 /* Move O_TMPFILEs to the bin to avoid blocking the parent dir. */
1581 if ((flags & O_TMPFILE) && !fh->pc.isremote ())
1582 try_to_bin (fh->pc, fh->get_handle (), DELETE,
1583 FILE_OPEN_FOR_BACKUP_INTENT);
1584 fd = fh;
1585 if (fd <= 2)
1586 set_std_handle (fd);
1587 res = fd;
1589 __except (EFAULT) {}
1590 __endtry
1591 if (res < 0 && fh)
1592 delete fh;
1593 syscall_printf ("%R = open(%s, %y)", res, unix_path, flags);
1594 return res;
1597 EXPORT_ALIAS (open, _open )
1599 extern "C" off_t
1600 lseek (int fd, off_t pos, int dir)
1602 off_t res;
1604 if (dir < SEEK_SET || dir > SEEK_HOLE)
1606 set_errno (EINVAL);
1607 res = -1;
1609 else
1611 cygheap_fdget cfd (fd);
1612 if (cfd < 0)
1613 res = -1;
1614 else if (cfd->getdents_dir ())
1616 if (dir != SEEK_SET && dir != SEEK_CUR) /* No SEEK_END */
1618 set_errno (EINVAL);
1619 res = -1;
1621 else
1623 long cur;
1625 cur = cfd->telldir (cfd->getdents_dir ());
1626 if (dir == SEEK_CUR && cur == 0)
1627 res = cur;
1628 else
1630 if (dir == SEEK_CUR)
1631 pos = cur + pos;
1632 cfd->seekdir (cfd->getdents_dir (), pos);
1633 res = pos;
1637 else
1638 res = cfd->lseek (pos, dir);
1640 /* Can't use %R/%lR here since res is always 8 bytes */
1641 syscall_printf (res == -1 ? "%D = lseek(%d, %D, %d), errno %d"
1642 : "%D = lseek(%d, %D, %d)",
1643 res, fd, pos, dir, get_errno ());
1645 return res;
1648 EXPORT_ALIAS (lseek, _lseek)
1650 extern "C" int
1651 close (int fd)
1653 int res;
1655 syscall_printf ("close(%d)", fd);
1657 pthread_testcancel ();
1659 cygheap_fdget cfd (fd, true);
1660 if (cfd < 0)
1661 res = -1;
1662 else
1664 cfd->isclosed (true);
1665 res = cfd->close_with_arch ();
1666 cfd.release ();
1669 syscall_printf ("%R = close(%d)", res, fd);
1670 return res;
1673 EXPORT_ALIAS (close, _close)
1675 extern "C" int
1676 isatty (int fd)
1678 int res;
1680 cygheap_fdget cfd (fd);
1681 if (cfd < 0)
1682 res = 0;
1683 else
1684 res = cfd->is_tty ();
1685 syscall_printf ("%R = isatty(%d)", res, fd);
1686 return res;
1688 EXPORT_ALIAS (isatty, _isatty)
1690 extern "C" int
1691 link (const char *oldpath, const char *newpath)
1693 int res = -1;
1694 fhandler_base *fh;
1696 if (!(fh = build_fh_name (oldpath, PC_SYM_NOFOLLOW | PC_KEEP_HANDLE,
1697 stat_suffixes)))
1698 goto error;
1700 if (fh->error ())
1702 debug_printf ("got %d error from build_fh_name", fh->error ());
1703 set_errno (fh->error ());
1705 else if (fh->pc.isdir ())
1706 set_errno (EPERM); /* We do not permit linking directories. */
1707 else if (!fh->pc.exists ())
1708 set_errno (ENOENT);
1709 else
1710 res = fh->link (newpath);
1712 delete fh;
1713 error:
1714 syscall_printf ("%R = link(%s, %s)", res, oldpath, newpath);
1715 return res;
1718 /* chown: POSIX 5.6.5.1 */
1720 * chown () is only implemented for Windows NT. Under other operating
1721 * systems, it is only a stub that always returns zero.
1723 static int
1724 chown_worker (const char *name, unsigned fmode, uid_t uid, gid_t gid)
1726 int res = -1;
1727 fhandler_base *fh;
1729 if (!(fh = build_fh_name (name, fmode, stat_suffixes)))
1730 goto error;
1732 if (fh->error ())
1734 debug_printf ("got %d error from build_fh_name", fh->error ());
1735 set_errno (fh->error ());
1737 else
1738 res = fh->fchown (uid, gid);
1740 delete fh;
1741 error:
1742 syscall_printf ("%R = %schown(%s,...)",
1743 res, (fmode & PC_SYM_NOFOLLOW) ? "l" : "", name);
1744 return res;
1747 extern "C" int
1748 chown (const char * name, uid_t uid, gid_t gid)
1750 return chown_worker (name, PC_SYM_FOLLOW, uid, gid);
1753 extern "C" int
1754 lchown (const char * name, uid_t uid, gid_t gid)
1756 return chown_worker (name, PC_SYM_NOFOLLOW, uid, gid);
1759 extern "C" int
1760 fchown (int fd, uid_t uid, gid_t gid)
1762 cygheap_fdget cfd (fd);
1763 if (cfd < 0)
1765 syscall_printf ("-1 = fchown (%d,...)", fd);
1766 return -1;
1768 else if (cfd->get_flags () & O_PATH)
1770 set_errno (EBADF);
1771 return -1;
1774 int res = cfd->fchown (uid, gid);
1776 syscall_printf ("%R = fchown(%s,...)", res, cfd->get_name ());
1777 return res;
1780 /* umask: POSIX 5.3.3.1 */
1781 extern "C" mode_t
1782 umask (mode_t mask)
1784 mode_t oldmask;
1786 oldmask = cygheap->umask;
1787 cygheap->umask = mask & 0777;
1788 return oldmask;
1791 #define FILTERED_MODE(m) ((m) & (S_ISUID | S_ISGID | S_ISVTX \
1792 | S_IRWXU | S_IRWXG | S_IRWXO))
1795 chmod_device (path_conv& pc, mode_t mode)
1797 return mknod_worker (pc, (pc.dev.mode () & S_IFMT) | FILTERED_MODE (mode),
1798 pc.dev.get_major (), pc.dev.get_minor ());
1801 /* chmod: POSIX 5.6.4.1 */
1802 extern "C" int
1803 chmod (const char *path, mode_t mode)
1805 int res = -1;
1806 fhandler_base *fh;
1807 if (!(fh = build_fh_name (path, PC_SYM_FOLLOW, stat_suffixes)))
1808 goto error;
1810 if (fh->error ())
1812 debug_printf ("got %d error from build_fh_name", fh->error ());
1813 set_errno (fh->error ());
1815 else
1816 res = fh->fchmod (FILTERED_MODE (mode));
1818 delete fh;
1819 error:
1820 syscall_printf ("%R = chmod(%s, 0%o)", res, path, mode);
1821 return res;
1824 /* fchmod: P96 5.6.4.1 */
1826 extern "C" int
1827 fchmod (int fd, mode_t mode)
1829 cygheap_fdget cfd (fd);
1830 if (cfd < 0)
1832 syscall_printf ("-1 = fchmod (%d, 0%o)", fd, mode);
1833 return -1;
1835 else if (cfd->get_flags () & O_PATH)
1837 set_errno (EBADF);
1838 return -1;
1841 return cfd->fchmod (FILTERED_MODE (mode));
1844 static struct stat dev_st;
1845 static bool dev_st_inited;
1847 void
1848 fhandler_base::stat_fixup (struct stat *buf)
1850 /* For devices, set inode number to device number. This gives us a valid,
1851 unique inode number without having to call hash_path_name. /dev/tty needs
1852 a bit of persuasion to get the same st_ino value in stat and fstat. */
1853 if (!buf->st_ino)
1855 if (get_major () == DEV_VIRTFS_MAJOR)
1856 buf->st_ino = get_ino ();
1857 else if (dev () == FH_TTY ||
1858 ((get_major () == DEV_PTYS_MAJOR
1859 || get_major () == DEV_CONS_MAJOR)
1860 && !strcmp (get_name (), "/dev/tty")))
1861 buf->st_ino = FH_TTY;
1862 else
1863 buf->st_ino = get_device ();
1865 /* For /dev-based devices, st_dev must be set to the device number of /dev,
1866 not it's own device major/minor numbers. What we do here to speed up
1867 the process is to fetch the device number of /dev only once, liberally
1868 assuming that /dev doesn't change over the lifetime of a process. */
1869 if (!buf->st_dev)
1871 if (dev ().is_dev_resident ())
1873 if (!dev_st_inited)
1875 stat ("/dev", &dev_st);
1876 dev_st_inited = true;
1878 buf->st_dev = dev_st.st_dev;
1880 else
1881 buf->st_dev = get_device ();
1883 /* Only set st_rdev if it's a device. */
1884 if (!buf->st_rdev && get_major () != DEV_VIRTFS_MAJOR)
1886 buf->st_rdev = get_device ();
1887 /* consX, console, conin, and conout point to the same device.
1888 Make sure the link count is correct. */
1889 if (buf->st_rdev == (dev_t) myself->ctty && iscons_dev (myself->ctty))
1890 buf->st_nlink = 4;
1891 /* CD-ROM drives have two links, /dev/srX and /dev/scdX. */
1892 else if (gnu_dev_major (buf->st_rdev) == DEV_CDROM_MAJOR)
1893 buf->st_nlink = 2;
1897 extern "C" int
1898 fstat (int fd, struct stat *buf)
1900 int res;
1902 cygheap_fdget cfd (fd);
1903 if (cfd < 0)
1904 res = -1;
1905 else
1907 memset (buf, 0, sizeof (struct stat));
1908 res = cfd->fstat (buf);
1909 if (!res)
1910 cfd->stat_fixup (buf);
1913 syscall_printf ("%R = fstat(%d, %p)", res, fd, buf);
1914 return res;
1917 extern "C" int
1918 _fstat_r (struct _reent *ptr, int fd, struct stat *buf)
1920 int ret;
1922 if ((ret = fstat (fd, buf)) == -1)
1923 _REENT_ERRNO(ptr) = get_errno ();
1924 return ret;
1927 /* fsync: P96 6.6.1.1 */
1928 extern "C" int
1929 fsync (int fd)
1931 pthread_testcancel ();
1932 cygheap_fdget cfd (fd);
1933 if (cfd < 0)
1935 syscall_printf ("-1 = fsync (%d)", fd);
1936 return -1;
1938 return cfd->fsync ();
1941 EXPORT_ALIAS (fsync, fdatasync)
1943 static void
1944 sync_worker (HANDLE dir, USHORT len, LPCWSTR vol)
1946 NTSTATUS status;
1947 HANDLE fh;
1948 IO_STATUS_BLOCK io;
1949 OBJECT_ATTRIBUTES attr;
1950 UNICODE_STRING uvol = { len, len, (WCHAR *) vol };
1952 InitializeObjectAttributes (&attr, &uvol, OBJ_CASE_INSENSITIVE, dir, NULL);
1953 status = NtOpenFile (&fh, GENERIC_WRITE, &attr, &io,
1954 FILE_SHARE_VALID_FLAGS, 0);
1955 if (!NT_SUCCESS (status))
1956 debug_printf ("NtOpenFile (%S), status %y", &uvol, status);
1957 else
1959 status = NtFlushBuffersFile (fh, &io);
1960 if (!NT_SUCCESS (status))
1961 debug_printf ("NtFlushBuffersFile (%S), status %y", &uvol, status);
1962 NtClose (fh);
1966 /* sync: SUSv3 */
1967 extern "C" void
1968 sync ()
1970 OBJECT_ATTRIBUTES attr;
1971 NTSTATUS status;
1972 HANDLE devhdl;
1973 UNICODE_STRING device;
1975 /* Open \Device object directory. */
1976 RtlInitUnicodeString (&device, L"\\Device");
1977 InitializeObjectAttributes (&attr, &device, OBJ_CASE_INSENSITIVE, NULL, NULL);
1978 status = NtOpenDirectoryObject (&devhdl, DIRECTORY_QUERY, &attr);
1979 if (!NT_SUCCESS (status))
1981 debug_printf ("NtOpenDirectoryObject, status %y", status);
1982 return;
1984 /* Traverse \Device directory ... */
1985 tmp_pathbuf tp;
1986 PDIRECTORY_BASIC_INFORMATION dbi_buf = (PDIRECTORY_BASIC_INFORMATION)
1987 tp.w_get ();
1988 BOOLEAN restart = TRUE;
1989 bool last_run = false;
1990 ULONG context = 0;
1991 while (!last_run)
1993 status = NtQueryDirectoryObject (devhdl, dbi_buf, 65536, FALSE, restart,
1994 &context, NULL);
1995 if (!NT_SUCCESS (status))
1997 debug_printf ("NtQueryDirectoryObject, status %y", status);
1998 break;
2000 if (status != STATUS_MORE_ENTRIES)
2001 last_run = true;
2002 restart = FALSE;
2003 for (PDIRECTORY_BASIC_INFORMATION dbi = dbi_buf;
2004 dbi->ObjectName.Length > 0;
2005 dbi++)
2007 /* ... and call sync_worker for each HarddiskVolumeX entry. */
2008 if (dbi->ObjectName.Length >= 15 * sizeof (WCHAR)
2009 && !wcsncasecmp (dbi->ObjectName.Buffer, L"HarddiskVolume", 14)
2010 && iswdigit (dbi->ObjectName.Buffer[14]))
2011 sync_worker (devhdl, dbi->ObjectName.Length,
2012 dbi->ObjectName.Buffer);
2015 NtClose (devhdl);
2018 /* Cygwin internal */
2020 stat_worker (path_conv &pc, struct stat *buf)
2022 int res = -1;
2024 __try
2026 if (pc.error)
2028 debug_printf ("got %d error from path_conv", pc.error);
2029 set_errno (pc.error);
2031 else if (pc.exists ())
2033 fhandler_base *fh;
2035 memset (buf, 0, sizeof (*buf));
2037 if (!(fh = build_fh_pc (pc)))
2038 __leave;
2040 debug_printf ("(%S, %p, %p), file_attributes %d",
2041 pc.get_nt_native_path (), buf, fh, (DWORD) *fh);
2043 res = fh->fstat (buf);
2044 if (!res)
2045 fh->stat_fixup (buf);
2046 delete fh;
2048 else
2049 set_errno (ENOENT);
2051 __except (EFAULT) {}
2052 __endtry
2053 syscall_printf ("%d = (%S,%p)", res, pc.get_nt_native_path (), buf);
2054 return res;
2057 extern "C" int
2058 stat (const char *__restrict name, struct stat *__restrict buf)
2060 syscall_printf ("entering");
2061 path_conv pc (name, PC_SYM_FOLLOW | PC_POSIX | PC_KEEP_HANDLE
2062 | PC_SYM_NOFOLLOW_PROCFD,
2063 stat_suffixes);
2064 return stat_worker (pc, buf);
2067 extern "C" int
2068 _stat_r (struct _reent *__restrict ptr, const char *__restrict name,
2069 struct stat *buf)
2071 int ret;
2073 if ((ret = stat (name, buf)) == -1)
2074 _REENT_ERRNO(ptr) = get_errno ();
2075 return ret;
2078 /* lstat: Provided by SVR4 and 4.3+BSD, POSIX? */
2079 extern "C" int
2080 lstat (const char *__restrict name, struct stat *__restrict buf)
2082 syscall_printf ("entering");
2083 path_conv pc (name, PC_SYM_NOFOLLOW | PC_POSIX | PC_KEEP_HANDLE,
2084 stat_suffixes);
2085 return stat_worker (pc, buf);
2088 extern "C" int
2089 access (const char *fn, int flags)
2091 // flags were incorrectly specified
2092 int res = -1;
2093 if (flags & ~(F_OK|R_OK|W_OK|X_OK))
2094 set_errno (EINVAL);
2095 else
2097 fhandler_base *fh = build_fh_name (fn, PC_SYM_FOLLOW | PC_KEEP_HANDLE,
2098 stat_suffixes);
2099 if (fh)
2101 res = fh->fhaccess (flags, false);
2102 delete fh;
2105 debug_printf ("returning %d", res);
2106 return res;
2109 /* Linux provides this extension; it is basically a wrapper around the
2110 POSIX:2008 faccessat (AT_FDCWD, fn, flags, AT_EACCESS). We also
2111 provide eaccess as an alias for this, in cygwin.din. */
2112 extern "C" int
2113 euidaccess (const char *fn, int flags)
2115 // flags were incorrectly specified
2116 int res = -1;
2117 if (flags & ~(F_OK|R_OK|W_OK|X_OK))
2118 set_errno (EINVAL);
2119 else
2121 fhandler_base *fh = build_fh_name (fn, PC_SYM_FOLLOW | PC_KEEP_HANDLE,
2122 stat_suffixes);
2123 if (fh)
2125 res = fh->fhaccess (flags, true);
2126 delete fh;
2129 debug_printf ("returning %d", res);
2130 return res;
2133 static void
2134 rename_append_suffix (path_conv &pc, const char *path, size_t len,
2135 const char *suffix)
2137 char buf[len + 5];
2139 if (ascii_strcasematch (path + len - 4, ".lnk")
2140 || ascii_strcasematch (path + len - 4, ".exe"))
2141 len -= 4;
2142 stpcpy (stpncpy (buf, path, len), suffix);
2143 pc.check (buf, PC_SYM_NOFOLLOW);
2146 /* This function tests if a filename has one of the "approved" executable
2147 suffix. This list is probably not complete... */
2148 static inline bool
2149 nt_path_has_executable_suffix (PUNICODE_STRING upath)
2151 static const PUNICODE_STRING blessed_executable_suffixes[] =
2153 &ro_u_exe,
2154 &ro_u_scr,
2155 &ro_u_sys,
2156 NULL
2159 USHORT pos = upath->Length / sizeof (WCHAR);
2160 PWCHAR path;
2161 UNICODE_STRING usuf;
2162 const PUNICODE_STRING *suf;
2164 /* Too short for a native path? */
2165 if (pos < 8)
2166 return false;
2167 /* Assumption: All executable suffixes have a length of three. */
2168 path = upath->Buffer + pos - 4;
2169 if (*path != L'.')
2170 return false;
2171 RtlInitCountedUnicodeString (&usuf, path, 4 * sizeof (WCHAR));
2172 for (suf = blessed_executable_suffixes; *suf; ++suf)
2173 if (RtlEqualUnicodeString (&usuf, *suf, TRUE))
2174 return true;
2175 return false;
2178 inline int
2179 set_same_file_return (bool noreplace)
2181 if (!noreplace)
2182 return 0;
2183 set_errno (EEXIST);
2184 return -1;
2186 /* If newpath names an existing file and the RENAME_NOREPLACE flag is
2187 specified, fail with EEXIST. Exception: Don't fail if the purpose
2188 of the rename is just to change the case of oldpath on a
2189 case-insensitive file system. */
2190 static int
2191 rename2 (const char *oldpath, const char *newpath, unsigned int at2flags)
2193 tmp_pathbuf tp;
2194 int res = -1;
2195 path_conv oldpc, newpc, new2pc, *dstpc, *removepc = NULL;
2196 bool old_dir_requested = false, new_dir_requested = false;
2197 bool old_explicit_suffix = false, new_explicit_suffix = false;
2198 bool use_posix_semantics;
2199 bool noreplace = at2flags & RENAME_NOREPLACE;
2200 size_t olen, nlen;
2201 bool equal_path;
2202 NTSTATUS status = STATUS_SUCCESS;
2203 HANDLE fh = NULL, nfh;
2204 HANDLE old_trans = NULL, trans = NULL;
2205 OBJECT_ATTRIBUTES attr;
2206 IO_STATUS_BLOCK io;
2207 FILE_STANDARD_INFORMATION ofsi;
2208 PFILE_RENAME_INFORMATION pfri;
2210 __try
2212 if (at2flags & ~RENAME_NOREPLACE)
2213 /* RENAME_NOREPLACE is the only flag currently supported. */
2215 set_errno (EINVAL);
2216 __leave;
2218 if (!*oldpath || !*newpath)
2220 /* Reject rename("","x"), rename("x",""). */
2221 set_errno (ENOENT);
2222 __leave;
2224 if (has_dot_last_component (oldpath, true))
2226 /* Reject rename("dir/.","x"). */
2227 oldpc.check (oldpath, PC_SYM_NOFOLLOW, stat_suffixes);
2228 set_errno (oldpc.isdir () ? EINVAL : ENOTDIR);
2229 __leave;
2231 if (has_dot_last_component (newpath, true))
2233 /* Reject rename("dir","x/."). */
2234 newpc.check (newpath, PC_SYM_NOFOLLOW, stat_suffixes);
2235 set_errno (!newpc.exists () ? ENOENT
2236 : newpc.isdir () ? EINVAL : ENOTDIR);
2237 __leave;
2240 /* A trailing slash requires that the pathname points to an existing
2241 directory. If it's not, it's a ENOTDIR condition. The same goes
2242 for newpath a bit further down this function. */
2243 olen = strlen (oldpath);
2244 if (isdirsep (oldpath[olen - 1]))
2246 char *buf;
2247 char *p = stpcpy (buf = tp.c_get (), oldpath) - 1;
2248 oldpath = buf;
2249 while (p >= oldpath && isdirsep (*p))
2250 *p-- = '\0';
2251 olen = p + 1 - oldpath;
2252 if (!olen)
2254 /* The root directory cannot be renamed. This also rejects
2255 the corner case of rename("/","/"), even though it is the
2256 same file. */
2257 set_errno (EINVAL);
2258 __leave;
2260 old_dir_requested = true;
2262 oldpc.check (oldpath, PC_SYM_NOFOLLOW, stat_suffixes);
2263 if (oldpc.error)
2265 set_errno (oldpc.error);
2266 __leave;
2268 if (!oldpc.exists ())
2270 set_errno (ENOENT);
2271 __leave;
2273 if (oldpc.isspecial () && !oldpc.issocket () && !oldpc.is_fs_special ())
2275 /* No renames from virtual FS */
2276 set_errno (EROFS);
2277 __leave;
2279 if (oldpc.has_attribute (FILE_ATTRIBUTE_REPARSE_POINT)
2280 && !oldpc.issymlink ())
2282 /* Volume mount point. If we try to rename a volume mount point, NT
2283 returns STATUS_NOT_SAME_DEVICE ==> Win32 ERROR_NOT_SAME_DEVICE ==>
2284 errno EXDEV. That's bad since mv(1) will now perform a
2285 cross-device move. So what we do here is to treat the volume
2286 mount point just like Linux treats a mount point. */
2287 set_errno (EBUSY);
2288 __leave;
2290 if (old_dir_requested && !oldpc.isdir ())
2292 /* Reject rename("file/","x"). */
2293 set_errno (ENOTDIR);
2294 __leave;
2296 if (oldpc.known_suffix ()
2297 && (ascii_strcasematch (oldpath + olen - 4, ".lnk")
2298 || ascii_strcasematch (oldpath + olen - 4, ".exe")))
2299 old_explicit_suffix = true;
2301 nlen = strlen (newpath);
2302 if (isdirsep (newpath[nlen - 1]))
2304 char *buf;
2305 char *p = stpcpy (buf = tp.c_get (), newpath) - 1;
2306 newpath = buf;
2307 while (p >= newpath && isdirsep (*p))
2308 *p-- = '\0';
2309 nlen = p + 1 - newpath;
2310 if (!nlen) /* The root directory is never empty. */
2312 set_errno (ENOTEMPTY);
2313 __leave;
2315 new_dir_requested = true;
2317 newpc.check (newpath, PC_SYM_NOFOLLOW, stat_suffixes);
2318 if (newpc.error)
2320 set_errno (newpc.error);
2321 __leave;
2323 if (newpc.isspecial () && !newpc.issocket ())
2325 /* No renames to virtual FSes */
2326 set_errno (EROFS);
2327 __leave;
2329 if (new_dir_requested && !(newpc.exists ()
2330 ? newpc.isdir () : oldpc.isdir ()))
2332 /* Reject rename("file1","file2/"), but allow rename("dir","d/"). */
2333 set_errno (newpc.exists () ? ENOTDIR : ENOENT);
2334 __leave;
2336 if (newpc.exists ()
2337 && (oldpc.isdir () ? !newpc.isdir () : newpc.isdir ()))
2339 /* Reject rename("file","dir") and rename("dir","file"). */
2340 set_errno (newpc.isdir () ? EISDIR : ENOTDIR);
2341 __leave;
2343 if (newpc.known_suffix ()
2344 && (ascii_strcasematch (newpath + nlen - 4, ".lnk")
2345 || ascii_strcasematch (newpath + nlen - 4, ".exe")))
2346 new_explicit_suffix = true;
2348 /* This test is necessary in almost every case, so do it once here. */
2349 equal_path = RtlEqualUnicodeString (oldpc.get_nt_native_path (),
2350 newpc.get_nt_native_path (),
2351 oldpc.objcaseinsensitive ());
2353 /* First check if oldpath and newpath only differ by case. If so, it's
2354 just a request to change the case of the filename. By simply setting
2355 the file attributes to INVALID_FILE_ATTRIBUTES (which translates to
2356 "file doesn't exist"), all later tests are skipped. */
2357 if (oldpc.objcaseinsensitive () && newpc.exists () && equal_path
2358 && old_explicit_suffix == new_explicit_suffix)
2360 if (RtlEqualUnicodeString (oldpc.get_nt_native_path (),
2361 newpc.get_nt_native_path (),
2362 FALSE))
2364 res = set_same_file_return (noreplace);
2365 __leave;
2367 newpc.file_attributes (INVALID_FILE_ATTRIBUTES);
2369 else if (oldpc.isdir ())
2371 /* Check for newpath being identical or a subdir of oldpath. */
2372 if (RtlPrefixUnicodeString (oldpc.get_nt_native_path (),
2373 newpc.get_nt_native_path (),
2374 oldpc.objcaseinsensitive ()))
2376 if (newpc.get_nt_native_path ()->Length
2377 == oldpc.get_nt_native_path ()->Length)
2379 res = set_same_file_return (noreplace);
2380 __leave;
2382 if (*(PWCHAR) ((PBYTE) newpc.get_nt_native_path ()->Buffer
2383 + oldpc.get_nt_native_path ()->Length) == L'\\')
2385 set_errno (EINVAL);
2386 __leave;
2390 else if (!newpc.exists ())
2392 if (equal_path && old_explicit_suffix != new_explicit_suffix)
2394 newpc.check (newpath, PC_SYM_NOFOLLOW);
2395 if (RtlEqualUnicodeString (oldpc.get_nt_native_path (),
2396 newpc.get_nt_native_path (),
2397 oldpc.objcaseinsensitive ()))
2399 res = set_same_file_return (noreplace);
2400 __leave;
2403 else if (oldpc.is_lnk_special ()
2404 && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
2405 &ro_u_lnk, TRUE))
2406 rename_append_suffix (newpc, newpath, nlen, ".lnk");
2407 else if (oldpc.is_binary () && !old_explicit_suffix
2408 && oldpc.known_suffix ()
2409 && !nt_path_has_executable_suffix
2410 (newpc.get_nt_native_path ()))
2411 /* Never append .exe suffix if oldpath had .exe suffix given
2412 explicitely, or if oldpath wasn't already a .exe file, or
2413 if the destination filename has one of the blessed executable
2414 suffixes.
2415 Note: To rename an executable foo.exe to bar-without-suffix,
2416 the .exe suffix must be given explicitly in oldpath. */
2417 rename_append_suffix (newpc, newpath, nlen, ".exe");
2419 else
2421 if (equal_path && old_explicit_suffix != new_explicit_suffix)
2423 newpc.check (newpath, PC_SYM_NOFOLLOW);
2424 if (RtlEqualUnicodeString (oldpc.get_nt_native_path (),
2425 newpc.get_nt_native_path (),
2426 oldpc.objcaseinsensitive ()))
2428 res = set_same_file_return (noreplace);
2429 __leave;
2432 else if (oldpc.is_lnk_special ())
2434 if (!newpc.is_lnk_special ()
2435 && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
2436 &ro_u_lnk, TRUE))
2438 rename_append_suffix (new2pc, newpath, nlen, ".lnk");
2439 removepc = &newpc;
2442 else if (oldpc.is_binary ())
2444 /* Never append .exe suffix if oldpath had .exe suffix given
2445 explicitely, or if newfile is a binary (in which case the given
2446 name probably makes sense as it is), or if the destination
2447 filename has one of the blessed executable suffixes. */
2448 if (!old_explicit_suffix && oldpc.known_suffix ()
2449 && !newpc.is_binary ()
2450 && !nt_path_has_executable_suffix
2451 (newpc.get_nt_native_path ()))
2453 rename_append_suffix (new2pc, newpath, nlen, ".exe");
2454 removepc = &newpc;
2457 else
2459 /* If the new path is an existing .lnk symlink or a .exe file,
2460 but the new path has not been specified with explicit suffix,
2461 rename to the new name without suffix, as expected, but also
2462 remove the clashing symlink or executable. Did I ever mention
2463 how I hate the file suffix idea? */
2464 if ((newpc.is_lnk_special ()
2465 || RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
2466 &ro_u_exe, TRUE))
2467 && !new_explicit_suffix)
2469 new2pc.check (newpath, PC_SYM_NOFOLLOW, stat_suffixes);
2470 newpc.get_nt_native_path ()->Length -= 4 * sizeof (WCHAR);
2471 if (new2pc.is_binary () || new2pc.is_lnk_special ())
2472 removepc = &new2pc;
2476 dstpc = (removepc == &newpc) ? &new2pc : &newpc;
2478 /* Check cross-device before touching anything. Otherwise we might end
2479 up with an unlinked target dir even if the actual rename didn't work.*/
2480 if (oldpc.fs_type () != dstpc->fs_type ()
2481 || oldpc.fs_serial_number () != dstpc->fs_serial_number ())
2483 set_errno (EXDEV);
2484 __leave;
2487 /* Should we replace an existing file? */
2488 if ((removepc || dstpc->exists ()) && noreplace)
2490 set_errno (EEXIST);
2491 __leave;
2494 /* POSIX semantics only on local NTFS drives. For the OPEN_BY_FILE_ID
2495 flag, see MINIMAL_WIN_NTFS_FLAGS comment in fs_info::update. */
2496 use_posix_semantics = wincap.has_posix_rename_semantics ()
2497 && !oldpc.isremote ()
2498 && oldpc.fs_is_ntfs ()
2499 && oldpc.has_attribute (FILE_SUPPORTS_OPEN_BY_FILE_ID);
2501 ignore_posix_semantics_retry:
2502 /* Opening the file must be part of the transaction. It's not sufficient
2503 to call only NtSetInformationFile under the transaction. Therefore we
2504 have to start the transaction here, if necessary. Don't start
2505 transaction on W10 1709 or later on local NTFS. Use POSIX semantics
2506 instead. */
2507 if (!use_posix_semantics
2508 && (dstpc->fs_flags () & FILE_SUPPORTS_TRANSACTIONS)
2509 && (dstpc->isdir ()
2510 || (!removepc && dstpc->has_attribute (FILE_ATTRIBUTE_READONLY))))
2511 start_transaction (old_trans, trans);
2513 int retry_count;
2514 retry_count = 0;
2515 retry:
2516 /* Talking about inconsistent behaviour...
2517 - DELETE is required to rename a file. So far, so good.
2518 - At least one cifs FS (Tru64) needs FILE_READ_ATTRIBUTE, otherwise the
2519 FileRenameInformation call fails with STATUS_ACCESS_DENIED. However,
2520 on NFS we get a STATUS_ACCESS_DENIED if FILE_READ_ATTRIBUTE is used
2521 and the file we try to rename is a symlink. Urgh.
2522 - Samba (only some versions?) doesn't like the FILE_SHARE_DELETE
2523 mode if the file has the R/O attribute set and returns
2524 STATUS_ACCESS_DENIED in that case. */
2526 ULONG access = DELETE
2527 | (oldpc.fs_is_cifs () ? FILE_READ_ATTRIBUTES : 0);
2528 ULONG sharing = FILE_SHARE_READ | FILE_SHARE_WRITE
2529 | (oldpc.fs_is_samba () ? 0 : FILE_SHARE_DELETE);
2530 ULONG flags = FILE_OPEN_FOR_BACKUP_INTENT
2531 | (oldpc.is_known_reparse_point ()
2532 ? FILE_OPEN_REPARSE_POINT : 0);
2533 status = NtOpenFile (&fh, access,
2534 oldpc.get_object_attr (attr, sec_none_nih),
2535 &io, sharing, flags);
2537 if (!NT_SUCCESS (status))
2539 debug_printf ("status %y", status);
2540 if (status == STATUS_SHARING_VIOLATION
2541 && cygwait (10L) != WAIT_SIGNALED)
2543 /* Typical BLODA problem. Some virus scanners check newly
2544 generated files and while doing that disallow DELETE access.
2545 That's really bad because it breaks applications which copy
2546 files by creating a temporary filename and then rename the
2547 temp filename to the target filename. This renaming fails due
2548 to the jealous virus scanner and the application fails to
2549 create the target file.
2551 This kludge tries to work around that by yielding until the
2552 sharing violation goes away, or a signal arrived, or after
2553 about a second, give or take. */
2554 if (++retry_count < 40)
2556 yield ();
2557 goto retry;
2560 else if (NT_TRANSACTIONAL_ERROR (status) && trans)
2562 /* If NtOpenFile fails due to transactional problems, stop
2563 transaction and go ahead without. */
2564 stop_transaction (status, old_trans, trans);
2565 debug_printf ("Transaction failure. Retry open.");
2566 goto retry;
2568 __seterrno_from_nt_status (status);
2569 __leave;
2572 if (use_posix_semantics)
2573 goto skip_pre_W10_checks;
2575 /* Renaming a dir to another, existing dir fails always, even if
2576 ReplaceIfExists is set to TRUE and the existing dir is empty. So
2577 we have to remove the destination dir first. This also covers the
2578 case that the destination directory is not empty. In that case,
2579 unlink_nt returns with STATUS_DIRECTORY_NOT_EMPTY. */
2580 if (dstpc->isdir ())
2582 status = unlink_nt (*dstpc, false);
2583 if (!NT_SUCCESS (status))
2585 __seterrno_from_nt_status (status);
2586 __leave;
2589 /* You can't copy a file if the destination exists and has the R/O
2590 attribute set. Remove the R/O attribute first. But first check
2591 if a removepc exists. If so, dstpc points to a non-existing file
2592 due to a mangled suffix. */
2593 else if (!removepc && dstpc->has_attribute (FILE_ATTRIBUTE_READONLY))
2595 status = NtOpenFile (&nfh, FILE_WRITE_ATTRIBUTES,
2596 dstpc->get_object_attr (attr, sec_none_nih),
2597 &io, FILE_SHARE_VALID_FLAGS,
2598 FILE_OPEN_FOR_BACKUP_INTENT
2599 | (dstpc->is_known_reparse_point ()
2600 ? FILE_OPEN_REPARSE_POINT : 0));
2601 if (!NT_SUCCESS (status))
2603 __seterrno_from_nt_status (status);
2604 __leave;
2606 status = NtSetAttributesFile (nfh, dstpc->file_attributes ()
2607 & ~FILE_ATTRIBUTE_READONLY);
2608 NtClose (nfh);
2609 if (!NT_SUCCESS (status))
2611 __seterrno_from_nt_status (status);
2612 __leave;
2616 skip_pre_W10_checks:
2618 /* SUSv3: If the old argument and the new argument resolve to the same
2619 existing file, rename() shall return successfully and perform no
2620 other action.
2621 The test tries to be as quick as possible. Due to the above cross
2622 device check we already know both files are on the same device. So
2623 it just tests if oldpath has more than 1 hardlink, then it opens
2624 newpath and tests for identical file ids. If so, oldpath and newpath
2625 refer to the same file. */
2626 if ((removepc || dstpc->exists ())
2627 && !oldpc.isdir ()
2628 && NT_SUCCESS (NtQueryInformationFile (fh, &io, &ofsi, sizeof ofsi,
2629 FileStandardInformation))
2630 && ofsi.NumberOfLinks > 1
2631 && NT_SUCCESS (NtOpenFile (&nfh, READ_CONTROL,
2632 (removepc ?: dstpc)->get_object_attr (attr, sec_none_nih),
2633 &io, FILE_SHARE_VALID_FLAGS,
2634 FILE_OPEN_FOR_BACKUP_INTENT
2635 | ((removepc ?: dstpc)->is_known_reparse_point ()
2636 ? FILE_OPEN_REPARSE_POINT : 0))))
2638 FILE_INTERNAL_INFORMATION ofii, nfii;
2640 if (NT_SUCCESS (NtQueryInformationFile (fh, &io, &ofii, sizeof ofii,
2641 FileInternalInformation))
2642 && NT_SUCCESS (NtQueryInformationFile (nfh, &io, &nfii,
2643 sizeof nfii,
2644 FileInternalInformation))
2645 && ofii.IndexNumber.QuadPart == nfii.IndexNumber.QuadPart)
2647 debug_printf ("%s and %s are the same file", oldpath, newpath);
2648 NtClose (nfh);
2649 res = set_same_file_return (noreplace);
2650 __leave;
2652 NtClose (nfh);
2654 /* Create FILE_RENAME_INFORMATION struct. Using a tmp_pathbuf area
2655 allows for paths of up to 32757 chars. This test is just for
2656 paranoia's sake. */
2657 if (dstpc->get_nt_native_path ()->Length
2658 > NT_MAX_PATH * sizeof (WCHAR) - sizeof (FILE_RENAME_INFORMATION))
2660 debug_printf ("target filename too long");
2661 set_errno (EINVAL);
2662 __leave;
2664 pfri = (PFILE_RENAME_INFORMATION) tp.w_get ();
2665 if (use_posix_semantics)
2666 pfri->Flags = noreplace ? 0
2667 : (FILE_RENAME_REPLACE_IF_EXISTS
2668 | FILE_RENAME_POSIX_SEMANTICS
2669 | FILE_RENAME_IGNORE_READONLY_ATTRIBUTE);
2670 else
2671 pfri->ReplaceIfExists = !noreplace;
2672 pfri->RootDirectory = NULL;
2673 pfri->FileNameLength = dstpc->get_nt_native_path ()->Length;
2674 memcpy (&pfri->FileName, dstpc->get_nt_native_path ()->Buffer,
2675 pfri->FileNameLength);
2676 /* If dstpc points to an existing file and RENAME_NOREPLACE has
2677 been specified, then we should get NT error
2678 STATUS_OBJECT_NAME_COLLISION ==> Win32 error
2679 ERROR_ALREADY_EXISTS ==> Cygwin error EEXIST. */
2680 status = NtSetInformationFile (fh, &io, pfri,
2681 sizeof *pfri + pfri->FileNameLength,
2682 use_posix_semantics
2683 ? FileRenameInformationEx
2684 : FileRenameInformation);
2685 /* This happens if the access rights don't allow deleting the destination.
2686 Even if the handle to the original file is opened with BACKUP
2687 and/or RECOVERY, these flags don't apply to the destination of the
2688 rename operation. So, a privileged user can't rename a file to an
2689 existing file, if the permissions of the existing file aren't right.
2690 Like directories, we have to handle this separately by removing the
2691 destination before renaming. */
2692 if (status == STATUS_ACCESS_DENIED && dstpc->exists ()
2693 && !dstpc->isdir ())
2695 bool need_open = false;
2697 if ((dstpc->fs_flags () & FILE_SUPPORTS_TRANSACTIONS) && !trans)
2699 /* As mentioned earlier, opening the file must be part of the
2700 transaction. Therefore we have to reopen the file here if the
2701 transaction hasn't been started already. Unfortunately we
2702 can't use the NT "reopen file from existing handle" feature.
2703 In that case NtOpenFile returns STATUS_TRANSACTIONAL_CONFLICT.
2704 We *have* to close the handle to the file first, *then* we can
2705 re-open it. Fortunately nothing has happened yet, so the
2706 atomicity of the rename functionality is not spoiled. */
2707 NtClose (fh);
2708 start_transaction (old_trans, trans);
2709 need_open = true;
2711 while (true)
2713 status = STATUS_SUCCESS;
2714 if (need_open)
2715 status = NtOpenFile (&fh, DELETE,
2716 oldpc.get_object_attr (attr, sec_none_nih),
2717 &io, FILE_SHARE_VALID_FLAGS,
2718 FILE_OPEN_FOR_BACKUP_INTENT
2719 | (oldpc.is_known_reparse_point ()
2720 ? FILE_OPEN_REPARSE_POINT : 0));
2721 if (NT_SUCCESS (status))
2723 status = unlink_nt (*dstpc, false);
2724 if (NT_SUCCESS (status))
2725 break;
2727 if (!NT_TRANSACTIONAL_ERROR (status) || !trans)
2728 break;
2729 /* If NtOpenFile or unlink_nt fail due to transactional problems,
2730 stop transaction and retry without. */
2731 NtClose (fh);
2732 stop_transaction (status, old_trans, trans);
2733 debug_printf ("Transaction failure %y. Retry open.", status);
2735 if (NT_SUCCESS (status))
2736 status = NtSetInformationFile (fh, &io, pfri,
2737 sizeof *pfri + pfri->FileNameLength,
2738 FileRenameInformation);
2740 if (NT_SUCCESS (status))
2742 if (removepc)
2743 unlink_nt (*removepc, false);
2744 res = 0;
2746 else if (use_posix_semantics && status == STATUS_INVALID_PARAMETER)
2748 /* NtSetInformationFile returns STATUS_INVALID_PARAMETER
2749 on a bind mounted file system in hyper-v container
2750 with FILE_RENAME_POSIX_SEMANTICS.
2751 Disable the use_posix semntics flag and retry. */
2752 debug_printf ("NtSetInformationFile failed with posix semantics. "
2753 "Disable it and retry.");
2754 use_posix_semantics = 0;
2755 goto ignore_posix_semantics_retry;
2757 else
2758 __seterrno_from_nt_status (status);
2760 __except (EFAULT)
2762 res = -1;
2764 __endtry
2765 if (fh)
2766 NtClose (fh);
2767 /* Stop transaction if we started one. */
2768 if (trans)
2769 stop_transaction (status, old_trans, trans);
2770 if (get_errno () != EFAULT)
2771 syscall_printf ("%R = rename(%s, %s)", res, oldpath, newpath);
2772 return res;
2775 extern "C" int
2776 rename (const char *oldpath, const char *newpath)
2778 return rename2 (oldpath, newpath, 0);
2781 extern "C" int
2782 system (const char *cmdstring)
2784 pthread_testcancel ();
2786 if (cmdstring == NULL)
2787 return 1;
2789 int res = -1;
2790 const char* command[4];
2792 __try
2794 command[0] = "sh";
2795 command[1] = "-c";
2796 command[2] = cmdstring;
2797 command[3] = (const char *) NULL;
2799 if ((res = spawnvp (_P_SYSTEM, "/bin/sh", command)) == -1)
2801 // when exec fails, return value should be as if shell
2802 // executed exit (127)
2803 res = 127;
2806 __except (EFAULT) {}
2807 __endtry
2808 return res;
2811 extern "C" int
2812 setdtablesize (int size)
2814 if (size < 0)
2816 set_errno (EINVAL);
2817 return -1;
2820 if (size <= (int) cygheap->fdtab.size
2821 || cygheap->fdtab.extend (size - cygheap->fdtab.size, OPEN_MAX))
2822 return 0;
2824 return -1;
2827 extern "C" int
2828 getdtablesize ()
2830 return OPEN_MAX;
2833 extern "C" int
2834 getpagesize ()
2836 return (size_t) wincap.allocation_granularity ();
2839 /* FIXME: not all values are correct... */
2840 extern "C" long int
2841 fpathconf (int fd, int v)
2843 cygheap_fdget cfd (fd);
2844 if (cfd < 0)
2845 return -1;
2846 return cfd->fpathconf (v);
2849 extern "C" long int
2850 pathconf (const char *file, int v)
2852 fhandler_base *fh = NULL;
2853 long ret = -1;
2855 __try
2857 if (!*file)
2859 set_errno (ENOENT);
2860 return -1;
2862 if (!(fh = build_fh_name (file, PC_SYM_FOLLOW, stat_suffixes)))
2863 return -1;
2864 if (!fh->exists ())
2865 set_errno (ENOENT);
2866 else
2867 ret = fh->fpathconf (v);
2869 __except (EFAULT) {}
2870 __endtry
2871 delete fh;
2872 return ret;
2875 extern "C" int
2876 ttyname_r (int fd, char *buf, size_t buflen)
2878 int ret = 0;
2880 __try
2882 cygheap_fdget cfd (fd, true);
2883 if (cfd < 0)
2884 ret = EBADF;
2885 else if (!cfd->is_tty ())
2886 ret = ENOTTY;
2887 else if (buflen < strlen (cfd->ttyname ()) + 1)
2888 ret = ERANGE;
2889 else
2890 strcpy (buf, cfd->ttyname ());
2891 debug_printf ("returning %d tty: %s", ret, ret ? "NULL" : buf);
2893 __except (NO_ERROR)
2895 ret = EFAULT;
2897 __endtry
2898 return ret;
2901 extern "C" char *
2902 ttyname (int fd)
2904 static char name[TTY_NAME_MAX];
2905 int ret = ttyname_r (fd, name, TTY_NAME_MAX);
2906 if (ret)
2908 set_errno (ret);
2909 return NULL;
2911 return name;
2914 extern "C" char *
2915 ctermid (char *str)
2917 if (str == NULL)
2918 str = _my_tls.locals.ttybuf;
2919 if (!CTTY_IS_VALID (myself->ctty))
2920 strcpy (str, "no tty");
2921 else
2923 device d;
2924 d.parse (myself->ctty);
2925 strcpy (str, d.name ());
2927 return str;
2930 /* Tells stdio if it should do the cr/lf conversion for this file */
2931 extern "C" int
2932 _cygwin_istext_for_stdio (int fd)
2934 cygheap_fdget cfd (fd, false, false);
2935 if (cfd < 0)
2937 syscall_printf ("fd %d: not open", fd);
2938 return 0;
2941 #if 0
2942 if (cfd->get_device () != FH_FS)
2944 syscall_printf ("fd not disk file. Defaulting to binary.");
2945 return 0;
2947 #endif
2949 if (cfd->wbinary () || cfd->rbinary ())
2951 syscall_printf ("fd %d: opened as binary", fd);
2952 return 0;
2955 syscall_printf ("fd %d: defaulting to text", fd);
2956 return 1;
2959 static int
2960 setmode_helper (struct _reent *ptr __unused, FILE *f)
2962 if (fileno (f) != _my_tls.locals.setmode_file)
2964 syscall_printf ("improbable, but %d != %d", fileno (f), _my_tls.locals.setmode_file);
2965 return 0;
2967 syscall_printf ("file was %s now %s", f->_flags & __SCLE ? "text" : "binary",
2968 _my_tls.locals.setmode_mode & O_TEXT ? "text" : "binary");
2969 if (_my_tls.locals.setmode_mode & O_TEXT)
2970 f->_flags |= __SCLE;
2971 else
2972 f->_flags &= ~__SCLE;
2973 return 0;
2976 extern "C" int
2977 getmode (int fd)
2979 cygheap_fdget cfd (fd);
2980 if (cfd < 0)
2981 return -1;
2983 return cfd->get_flags () & (O_BINARY | O_TEXT);
2986 /* Set a file descriptor into text or binary mode, returning the
2987 previous mode. */
2989 extern "C" int
2990 _setmode (int fd, int mode)
2992 cygheap_fdget cfd (fd);
2993 if (cfd < 0)
2994 return -1;
2995 if (mode != O_BINARY && mode != O_TEXT && mode != 0)
2997 set_errno (EINVAL);
2998 return -1;
3001 /* Note that we have no way to indicate the case that writes are
3002 binary but not reads, or vice-versa. These cases can arise when
3003 using the tty or console interface. People using those
3004 interfaces should not use setmode. */
3006 int res;
3007 if (cfd->wbinary () && cfd->rbinary ())
3008 res = O_BINARY;
3009 else if (cfd->wbinset () && cfd->rbinset ())
3010 res = O_TEXT; /* Specifically set O_TEXT */
3011 else
3012 res = 0;
3014 if (!mode)
3015 cfd->reset_to_open_binmode ();
3016 else
3017 cfd->set_flags ((cfd->get_flags () & ~(O_TEXT | O_BINARY)) | mode);
3019 syscall_printf ("(%d<%S>, %p) returning %s", fd,
3020 cfd->pc.get_nt_native_path (), mode,
3021 res & O_TEXT ? "text" : "binary");
3022 return res;
3025 extern "C" int
3026 cygwin_setmode (int fd, int mode)
3028 int res = _setmode (fd, mode);
3029 if (res != -1)
3031 _my_tls.locals.setmode_file = fd;
3032 if (_cygwin_istext_for_stdio (fd))
3033 _my_tls.locals.setmode_mode = O_TEXT;
3034 else
3035 _my_tls.locals.setmode_mode = O_BINARY;
3036 _fwalk_sglue (_GLOBAL_REENT, setmode_helper, &__sglue);
3038 return res;
3041 extern "C" int
3042 posix_fadvise (int fd, off_t offset, off_t len, int advice)
3044 int res = -1;
3045 cygheap_fdget cfd (fd);
3046 if (cfd >= 0)
3047 res = cfd->fadvise (offset, len, advice);
3048 else
3049 res = EBADF;
3050 syscall_printf ("%R = posix_fadvice(%d, %D, %D, %d)",
3051 res, fd, offset, len, advice);
3052 return res;
3055 extern "C" int
3056 fallocate (int fd, int mode, off_t offset, off_t len)
3058 int res = 0;
3060 /* First check mask of allowed flags */
3061 if (mode & ~(FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE
3062 | FALLOC_FL_UNSHARE_RANGE | FALLOC_FL_COLLAPSE_RANGE
3063 | FALLOC_FL_INSERT_RANGE | FALLOC_FL_KEEP_SIZE))
3064 res = EOPNOTSUPP;
3065 /* Either FALLOC_FL_PUNCH_HOLE or FALLOC_FL_ZERO_RANGE, never both */
3066 else if ((mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE))
3067 == (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE))
3068 res = EOPNOTSUPP;
3069 /* FALLOC_FL_PUNCH_HOLE must be ORed with FALLOC_FL_KEEP_SIZE */
3070 else if ((mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE))
3071 == FALLOC_FL_PUNCH_HOLE)
3072 res = EOPNOTSUPP;
3073 else if (offset < 0 || len <= 0)
3074 res = EINVAL;
3075 else if (INT64_MAX - len < offset)
3076 res = EFBIG;
3077 else
3079 cygheap_fdget cfd (fd);
3080 if (cfd >= 0)
3081 res = cfd->fallocate (mode, offset, len);
3082 else
3083 res = EBADF;
3084 if (res == EISDIR)
3085 res = ENODEV;
3087 if (res)
3089 set_errno (res);
3090 res = -1;
3092 syscall_printf ("%R = fallocate(%d, %y, %D, %D)", res, fd, mode, offset, len);
3093 return res;
3096 extern "C" int
3097 posix_fallocate (int fd, off_t offset, off_t len)
3099 int res = 0;
3100 if (offset < 0 || len <= 0)
3101 res = EINVAL;
3102 else if (INT64_MAX - len < offset)
3103 res = EFBIG;
3104 else
3106 cygheap_fdget cfd (fd);
3107 if (cfd >= 0)
3108 res = cfd->fallocate (0, offset, len);
3109 else
3110 res = EBADF;
3111 if (res == EISDIR)
3112 res = ENODEV;
3114 syscall_printf ("%R = posix_fallocate(%d, %D, %D)", res, fd, offset, len);
3115 return res;
3118 extern "C" int
3119 ftruncate (int fd, off_t length)
3121 int res = -1;
3122 cygheap_fdget cfd (fd);
3123 if (cfd >= 0)
3125 res = cfd->fallocate (__FALLOC_FL_TRUNCATE, 0, length);
3126 if (res)
3128 if (res == ENODEV)
3129 res = EINVAL;
3130 set_errno (res);
3131 res = -1;
3134 else
3135 set_errno (EBADF);
3136 syscall_printf ("%R = ftruncate(%d, %D)", res, fd, length);
3137 return res;
3140 /* truncate: Provided by SVR4 and 4.3+BSD. Not part of POSIX.1 or XPG3 */
3141 extern "C" int
3142 truncate (const char *pathname, off_t length)
3144 int fd;
3145 int res = -1;
3147 fd = open (pathname, O_RDWR);
3149 if (fd != -1)
3151 res = ftruncate (fd, length);
3152 close (fd);
3154 syscall_printf ("%R = truncate(%s, %D)", res, pathname, length);
3156 return res;
3159 extern "C" long
3160 _get_osfhandle (int fd)
3162 long res;
3164 cygheap_fdget cfd (fd);
3165 if (cfd >= 0)
3166 res = (long) cfd->get_handle ();
3167 else
3168 res = -1;
3170 syscall_printf ("%R = get_osfhandle(%d)", res, fd);
3171 return res;
3174 extern "C" int
3175 fstatvfs (int fd, struct statvfs *sfs)
3177 __try
3179 cygheap_fdget cfd (fd);
3180 if (cfd < 0)
3181 __leave;
3182 return cfd->fstatvfs (sfs);
3184 __except (EFAULT) {}
3185 __endtry
3186 return -1;
3189 extern "C" int
3190 statvfs (const char *name, struct statvfs *sfs)
3192 int res = -1;
3193 fhandler_base *fh = NULL;
3195 __try
3197 if (!(fh = build_fh_name (name, PC_SYM_FOLLOW, stat_suffixes)))
3198 __leave;
3200 if (fh->error ())
3202 debug_printf ("got %d error from build_fh_name", fh->error ());
3203 set_errno (fh->error ());
3205 else if (fh->exists ())
3207 debug_printf ("(%s, %p), file_attributes %d", name, sfs, (DWORD) *fh);
3208 res = fh->fstatvfs (sfs);
3210 else
3211 set_errno (ENOENT);
3214 __except (EFAULT) {}
3215 __endtry
3216 delete fh;
3217 if (get_errno () != EFAULT)
3218 syscall_printf ("%R = statvfs(%s,%p)", res, name, sfs);
3219 return res;
3222 extern "C" int
3223 fstatfs (int fd, struct statfs *sfs)
3225 struct statvfs vfs;
3226 int ret = fstatvfs (fd, &vfs);
3227 if (!ret)
3229 sfs->f_type = vfs.f_flag;
3230 sfs->f_bsize = vfs.f_bsize;
3231 sfs->f_blocks = vfs.f_blocks;
3232 sfs->f_bavail = vfs.f_bavail;
3233 sfs->f_bfree = vfs.f_bfree;
3234 sfs->f_files = -1;
3235 sfs->f_ffree = -1;
3236 sfs->f_fsid = vfs.f_fsid;
3237 sfs->f_namelen = vfs.f_namemax;
3239 return ret;
3242 extern "C" int
3243 statfs (const char *fname, struct statfs *sfs)
3245 struct statvfs vfs;
3246 int ret = statvfs (fname, &vfs);
3247 if (!ret)
3249 sfs->f_type = vfs.f_flag;
3250 sfs->f_bsize = vfs.f_bsize;
3251 sfs->f_blocks = vfs.f_blocks;
3252 sfs->f_bavail = vfs.f_bavail;
3253 sfs->f_bfree = vfs.f_bfree;
3254 sfs->f_files = -1;
3255 sfs->f_ffree = -1;
3256 sfs->f_fsid = vfs.f_fsid;
3257 sfs->f_namelen = vfs.f_namemax;
3259 return ret;
3262 /* setpgid: POSIX 4.3.3.1 */
3263 extern "C" int
3264 setpgid (pid_t pid, pid_t pgid)
3266 int res = -1;
3267 if (pid == 0)
3268 pid = getpid ();
3269 if (pgid == 0)
3270 pgid = pid;
3272 if (pgid < 0)
3273 set_errno (EINVAL);
3274 else
3276 pinfo p (pid, PID_MAP_RW);
3277 if (!p)
3278 set_errno (ESRCH);
3279 else if (p->pgid == pgid)
3280 res = 0;
3281 /* A process may only change the process group of itself and its children */
3282 else if (p != myself && p->ppid != myself->pid)
3283 set_errno (EPERM);
3284 else
3286 p->pgid = pgid;
3287 if (p->pid != p->pgid)
3288 p->set_has_pgid_children (0);
3289 res = 0;
3293 syscall_printf ("pid %d, pgid %d, res %d", pid, pgid, res);
3294 return res;
3297 extern "C" pid_t
3298 getpgid (pid_t pid)
3300 if (pid == 0)
3301 pid = getpid ();
3303 pinfo p (pid);
3304 if (!p)
3306 set_errno (ESRCH);
3307 return -1;
3309 return p->pgid;
3312 extern "C" int
3313 setpgrp (void)
3315 return setpgid (0, 0);
3318 extern "C" pid_t
3319 getpgrp (void)
3321 return getpgid (0);
3324 extern "C" char *
3325 ptsname (int fd)
3327 static char buf[TTY_NAME_MAX];
3328 return ptsname_r (fd, buf, sizeof (buf)) == 0 ? buf : NULL;
3331 extern "C" int
3332 ptsname_r (int fd, char *buf, size_t buflen)
3334 if (!buf)
3336 set_errno (EINVAL);
3337 return EINVAL;
3340 cygheap_fdget cfd (fd);
3341 if (cfd < 0)
3342 return EBADF;
3343 return cfd->ptsname_r (buf, buflen);
3346 static int
3347 mknod_worker (path_conv &pc, mode_t mode, _major_t major, _minor_t minor)
3349 char buf[sizeof (":\\00000000:00000000:00000000") + PATH_MAX];
3350 sprintf (buf, ":\\%x:%x:%x", major, minor, mode);
3351 return symlink_worker (buf, pc, true);
3354 extern "C" int
3355 mknod (const char *path, mode_t mode, dev_t dev)
3357 __try
3359 if (!*path)
3361 set_errno (ENOENT);
3362 __leave;
3365 if (strlen (path) >= PATH_MAX)
3366 __leave;
3368 /* Trailing dirsep is a no-no, only errno differs. */
3369 bool has_trailing_dirsep = isdirsep (path[strlen (path) - 1]);
3371 path_conv w32path (path, PC_SYM_NOFOLLOW | PC_SYM_NOFOLLOW_DIR
3372 | PC_POSIX, stat_suffixes);
3374 if (w32path.exists () || has_trailing_dirsep)
3376 set_errno (w32path.exists () ? EEXIST : ENOENT);
3377 __leave;
3380 mode_t type = mode & S_IFMT;
3381 _major_t major = _major (dev);
3382 _minor_t minor = _minor (dev);
3383 switch (type)
3385 case S_IFCHR:
3386 case S_IFBLK:
3387 break;
3389 case S_IFIFO:
3390 major = _major (FH_FIFO);
3391 minor = _minor (FH_FIFO);
3392 break;
3394 case 0:
3395 case S_IFREG:
3397 int fd = open (path, O_CREAT, mode);
3398 if (fd < 0)
3399 __leave;
3400 close (fd);
3401 return 0;
3404 default:
3405 set_errno (EINVAL);
3406 __leave;
3409 return mknod_worker (w32path, mode, major, minor);
3411 __except (EFAULT)
3412 __endtry
3413 return -1;
3416 extern "C" int
3417 mkfifo (const char *path, mode_t mode)
3419 return mknod (path, (mode & ~S_IFMT) | S_IFIFO, 0);
3422 /* seteuid: standards? */
3423 extern "C" int
3424 seteuid (uid_t uid)
3426 debug_printf ("uid: %u myself->uid: %u myself->gid: %u",
3427 uid, myself->uid, myself->gid);
3429 /* Same uid as we're just running under is usually a no-op.
3431 Except we have an external token which is a restricted token. Or,
3432 the external token is NULL, but the current impersonation token is
3433 a restricted token. This allows to restrict user rights temporarily
3434 like this:
3436 cygwin_internal(CW_SET_EXTERNAL_TOKEN, restricted_token,
3437 CW_TOKEN_RESTRICTED);
3438 setuid (getuid ());
3439 [...do stuff with restricted rights...]
3440 cygwin_internal(CW_SET_EXTERNAL_TOKEN, INVALID_HANDLE_VALUE,
3441 CW_TOKEN_RESTRICTED);
3442 setuid (getuid ());
3444 Note that using the current uid is a requirement! We have restricted
3445 tokens galore (UAC), so this is really just a special case to restrict
3446 your own processes to lesser rights. */
3447 bool request_restricted_uid_switch = (uid == myself->uid
3448 && cygheap->user.ext_token_is_restricted);
3449 if (uid == myself->uid && !cygheap->user.groups.ischanged
3450 && !request_restricted_uid_switch)
3452 debug_printf ("Nothing happens");
3453 return 0;
3456 cygsid usersid;
3457 user_groups &groups = cygheap->user.groups;
3458 HANDLE new_token = NULL;
3459 struct passwd * pw_new;
3460 bool token_is_internal, issamesid = false;
3462 pw_new = internal_getpwuid (uid);
3463 if (!usersid.getfrompw (pw_new))
3465 set_errno (EINVAL);
3466 return -1;
3469 cygheap->user.deimpersonate ();
3471 /* Verify if the process token is suitable. */
3472 /* First of all, skip all checks if a switch to a restricted token has been
3473 requested, or if trying to switch back from it. */
3474 if (request_restricted_uid_switch)
3476 if (cygheap->user.external_token != NO_IMPERSONATION)
3478 debug_printf ("Switch to restricted token");
3479 new_token = cygheap->user.external_token;
3481 else
3483 debug_printf ("Switch back from restricted token");
3484 new_token = hProcToken;
3485 cygheap->user.ext_token_is_restricted = false;
3488 /* TODO, CV 2008-11-25: The check against saved_sid is a kludge and a
3489 shortcut. We must check if it's really feasible in the long run.
3490 The reason to add this shortcut is this: sshd switches back to the
3491 privileged user running sshd at least twice in the process of
3492 authentication. It calls seteuid first, then setegid. Due to this
3493 order, the setgroups group list is still active when calling seteuid
3494 and verify_token treats the original token of the privileged user as
3495 insufficient. This in turn results in creating a new user token for
3496 the privileged user instead of using the original token. This can have
3497 unfortunate side effects. The created token has different group
3498 memberships, different user rights, and misses possible network
3499 credentials.
3500 Therefore we try this shortcut now. When switching back to the
3501 privileged user, we probably always want a correct (aka original)
3502 user token for this privileged user, not only in sshd. */
3503 else if ((uid == cygheap->user.saved_uid
3504 && usersid == cygheap->user.saved_sid ())
3505 || verify_token (hProcToken, usersid, groups))
3506 new_token = hProcToken;
3507 /* Verify if the external token is suitable */
3508 else if (cygheap->user.external_token != NO_IMPERSONATION
3509 && verify_token (cygheap->user.external_token, usersid, groups))
3510 new_token = cygheap->user.external_token;
3511 /* Verify if the current token (internal or former external) is suitable */
3512 else if (cygheap->user.curr_primary_token != NO_IMPERSONATION
3513 && cygheap->user.curr_primary_token != cygheap->user.external_token
3514 && verify_token (cygheap->user.curr_primary_token, usersid, groups,
3515 &token_is_internal))
3516 new_token = cygheap->user.curr_primary_token;
3517 /* Verify if the internal token is suitable */
3518 else if (cygheap->user.internal_token != NO_IMPERSONATION
3519 && cygheap->user.internal_token != cygheap->user.curr_primary_token
3520 && verify_token (cygheap->user.internal_token, usersid, groups,
3521 &token_is_internal))
3522 new_token = cygheap->user.internal_token;
3524 debug_printf ("Found token %p", new_token);
3526 /* If no impersonation token is available, try to authenticate using
3527 LSA private data stored password, or, if that fails, S4U logon. */
3528 if (new_token == NULL)
3530 if (!(new_token = lsaprivkeyauth (pw_new)))
3532 NTSTATUS status;
3533 WCHAR domain[MAX_DOMAIN_NAME_LEN + 1];
3534 WCHAR user[UNLEN + 1];
3536 debug_printf ("lsaprivkeyauth failed, try s4uauth.");
3537 extract_nt_dom_user (pw_new, domain, user);
3538 if (!(new_token = s4uauth (true, domain, user, status)))
3540 debug_printf ("s4uauth failed, bail out");
3541 cygheap->user.reimpersonate ();
3542 return -1;
3546 /* Keep at most one internal token */
3547 if (cygheap->user.internal_token != NO_IMPERSONATION)
3548 CloseHandle (cygheap->user.internal_token);
3549 cygheap->user.internal_token = new_token;
3552 if (new_token != hProcToken)
3554 NTSTATUS status;
3556 if (!request_restricted_uid_switch)
3557 load_user_profile (new_token, pw_new, usersid);
3559 /* Try setting owner to same value as user. */
3560 status = NtSetInformationToken (new_token, TokenOwner,
3561 &usersid, sizeof usersid);
3562 if (!NT_SUCCESS (status))
3563 debug_printf ("NtSetInformationToken (user.token, TokenOwner), %y",
3564 status);
3565 /* Try setting primary group in token to current group */
3566 status = NtSetInformationToken (new_token, TokenPrimaryGroup,
3567 &groups.pgsid, sizeof (cygsid));
3568 if (!NT_SUCCESS (status))
3569 debug_printf ("NtSetInformationToken (user.token, TokenPrimaryGroup),"
3570 "%y", status);
3571 /* Try setting default DACL */
3572 PACL dacl_buf = (PACL) alloca (MAX_DACL_LEN (5));
3573 if (sec_acl (dacl_buf, true, true, usersid))
3575 TOKEN_DEFAULT_DACL tdacl = { dacl_buf };
3576 status = NtSetInformationToken (new_token, TokenDefaultDacl,
3577 &tdacl, sizeof (tdacl));
3578 if (!NT_SUCCESS (status))
3579 debug_printf ("NtSetInformationToken (TokenDefaultDacl), %y",
3580 status);
3584 issamesid = (usersid == cygheap->user.sid ());
3585 cygheap->user.set_sid (usersid);
3586 cygheap->user.curr_primary_token = new_token == hProcToken ? NO_IMPERSONATION
3587 : new_token;
3588 cygheap->user.curr_token_is_restricted = false;
3589 cygheap->user.setuid_to_restricted = false;
3590 if (cygheap->user.curr_imp_token != NO_IMPERSONATION)
3592 CloseHandle (cygheap->user.curr_imp_token);
3593 cygheap->user.curr_imp_token = NO_IMPERSONATION;
3595 if (cygheap->user.curr_primary_token != NO_IMPERSONATION)
3597 /* HANDLE_FLAG_INHERIT may be missing in external token. */
3598 if (!SetHandleInformation (cygheap->user.curr_primary_token,
3599 HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)
3600 || !DuplicateTokenEx (cygheap->user.curr_primary_token,
3601 MAXIMUM_ALLOWED, &sec_none,
3602 SecurityImpersonation, TokenImpersonation,
3603 &cygheap->user.curr_imp_token))
3605 __seterrno ();
3606 cygheap->user.curr_primary_token = NO_IMPERSONATION;
3607 return -1;
3609 cygheap->user.curr_token_is_restricted = request_restricted_uid_switch;
3610 set_cygwin_privileges (cygheap->user.curr_primary_token);
3611 set_cygwin_privileges (cygheap->user.curr_imp_token);
3613 if (!cygheap->user.reimpersonate ())
3615 __seterrno ();
3616 return -1;
3619 cygheap->user.set_name (pw_new->pw_name);
3620 myself->uid = uid;
3621 groups.ischanged = FALSE;
3622 if (!issamesid)
3623 /* Recreate and fill out the user shared region for a new user. */
3624 user_info::create (true);
3625 return 0;
3628 /* setuid: POSIX 4.2.2.1 */
3629 extern "C" int
3630 setuid (uid_t uid)
3632 int ret = seteuid (uid);
3633 if (!ret)
3635 cygheap->user.real_uid = myself->uid;
3636 /* If restricted token, forget original privileges on exec (). */
3637 cygheap->user.setuid_to_restricted = cygheap->user.curr_token_is_restricted;
3639 debug_printf ("real: %d, effective: %d", cygheap->user.real_uid, myself->uid);
3640 return ret;
3643 extern "C" int
3644 setreuid (uid_t ruid, uid_t euid)
3646 int ret = 0;
3647 bool tried = false;
3648 uid_t old_euid = myself->uid;
3650 if (ruid != ILLEGAL_UID && cygheap->user.real_uid != ruid && euid != ruid)
3651 tried = !(ret = seteuid (ruid));
3652 if (!ret && euid != ILLEGAL_UID)
3653 ret = seteuid (euid);
3654 if (tried && (ret || euid == ILLEGAL_UID) && seteuid (old_euid))
3655 system_printf ("Cannot restore original euid %u", old_euid);
3656 if (!ret && ruid != ILLEGAL_UID)
3657 cygheap->user.real_uid = ruid;
3658 debug_printf ("real: %u, effective: %u", cygheap->user.real_uid, myself->uid);
3659 return ret;
3662 /* setegid: from System V. */
3663 extern "C" int
3664 setegid (gid_t gid)
3666 debug_printf ("new egid: %u current: %u", gid, myself->gid);
3668 if (gid == myself->gid)
3670 myself->gid = gid;
3671 return 0;
3674 NTSTATUS status;
3675 user_groups * groups = &cygheap->user.groups;
3676 cygsid gsid;
3677 struct group * gr = internal_getgrgid (gid);
3679 if (!gsid.getfromgr (gr))
3681 set_errno (EINVAL);
3682 return -1;
3684 myself->gid = gid;
3686 groups->update_pgrp (gsid);
3687 if (cygheap->user.issetuid ())
3689 /* If impersonated, update impersonation token... */
3690 status = NtSetInformationToken (cygheap->user.primary_token (),
3691 TokenPrimaryGroup, &gsid, sizeof gsid);
3692 if (!NT_SUCCESS (status))
3693 debug_printf ("NtSetInformationToken (primary_token, "
3694 "TokenPrimaryGroup), %y", status);
3695 status = NtSetInformationToken (cygheap->user.imp_token (),
3696 TokenPrimaryGroup, &gsid, sizeof gsid);
3697 if (!NT_SUCCESS (status))
3698 debug_printf ("NtSetInformationToken (token, TokenPrimaryGroup), %y",
3699 status);
3701 cygheap->user.deimpersonate ();
3702 status = NtSetInformationToken (hProcToken, TokenPrimaryGroup,
3703 &gsid, sizeof gsid);
3704 if (!NT_SUCCESS (status))
3705 debug_printf ("NtSetInformationToken (hProcToken, TokenPrimaryGroup), %y",
3706 status);
3707 clear_procimptoken ();
3708 cygheap->user.reimpersonate ();
3709 return 0;
3712 /* setgid: POSIX 4.2.2.1 */
3713 extern "C" int
3714 setgid (gid_t gid)
3716 int ret = setegid (gid);
3717 if (!ret)
3718 cygheap->user.real_gid = myself->gid;
3719 return ret;
3722 extern "C" int
3723 setregid (gid_t rgid, gid_t egid)
3725 int ret = 0;
3726 bool tried = false;
3727 gid_t old_egid = myself->gid;
3729 if (rgid != ILLEGAL_GID && cygheap->user.real_gid != rgid && egid != rgid)
3730 tried = !(ret = setegid (rgid));
3731 if (!ret && egid != ILLEGAL_GID)
3732 ret = setegid (egid);
3733 if (tried && (ret || egid == ILLEGAL_GID) && setegid (old_egid))
3734 system_printf ("Cannot restore original egid %u", old_egid);
3735 if (!ret && rgid != ILLEGAL_GID)
3736 cygheap->user.real_gid = rgid;
3737 debug_printf ("real: %u, effective: %u", cygheap->user.real_gid, myself->gid);
3738 return ret;
3741 /* chroot: privileged Unix system call. */
3742 /* FIXME: Not privileged here. How should this be done? */
3743 extern "C" int
3744 chroot (const char *newroot)
3746 path_conv path (newroot, PC_SYM_FOLLOW | PC_POSIX);
3748 int ret = -1;
3749 if (path.error)
3750 set_errno (path.error);
3751 else if (!path.exists ())
3752 set_errno (ENOENT);
3753 else if (!path.isdir ())
3754 set_errno (ENOTDIR);
3755 else if (path.isspecial ())
3756 set_errno (EPERM);
3757 else
3759 getwinenv("PATH="); /* Save the native PATH */
3760 cygheap->root.set (path.get_posix (), path.get_win32 (),
3761 !!path.objcaseinsensitive ());
3762 ret = 0;
3765 syscall_printf ("%R = chroot(%s)", ret, newroot ?: "NULL");
3766 return ret;
3769 extern "C" int
3770 creat (const char *path, mode_t mode)
3772 return open (path, O_WRONLY | O_CREAT | O_TRUNC, mode);
3775 extern "C" void
3776 __assertfail ()
3778 exit (99);
3781 extern "C" int
3782 vhangup ()
3784 set_errno (ENOSYS);
3785 return -1;
3788 extern "C" int
3789 setpriority (int which, id_t who, int value)
3791 DWORD prio = nice_to_winprio (value);
3792 int error = 0;
3794 switch (which)
3796 case PRIO_PROCESS:
3797 if (!who)
3798 who = myself->pid;
3799 if ((pid_t) who == myself->pid)
3801 if (!SetPriorityClass (GetCurrentProcess (), prio))
3803 set_errno (EACCES);
3804 return -1;
3806 myself->nice = value;
3807 debug_printf ("Set nice to %d", myself->nice);
3808 return 0;
3810 break;
3811 case PRIO_PGRP:
3812 if (!who)
3813 who = myself->pgid;
3814 break;
3815 case PRIO_USER:
3816 if (!who)
3817 who = myself->uid;
3818 break;
3819 default:
3820 set_errno (EINVAL);
3821 return -1;
3823 winpids pids ((DWORD) PID_MAP_RW);
3824 for (DWORD i = 0; i < pids.npids; ++i)
3826 _pinfo *p = pids[i];
3827 if (p)
3829 switch (which)
3831 case PRIO_PROCESS:
3832 if ((pid_t) who != p->pid)
3833 continue;
3834 break;
3835 case PRIO_PGRP:
3836 if ((pid_t) who != p->pgid)
3837 continue;
3838 break;
3839 case PRIO_USER:
3840 if ((uid_t) who != p->uid)
3841 continue;
3842 break;
3844 HANDLE proc_h = OpenProcess (PROCESS_SET_INFORMATION, FALSE,
3845 p->dwProcessId);
3846 if (!proc_h)
3847 error = EPERM;
3848 else
3850 if (!SetPriorityClass (proc_h, prio))
3851 error = EACCES;
3852 else
3853 p->nice = value;
3854 CloseHandle (proc_h);
3858 pids.reset ();
3859 if (error)
3861 set_errno (error);
3862 return -1;
3864 return 0;
3867 extern "C" int
3868 getpriority (int which, id_t who)
3870 int nice = NZERO * 2; /* Illegal value */
3872 switch (which)
3874 case PRIO_PROCESS:
3875 if (!who)
3876 who = myself->pid;
3877 if ((pid_t) who == myself->pid)
3879 DWORD winprio = GetPriorityClass(GetCurrentProcess());
3880 if (winprio != nice_to_winprio(myself->nice))
3881 myself->nice = winprio_to_nice(winprio);
3882 return myself->nice;
3884 break;
3885 case PRIO_PGRP:
3886 if (!who)
3887 who = myself->pgid;
3888 break;
3889 case PRIO_USER:
3890 if (!who)
3891 who = myself->uid;
3892 break;
3893 default:
3894 set_errno (EINVAL);
3895 return -1;
3897 winpids pids ((DWORD) 0);
3898 for (DWORD i = 0; i < pids.npids; ++i)
3900 _pinfo *p = pids[i];
3901 if (p)
3902 switch (which)
3904 case PRIO_PROCESS:
3905 if ((pid_t) who == p->pid)
3907 nice = p->nice;
3908 goto out;
3910 break;
3911 case PRIO_PGRP:
3912 if ((pid_t) who == p->pgid && p->nice < nice)
3913 nice = p->nice;
3914 break;
3915 case PRIO_USER:
3916 if ((uid_t) who == p->uid && p->nice < nice)
3917 nice = p->nice;
3918 break;
3921 out:
3922 pids.reset ();
3923 if (nice == NZERO * 2)
3925 set_errno (ESRCH);
3926 return -1;
3928 return nice;
3931 extern "C" int
3932 nice (int incr)
3934 return setpriority (PRIO_PROCESS, myself->pid, myself->nice + incr);
3937 static void
3938 locked_append (int fd, const void * buf, size_t size)
3940 struct flock lock_buffer = {F_WRLCK, SEEK_SET, 0, 0, 0};
3941 int count = 0;
3944 if ((lock_buffer.l_start = lseek (fd, 0, SEEK_END)) != (off_t) -1
3945 && fcntl (fd, F_SETLKW, &lock_buffer) != -1)
3947 if (lseek (fd, 0, SEEK_END) != (off_t) -1)
3948 write (fd, buf, size);
3949 lock_buffer.l_type = F_UNLCK;
3950 fcntl (fd, F_SETLK, &lock_buffer);
3951 break;
3953 while (count++ < 1000
3954 && (errno == EACCES || errno == EAGAIN)
3955 && !usleep (1000));
3958 extern "C" void
3959 updwtmp (const char *wtmp_file, const struct utmp *ut)
3961 int fd;
3963 if ((fd = open (wtmp_file, O_WRONLY | O_BINARY, 0)) >= 0)
3965 locked_append (fd, ut, sizeof *ut);
3966 close (fd);
3970 static int utmp_fd = -1;
3971 static bool utmp_readonly = false;
3972 static char *utmp_file = (char *) _PATH_UTMP;
3974 static void
3975 internal_setutent (bool force_readwrite)
3977 if (force_readwrite && utmp_readonly)
3978 endutent ();
3979 if (utmp_fd < 0)
3981 utmp_fd = open (utmp_file, O_RDWR | O_BINARY);
3982 /* If open fails, we assume an unprivileged process (who?). In this
3983 case we try again for reading only unless the process calls
3984 pututline() (==force_readwrite) in which case opening just fails. */
3985 if (utmp_fd < 0 && !force_readwrite)
3987 utmp_fd = open (utmp_file, O_RDONLY | O_BINARY);
3988 if (utmp_fd >= 0)
3989 utmp_readonly = true;
3992 else
3993 lseek (utmp_fd, 0, SEEK_SET);
3996 extern "C" void
3997 setutent ()
3999 internal_setutent (false);
4002 extern "C" void
4003 endutent ()
4005 if (utmp_fd >= 0)
4007 close (utmp_fd);
4008 utmp_fd = -1;
4009 utmp_readonly = false;
4013 extern "C" int
4014 utmpname (const char *file)
4016 __try
4018 if (*file)
4020 endutent ();
4021 utmp_file = strdup (file);
4022 if (utmp_file)
4024 debug_printf ("New UTMP file: %s", utmp_file);
4025 return 0;
4029 __except (EFAULT) {}
4030 __endtry
4031 debug_printf ("Setting UTMP file failed");
4032 return -1;
4035 EXPORT_ALIAS (utmpname, utmpxname)
4037 /* Note: do not make NO_COPY */
4038 static struct utmp utmp_data_buf[16];
4039 static unsigned utix = 0;
4040 #define nutdbuf (sizeof (utmp_data_buf) / sizeof (utmp_data_buf[0]))
4041 #define utmp_data ({ \
4042 if (utix >= nutdbuf) \
4043 utix = 0; \
4044 utmp_data_buf + utix++; \
4047 static struct utmpx *
4048 copy_ut_to_utx (struct utmp *ut, struct utmpx *utx)
4050 if (!ut)
4051 return NULL;
4052 memcpy (utx, ut, sizeof *ut);
4053 utx->ut_tv.tv_sec = ut->ut_time;
4054 utx->ut_tv.tv_usec = 0;
4055 return utx;
4058 extern "C" struct utmp *
4059 getutent ()
4061 if (utmp_fd < 0)
4063 internal_setutent (false);
4064 if (utmp_fd < 0)
4065 return NULL;
4068 utmp *ut = utmp_data;
4069 if (read (utmp_fd, ut, sizeof *ut) != sizeof *ut)
4070 return NULL;
4071 return ut;
4074 extern "C" struct utmp *
4075 getutid (const struct utmp *id)
4077 __try
4079 if (utmp_fd < 0)
4081 internal_setutent (false);
4082 if (utmp_fd < 0)
4083 __leave;
4085 utmp *ut = utmp_data;
4086 while (read (utmp_fd, ut, sizeof *ut) == sizeof *ut)
4088 switch (id->ut_type)
4090 case RUN_LVL:
4091 case BOOT_TIME:
4092 case OLD_TIME:
4093 case NEW_TIME:
4094 if (id->ut_type == ut->ut_type)
4095 return ut;
4096 break;
4097 case INIT_PROCESS:
4098 case LOGIN_PROCESS:
4099 case USER_PROCESS:
4100 case DEAD_PROCESS:
4101 if (strncmp (id->ut_id, ut->ut_id, UT_IDLEN) == 0)
4102 return ut;
4103 break;
4104 default:
4105 break;
4109 __except (EFAULT) {}
4110 __endtry
4111 return NULL;
4114 extern "C" struct utmp *
4115 getutline (const struct utmp *line)
4117 __try
4119 if (utmp_fd < 0)
4121 internal_setutent (false);
4122 if (utmp_fd < 0)
4123 __leave;
4126 utmp *ut = utmp_data;
4127 while (read (utmp_fd, ut, sizeof *ut) == sizeof *ut)
4128 if ((ut->ut_type == LOGIN_PROCESS ||
4129 ut->ut_type == USER_PROCESS) &&
4130 !strncmp (ut->ut_line, line->ut_line, sizeof (ut->ut_line)))
4131 return ut;
4133 __except (EFAULT) {}
4134 __endtry
4135 return NULL;
4138 extern "C" struct utmp *
4139 pututline (const struct utmp *ut)
4141 __try
4143 internal_setutent (true);
4144 if (utmp_fd < 0)
4146 debug_printf ("error: utmp_fd %d", utmp_fd);
4147 __leave;
4149 debug_printf ("ut->ut_type %d, ut->ut_pid %d, ut->ut_line '%s', ut->ut_id '%s'\n",
4150 ut->ut_type, ut->ut_pid, ut->ut_line, ut->ut_id);
4151 debug_printf ("ut->ut_user '%s', ut->ut_host '%s'\n",
4152 ut->ut_user, ut->ut_host);
4154 struct utmp *u;
4155 if ((u = getutid (ut)))
4157 lseek (utmp_fd, -sizeof *ut, SEEK_CUR);
4158 write (utmp_fd, ut, sizeof *ut);
4160 else
4161 locked_append (utmp_fd, ut, sizeof *ut);
4162 /* The documentation says to return a pointer to this which implies that
4163 this has to be cast from a const. That doesn't seem right but the
4164 documentation seems pretty clear on this. */
4165 return (struct utmp *) ut;
4167 __except (EFAULT) {}
4168 __endtry
4169 return NULL;
4172 extern "C" void
4173 setutxent ()
4175 internal_setutent (false);
4178 extern "C" void
4179 endutxent ()
4181 endutent ();
4184 extern "C" struct utmpx *
4185 getutxent ()
4187 /* POSIX: Not required to be thread safe. */
4188 static struct utmpx utx;
4189 return copy_ut_to_utx (getutent (), &utx);
4192 extern "C" struct utmpx *
4193 getutxid (const struct utmpx *id)
4195 /* POSIX: Not required to be thread safe. */
4196 static struct utmpx utx;
4198 __try
4200 ((struct utmpx *)id)->ut_time = id->ut_tv.tv_sec;
4201 return copy_ut_to_utx (getutid ((struct utmp *) id), &utx);
4203 __except (EFAULT) {}
4204 __endtry
4205 return NULL;
4208 extern "C" struct utmpx *
4209 getutxline (const struct utmpx *line)
4211 /* POSIX: Not required to be thread safe. */
4212 static struct utmpx utx;
4214 __try
4216 ((struct utmpx *)line)->ut_time = line->ut_tv.tv_sec;
4217 return copy_ut_to_utx (getutline ((struct utmp *) line), &utx);
4219 __except (EFAULT) {}
4220 __endtry
4221 return NULL;
4224 extern "C" struct utmpx *
4225 pututxline (const struct utmpx *utmpx)
4227 /* POSIX: Not required to be thread safe. */
4228 static struct utmpx utx;
4230 __try
4232 ((struct utmpx *)utmpx)->ut_time = utmpx->ut_tv.tv_sec;
4233 return copy_ut_to_utx (pututline ((struct utmp *) utmpx), &utx);
4235 __except (EFAULT) {}
4236 __endtry
4237 return NULL;
4240 extern "C" void
4241 updwtmpx (const char *wtmpx_file, const struct utmpx *utmpx)
4243 ((struct utmpx *)utmpx)->ut_time = utmpx->ut_tv.tv_sec;
4244 updwtmp (wtmpx_file, (const struct utmp *) utmpx);
4247 extern "C" long
4248 gethostid (void)
4250 /* Fetch the globally unique MachineGuid value from
4251 HKLM/Software/Microsoft/Cryptography and hash it. */
4252 int32_t hostid = 0x40291372; /* Choose a nice start value */
4253 WCHAR wguid[38];
4255 reg_key key (HKEY_LOCAL_MACHINE, KEY_READ,
4256 L"SOFTWARE", L"Microsoft", L"Cryptography", NULL);
4257 key.get_string (L"MachineGuid", wguid, 38,
4258 L"00000000-0000-0000-0000-000000000000");
4259 /* SDBM hash */
4260 for (PWCHAR wp = wguid; *wp; ++wp)
4261 hostid = *wp + (hostid << 6) + (hostid << 16) - hostid;
4262 debug_printf ("hostid %08y from MachineGuid %W", hostid, wguid);
4263 return (int32_t) hostid; /* Avoid sign extension. */
4266 #define ETC_SHELLS "/etc/shells"
4267 static int shell_index;
4268 static FILE *shell_fp;
4270 extern "C" char *
4271 getusershell ()
4273 /* List of default shells if no /etc/shells exists, defined as on Linux.
4274 FIXME: SunOS has a far longer list, containing all shells which
4275 might be shipped with the OS. Should we do the same for the Cygwin
4276 distro, adding bash, tcsh, ksh, pdksh and zsh? */
4277 static const char *def_shells[] = {
4278 "/bin/sh",
4279 "/bin/csh",
4280 "/usr/bin/sh",
4281 "/usr/bin/csh",
4282 NULL
4284 static char buf[PATH_MAX];
4285 int ch, buf_idx;
4287 if (!shell_fp && !(shell_fp = fopen (ETC_SHELLS, "rt")))
4289 if (def_shells[shell_index])
4290 return strcpy (buf, def_shells[shell_index++]);
4291 return NULL;
4293 /* Skip white space characters. */
4294 while ((ch = getc (shell_fp)) != EOF && isspace (ch))
4296 /* Get each non-whitespace character as part of the shell path as long as
4297 it fits in buf. */
4298 for (buf_idx = 0;
4299 ch != EOF && !isspace (ch) && buf_idx < (PATH_MAX - 1);
4300 buf_idx++, ch = getc (shell_fp))
4301 buf[buf_idx] = ch;
4302 /* Skip any trailing non-whitespace character not fitting in buf. If the
4303 path is longer than PATH_MAX, it's invalid anyway. */
4304 while (ch != EOF && !isspace (ch))
4305 ch = getc (shell_fp);
4306 if (buf_idx)
4308 buf[buf_idx] = '\0';
4309 return buf;
4311 return NULL;
4314 extern "C" void
4315 setusershell ()
4317 if (shell_fp)
4318 fseek (shell_fp, 0L, SEEK_SET);
4319 shell_index = 0;
4322 extern "C" void
4323 endusershell ()
4325 if (shell_fp)
4327 fclose (shell_fp);
4328 shell_fp = NULL;
4330 shell_index = 0;
4333 extern "C" void
4334 flockfile (FILE *file)
4336 _flockfile (file);
4339 extern "C" int
4340 ftrylockfile (FILE *file)
4342 return _ftrylockfile (file);
4345 extern "C" void
4346 funlockfile (FILE *file)
4348 _funlockfile (file);
4351 extern "C" FILE *
4352 popen (const char *command, const char *in_type)
4354 const char *type = in_type;
4355 char fdopen_flags[3] = "\0\0";
4356 int pipe_flags = 0;
4358 #define rw fdopen_flags[0]
4359 #define bintext fdopen_flags[1]
4361 /* Sanity check. GLibc allows any order and any number of repetition,
4362 as long as the string doesn't contradict itself. We do the same here. */
4363 while (*type)
4365 if (*type == 'r' || *type == 'w')
4367 if (rw && rw != *type)
4368 break;
4369 rw = *type++;
4371 else if (*type == 'b' || *type == 't')
4373 if (bintext && bintext != *type)
4374 break;
4375 bintext = *type++;
4377 else if (*type == 'e')
4379 pipe_flags = O_CLOEXEC;
4380 ++type;
4382 else
4383 break;
4385 if ((rw != 'r' && rw != 'w') || (*type != '\0'))
4387 set_errno (EINVAL);
4388 return NULL;
4391 int fds[2];
4392 if (pipe2 (fds, pipe_flags) < 0)
4393 return NULL;
4395 int myix = rw == 'r' ? 0 : 1;
4397 lock_process now;
4398 FILE *fp = fdopen (fds[myix], fdopen_flags);
4399 if (fp)
4401 /* If fds are in the range of stdin/stdout/stderr, move them
4402 out of the way (possibly temporarily). Otherwise, spawn_guts
4403 will be confused. We do this here rather than adding logic to
4404 spawn_guts because spawn_guts is likely to be a more frequently
4405 used routine and having stdin/stdout/stderr closed and reassigned
4406 to pipe handles is an unlikely event. */
4407 int orig_fds[2] = {fds[0], fds[1]};
4408 for (int i = 0; i < 2; i++)
4409 if (fds[i] <= 2)
4411 cygheap_fdnew newfd(3);
4412 cygheap->fdtab.move_fd (fds[i], newfd);
4413 fds[i] = newfd;
4416 int myfd = fds[myix]; /* myfd - convenience variable for manipulation
4417 of the "parent" end of the pipe. */
4418 int stdchild = myix ^ 1; /* stdchild denotes the index into fd for the
4419 handle which will be redirected to
4420 stdin/stdout */
4421 int __std[2];
4422 __std[myix] = -1; /* -1 means don't pass this fd to the child
4423 process */
4424 __std[stdchild] = fds[stdchild]; /* Do pass this as the std handle */
4426 const char *argv[4] =
4428 "/bin/sh",
4429 "-c",
4430 command,
4431 NULL
4434 /* With 'e' flag given, we have to revert the close-on-exec on the child
4435 end of the pipe. Otherwise don't pass our end of the pipe to the
4436 child process. */
4437 if (pipe_flags & O_CLOEXEC)
4438 fcntl (__std[stdchild], F_SETFD, 0);
4439 else
4440 fcntl (myfd, F_SETFD, FD_CLOEXEC);
4442 /* Also don't pass the file handle currently associated with stdin/stdout
4443 to the child. This function may actually fail if the stdchild fd
4444 is closed. But that's ok. */
4445 int stdchild_state = fcntl (stdchild, F_GETFD, 0);
4446 fcntl (stdchild, F_SETFD, stdchild_state | FD_CLOEXEC);
4448 /* Start a shell process to run the given command without forking. */
4449 pid_t pid = ch_spawn.worker ("/bin/sh", argv, environ, _P_NOWAIT,
4450 __std[0], __std[1]);
4452 /* Reinstate the close-on-exec state */
4453 fcntl (stdchild, F_SETFD, stdchild_state);
4455 /* If pid >= 0 then spawn_guts succeeded. */
4456 if (pid >= 0)
4458 close (fds[stdchild]); /* Close the child end of the pipe. */
4459 /* Move the fd back to its original slot if it has been moved since
4460 we're always supposed to open the lowest numbered available fd
4461 and, if fds[mix] != orig_fds[myix] then orig_fds[myix] is
4462 presumably lower. */
4463 if (fds[myix] != orig_fds[myix])
4464 cygheap->fdtab.move_fd (fds[myix], myfd = orig_fds[myix]);
4465 fhandler_pipe *fh = (fhandler_pipe *) cygheap->fdtab[myfd];
4466 /* Flag that this handle is associated with popen. */
4467 fh->set_popen_pid (pid);
4468 return fp;
4472 /* If we reach here we've seen an error but the pipe handles are open.
4473 Close them and return NULL. */
4474 int save_errno = get_errno ();
4475 if (fp)
4477 /* Must fclose fp to avoid memory leak. */
4478 fclose (fp);
4479 close (fds[myix ^ 1]);
4481 else
4483 close (fds[0]);
4484 close (fds[1]);
4486 set_errno (save_errno);
4488 #undef rw
4489 #undef bintext
4491 return NULL;
4495 pclose (FILE *fp)
4497 fhandler_pipe *fh = (fhandler_pipe *) cygheap->fdtab[fileno(fp)];
4499 if (fh->get_device () != FH_PIPEW && fh->get_device () != FH_PIPER)
4501 set_errno (EBADF);
4502 return -1;
4505 int pid = fh->get_popen_pid ();
4506 if (!pid)
4508 set_errno (ECHILD);
4509 return -1;
4512 if (fclose (fp))
4513 return -1;
4515 int status;
4516 while (1)
4517 if (waitpid (pid, &status, 0) == pid)
4518 break;
4519 else if (get_errno () == EINTR)
4520 continue;
4521 else
4522 return -1;
4524 return status;
4527 /* Preliminary(?) implementation of the openat family of functions. */
4529 static int
4530 gen_full_path_at (char *path_ret, int dirfd, const char *pathname,
4531 int flags = 0)
4533 /* futimesat allows a NULL pathname. */
4534 if (!pathname && !(flags & _AT_NULL_PATHNAME_ALLOWED))
4536 set_errno (EFAULT);
4537 return -1;
4539 if (pathname && isabspath_strict (pathname))
4540 stpcpy (path_ret, pathname);
4541 else
4543 char *p;
4545 if (dirfd == AT_FDCWD)
4547 cwdstuff::acquire_read ();
4548 p = stpcpy (path_ret, cygheap->cwd.get_posix ());
4549 cwdstuff::release_read ();
4551 else
4553 cygheap_fdget cfd (dirfd);
4554 if (cfd < 0)
4555 return -1;
4556 if (!cfd->pc.isdir () && !(flags & AT_EMPTY_PATH))
4558 set_errno (ENOTDIR);
4559 return -1;
4561 p = stpcpy (path_ret, cfd->get_name ());
4563 if (pathname)
4565 if (!*pathname)
4567 if (flags & AT_EMPTY_PATH)
4568 return 0;
4569 set_errno (ENOENT);
4570 return -1;
4572 if (strlen (pathname) >= PATH_MAX)
4574 set_errno (ENAMETOOLONG);
4575 return -1;
4577 if (p[-1] != '/')
4578 *p++ = '/';
4579 stpcpy (p, pathname);
4582 return 0;
4585 extern "C" int
4586 openat (int dirfd, const char *pathname, int flags, ...)
4588 tmp_pathbuf tp;
4589 __try
4591 char *path = tp.c_get ();
4592 if (gen_full_path_at (path, dirfd, pathname))
4593 __leave;
4595 va_list ap;
4596 mode_t mode;
4598 va_start (ap, flags);
4599 mode = va_arg (ap, mode_t);
4600 va_end (ap);
4601 return open (path, flags, mode);
4603 __except (EFAULT) {}
4604 __endtry
4605 return -1;
4608 extern "C" int
4609 faccessat (int dirfd, const char *pathname, int mode, int flags)
4611 tmp_pathbuf tp;
4612 int res = -1;
4614 __try
4616 char *path = tp.c_get ();
4617 if (!gen_full_path_at (path, dirfd, pathname))
4619 if ((mode & ~(F_OK|R_OK|W_OK|X_OK))
4620 || (flags & ~(AT_SYMLINK_NOFOLLOW|AT_EACCESS)))
4621 set_errno (EINVAL);
4622 else
4624 fhandler_base *fh = build_fh_name (path,
4625 (flags & AT_SYMLINK_NOFOLLOW
4626 ? PC_SYM_NOFOLLOW
4627 : PC_SYM_FOLLOW)
4628 | PC_KEEP_HANDLE,
4629 stat_suffixes);
4630 if (fh)
4632 res = fh->fhaccess (mode, !!(flags & AT_EACCESS));
4633 delete fh;
4638 __except (EFAULT) {}
4639 __endtry
4640 debug_printf ("returning %d", res);
4641 return res;
4644 extern "C" int
4645 fchmodat (int dirfd, const char *pathname, mode_t mode, int flags)
4647 tmp_pathbuf tp;
4648 __try
4650 if (flags & ~AT_SYMLINK_NOFOLLOW)
4652 set_errno (EINVAL);
4653 __leave;
4655 char *path = tp.c_get ();
4656 if (gen_full_path_at (path, dirfd, pathname))
4657 __leave;
4658 if (flags & AT_SYMLINK_NOFOLLOW)
4660 /* BSD has lchmod, but Linux does not. POSIX says
4661 AT_SYMLINK_NOFOLLOW is allowed to fail on symlinks.
4662 Linux blindly fails even for non-symlinks, but we allow
4663 it to succeed. */
4664 path_conv pc (path, PC_SYM_NOFOLLOW, stat_suffixes);
4665 if (pc.issymlink ())
4667 set_errno (EOPNOTSUPP);
4668 __leave;
4671 return chmod (path, mode);
4673 __except (EFAULT) {}
4674 __endtry
4675 return -1;
4678 extern "C" int
4679 fchownat (int dirfd, const char *pathname, uid_t uid, gid_t gid, int flags)
4681 tmp_pathbuf tp;
4682 __try
4684 if (flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH))
4686 set_errno (EINVAL);
4687 __leave;
4689 char *path = tp.c_get ();
4690 int res = gen_full_path_at (path, dirfd, pathname, flags);
4691 if (res)
4692 __leave;
4693 if (!*pathname) /* Implies AT_EMPTY_PATH */
4695 /* If dirfd refers to a symlink (which was necessarily opened with
4696 O_PATH | O_NOFOLLOW), we must operate directly on that symlink. */
4697 flags = AT_SYMLINK_NOFOLLOW;
4699 return chown_worker (path, (flags & AT_SYMLINK_NOFOLLOW)
4700 ? PC_SYM_NOFOLLOW : PC_SYM_FOLLOW, uid, gid);
4702 __except (EFAULT) {}
4703 __endtry
4704 return -1;
4707 extern "C" int
4708 fstatat (int dirfd, const char *__restrict pathname, struct stat *__restrict st,
4709 int flags)
4711 tmp_pathbuf tp;
4712 __try
4714 if (flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH))
4716 set_errno (EINVAL);
4717 __leave;
4719 char *path = tp.c_get ();
4720 int res = gen_full_path_at (path, dirfd, pathname, flags);
4721 if (res)
4722 __leave;
4723 path_conv pc (path, ((flags & AT_SYMLINK_NOFOLLOW)
4724 ? PC_SYM_NOFOLLOW : PC_SYM_FOLLOW)
4725 | PC_POSIX | PC_KEEP_HANDLE, stat_suffixes);
4726 return stat_worker (pc, st);
4728 __except (EFAULT) {}
4729 __endtry
4730 return -1;
4733 extern int utimens_worker (path_conv &, const struct timespec *);
4735 extern "C" int
4736 utimensat (int dirfd, const char *pathname, const struct timespec *times,
4737 int flags)
4739 tmp_pathbuf tp;
4740 __try
4742 char *path = tp.c_get ();
4743 if (flags & ~AT_SYMLINK_NOFOLLOW)
4745 set_errno (EINVAL);
4746 __leave;
4748 if (gen_full_path_at (path, dirfd, pathname))
4749 __leave;
4750 path_conv win32 (path, PC_POSIX | ((flags & AT_SYMLINK_NOFOLLOW)
4751 ? PC_SYM_NOFOLLOW : PC_SYM_FOLLOW),
4752 stat_suffixes);
4753 return utimens_worker (win32, times);
4755 __except (EFAULT) {}
4756 __endtry
4757 return -1;
4760 extern "C" int
4761 futimesat (int dirfd, const char *pathname, const struct timeval times[2])
4763 tmp_pathbuf tp;
4764 __try
4766 char *path = tp.c_get ();
4767 if (gen_full_path_at (path, dirfd, pathname, _AT_NULL_PATHNAME_ALLOWED))
4768 __leave;
4769 return utimes (path, times);
4771 __except (EFAULT) {}
4772 __endtry
4773 return -1;
4776 extern "C" int
4777 linkat (int olddirfd, const char *oldpathname,
4778 int newdirfd, const char *newpathname,
4779 int flags)
4781 tmp_pathbuf tp;
4782 fhandler_base *fh = NULL;
4784 __try
4786 if (flags & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH))
4788 set_errno (EINVAL);
4789 __leave;
4791 char *oldpath = tp.c_get ();
4792 if ((flags & AT_EMPTY_PATH) && oldpathname && oldpathname[0] == '\0')
4794 /* Operate directly on olddirfd, which can be anything
4795 except a directory. */
4796 if (olddirfd == AT_FDCWD)
4798 set_errno (EPERM);
4799 __leave;
4801 cygheap_fdget cfd (olddirfd);
4802 if (cfd < 0)
4803 __leave;
4804 if (cfd->pc.isdir ())
4806 set_errno (EPERM);
4807 __leave;
4809 fh = cfd;
4810 flags = 0; /* In case AT_SYMLINK_FOLLOW was set. */
4812 else if (gen_full_path_at (oldpath, olddirfd, oldpathname))
4813 __leave;
4814 char *newpath = tp.c_get ();
4815 if (gen_full_path_at (newpath, newdirfd, newpathname))
4816 __leave;
4817 if (flags & AT_SYMLINK_FOLLOW)
4819 path_conv old_name (oldpath,
4820 PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_PROCFD | PC_POSIX,
4821 stat_suffixes);
4822 if (old_name.error)
4824 set_errno (old_name.error);
4825 __leave;
4827 strcpy (oldpath, old_name.get_posix ());
4829 if (fh)
4830 return fh->link (newpath);
4831 return link (oldpath, newpath);
4833 __except (EFAULT) {}
4834 __endtry
4835 return -1;
4838 extern "C" int
4839 mkdirat (int dirfd, const char *pathname, mode_t mode)
4841 tmp_pathbuf tp;
4842 __try
4844 char *path = tp.c_get ();
4845 if (gen_full_path_at (path, dirfd, pathname))
4846 __leave;
4847 return mkdir (path, mode);
4849 __except (EFAULT) {}
4850 __endtry
4851 return -1;
4854 extern "C" int
4855 mkfifoat (int dirfd, const char *pathname, mode_t mode)
4857 tmp_pathbuf tp;
4858 __try
4860 char *path = tp.c_get ();
4861 if (gen_full_path_at (path, dirfd, pathname))
4862 __leave;
4863 return mkfifo (path, mode);
4865 __except (EFAULT) {}
4866 __endtry
4867 return -1;
4870 extern "C" int
4871 mknodat (int dirfd, const char *pathname, mode_t mode, dev_t dev)
4873 tmp_pathbuf tp;
4874 __try
4876 char *path = tp.c_get ();
4877 if (gen_full_path_at (path, dirfd, pathname))
4878 __leave;
4879 return mknod (path, mode, dev);
4881 __except (EFAULT) {}
4882 __endtry
4883 return -1;
4886 extern "C" ssize_t
4887 readlinkat (int dirfd, const char *__restrict pathname, char *__restrict buf,
4888 size_t bufsize)
4890 tmp_pathbuf tp;
4891 __try
4893 char *path = tp.c_get ();
4894 int save_errno = errno;
4895 int res = gen_full_path_at (path, dirfd, pathname);
4896 if (res)
4898 if (errno != ENOENT && errno != ENOTDIR)
4899 __leave;
4900 /* pathname is an empty string. This is OK if dirfd refers
4901 to a symlink that was opened with O_PATH | O_NOFOLLOW.
4902 In this case, readlinkat operates on the symlink.
4903 Don't propagate errors from gen_full_path_at after this point. */
4904 errno = save_errno;
4905 cygheap_fdget cfd (dirfd);
4906 if (cfd < 0
4907 || (!(cfd->issymlink ()
4908 && cfd->get_flags () & O_PATH
4909 && cfd->get_flags () & O_NOFOLLOW)))
4911 set_errno (ENOENT);
4912 __leave;
4914 strcpy (path, cfd->get_name ());
4916 return readlink (path, buf, bufsize);
4918 __except (EFAULT) {}
4919 __endtry
4920 return -1;
4923 extern "C" int
4924 renameat2 (int olddirfd, const char *oldpathname,
4925 int newdirfd, const char *newpathname, unsigned int flags)
4927 tmp_pathbuf tp;
4928 __try
4930 char *oldpath = tp.c_get ();
4931 if (gen_full_path_at (oldpath, olddirfd, oldpathname))
4932 __leave;
4933 char *newpath = tp.c_get ();
4934 if (gen_full_path_at (newpath, newdirfd, newpathname))
4935 __leave;
4936 return rename2 (oldpath, newpath, flags);
4938 __except (EFAULT) {}
4939 __endtry
4940 return -1;
4943 extern "C" int
4944 renameat (int olddirfd, const char *oldpathname,
4945 int newdirfd, const char *newpathname)
4947 return renameat2 (olddirfd, oldpathname, newdirfd, newpathname, 0);
4950 extern "C" int
4951 scandirat (int dirfd, const char *pathname, struct dirent ***namelist,
4952 int (*select) (const struct dirent *),
4953 int (*compar) (const struct dirent **, const struct dirent **))
4955 tmp_pathbuf tp;
4956 __try
4958 char *path = tp.c_get ();
4959 if (gen_full_path_at (path, dirfd, pathname))
4960 __leave;
4961 return scandir (path, namelist, select, compar);
4963 __except (EFAULT) {}
4964 __endtry
4965 return -1;
4968 extern "C" int
4969 symlinkat (const char *oldpath, int newdirfd, const char *newpathname)
4971 tmp_pathbuf tp;
4972 __try
4974 char *newpath = tp.c_get ();
4975 if (gen_full_path_at (newpath, newdirfd, newpathname))
4976 __leave;
4977 return symlink (oldpath, newpath);
4979 __except (EFAULT) {}
4980 __endtry
4981 return -1;
4984 extern "C" int
4985 unlinkat (int dirfd, const char *pathname, int flags)
4987 tmp_pathbuf tp;
4988 __try
4990 if (flags & ~AT_REMOVEDIR)
4992 set_errno (EINVAL);
4993 __leave;
4995 char *path = tp.c_get ();
4996 if (gen_full_path_at (path, dirfd, pathname))
4997 __leave;
4998 return (flags & AT_REMOVEDIR) ? rmdir (path) : unlink (path);
5000 __except (EFAULT) {}
5001 __endtry
5002 return -1;
5005 static int
5006 pipe_worker (int filedes[2], unsigned int psize, int mode)
5008 fhandler_pipe *fhs[2];
5009 int res = fhandler_pipe::create (fhs, psize, mode);
5010 if (!res)
5012 cygheap_fdnew fdin;
5013 cygheap_fdnew fdout (fdin, false);
5014 char buf[sizeof ("pipe:[9223372036854775807]")];
5015 __small_sprintf (buf, "pipe:[%D]", fhs[0]->get_plain_ino ());
5016 fhs[0]->pc.set_posix (buf);
5017 __small_sprintf (buf, "pipe:[%D]", fhs[1]->get_plain_ino ());
5018 fhs[1]->pc.set_posix (buf);
5019 fdin = fhs[0];
5020 fdout = fhs[1];
5021 filedes[0] = fdin;
5022 filedes[1] = fdout;
5024 return res;
5027 /* MS compatible version of pipe. Hopefully nobody is using it... */
5028 extern "C" int
5029 _pipe (int filedes[2], unsigned int psize, int mode)
5031 int res = pipe_worker (filedes, psize, mode);
5032 int read, write;
5033 if (res != 0)
5034 read = write = -1;
5035 else
5037 read = filedes[0];
5038 write = filedes[1];
5040 syscall_printf ("%R = _pipe([%d, %d], %u, %y)", res, read, write, psize, mode);
5041 return res;
5044 extern "C" int
5045 pipe (int filedes[2])
5047 int res = pipe_worker (filedes, DEFAULT_PIPEBUFSIZE, O_BINARY);
5048 int read, write;
5049 if (res != 0)
5050 read = write = -1;
5051 else
5053 read = filedes[0];
5054 write = filedes[1];
5056 syscall_printf ("%R = pipe([%d, %d])", res, read, write);
5057 return res;
5060 extern "C" int
5061 pipe2 (int filedes[2], int mode)
5063 int res = pipe_worker (filedes, DEFAULT_PIPEBUFSIZE, mode);
5064 int read, write;
5065 if (res != 0)
5066 read = write = -1;
5067 else
5069 read = filedes[0];
5070 write = filedes[1];
5072 syscall_printf ("%R = pipe2([%d, %d], %y)", res, read, write, mode);
5073 return res;
5076 extern "C" FILE *
5077 tmpfile (void)
5079 char *dir = getenv ("TMPDIR");
5080 if (!dir)
5081 dir = P_tmpdir;
5082 int fd = open (dir, O_RDWR | O_BINARY | O_TMPFILE, S_IRUSR | S_IWUSR);
5083 if (fd < 0)
5084 return NULL;
5085 FILE *fp = fdopen (fd, "wb+");
5086 int e = errno;
5087 if (!fp)
5088 close (fd); // ..will remove tmp file
5089 set_errno (e);
5090 return fp;