Cygwin: cygtls: rename sig to current_sig
[newlib-cygwin.git] / winsup / cygwin / syscalls.cc
blob433739cda6e046e47c591614653abc09dd3f46c8
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_buf = (PFILE_NAMES_INFORMATION)
621 alloca (bufsiz);
622 PFILE_NAMES_INFORMATION pfni;
623 NTSTATUS status = NtQueryDirectoryFile (dir, NULL, NULL, 0, &io, pfni_buf,
624 bufsiz, FileNamesInformation,
625 FALSE, NULL, TRUE);
626 if (!NT_SUCCESS (status))
628 debug_printf ("Checking if directory %S is empty failed, status = %y",
629 pc.get_nt_native_path (), status);
630 return status;
632 int cnt = 1;
635 pfni = pfni_buf;
636 while (pfni->NextEntryOffset)
638 if (++cnt > 2)
640 UNICODE_STRING fname;
641 OBJECT_ATTRIBUTES attr;
642 FILE_BASIC_INFORMATION fbi;
644 pfni = (PFILE_NAMES_INFORMATION)
645 ((caddr_t) pfni + pfni->NextEntryOffset);
646 RtlInitCountedUnicodeString(&fname, pfni->FileName,
647 pfni->FileNameLength);
648 InitializeObjectAttributes (&attr, &fname, 0, dir, NULL);
649 status = NtQueryAttributesFile (&attr, &fbi);
650 /* Intensive testing shows that sometimes directories, for which
651 the delete disposition has already been set, and the deleting
652 handle is already closed, can linger in the parent dir for a
653 couple of ms for no apparent reason (Windows Defender or other
654 real-time scanners are suspect).
656 A fast rm -r is capable to exploit this problem. Setting the
657 delete disposition of the parent dir then fails with
658 STATUS_DIRECTORY_NOT_EMPTY. Examining the content of the
659 affected dir can then show either that the dir is empty, or it
660 can contain a lingering subdir. Calling NtQueryAttributesFile
661 on that subdir returns with STATUS_DELETE_PENDING, or it
662 disappeared before that call.
664 That's what we do here. If NtQueryAttributesFile succeeded,
665 or if the error code does not indicate an already deleted
666 entry, STATUS_DIRECTORY_NOT_EMPTY is returned.
668 Otherwise STATUS_SUCCESS is returned. Read on in unlink_nt. */
669 if (status != STATUS_DELETE_PENDING
670 && status != STATUS_OBJECT_NAME_NOT_FOUND
671 && status != STATUS_OBJECT_PATH_NOT_FOUND)
673 debug_printf ("Directory %S not empty, found file <%S>, "
674 "query status = %y",
675 pc.get_nt_native_path (), &fname, status);
676 return STATUS_DIRECTORY_NOT_EMPTY;
679 pfni = (PFILE_NAMES_INFORMATION) ((caddr_t) pfni + pfni->NextEntryOffset);
682 while (NT_SUCCESS (NtQueryDirectoryFile (dir, NULL, NULL, 0, &io, pfni_buf,
683 bufsiz, FileNamesInformation,
684 FALSE, NULL, FALSE)));
685 return STATUS_SUCCESS;
688 static inline NTSTATUS
689 _unlink_nt_post_dir_check (NTSTATUS status, POBJECT_ATTRIBUTES attr, const path_conv &pc)
691 /* Check for existence of remote dirs after trying to delete them.
692 Two reasons:
693 - Sometimes SMB indicates failure when it really succeeds.
694 - Removing a directory on a Samba drive using an old Samba version
695 sometimes doesn't return an error, if the directory can't be removed
696 because it's not empty. */
697 if (pc.isremote ())
699 FILE_BASIC_INFORMATION fbi;
700 NTSTATUS q_status;
702 q_status = NtQueryAttributesFile (attr, &fbi);
703 if (!NT_SUCCESS (status) && q_status == STATUS_OBJECT_NAME_NOT_FOUND)
704 status = STATUS_SUCCESS;
705 else if (pc.fs_is_samba ()
706 && NT_SUCCESS (status) && NT_SUCCESS (q_status))
707 status = STATUS_DIRECTORY_NOT_EMPTY;
709 return status;
712 NTSTATUS
713 unlink_nt (path_conv &pc, bool shareable)
715 NTSTATUS status;
716 HANDLE fh, fh_ro = NULL;
717 OBJECT_ATTRIBUTES attr;
718 IO_STATUS_BLOCK io;
719 ACCESS_MASK access = DELETE;
720 ULONG flags = FILE_OPEN_FOR_BACKUP_INTENT;
721 HANDLE old_trans = NULL, trans = NULL;
722 ULONG num_links = 1;
723 FILE_DISPOSITION_INFORMATION disp = { TRUE };
724 int reopened = 0;
725 bin_status bin_stat = dont_move;
727 syscall_printf ("Trying to delete %S, isdir = %d",
728 pc.get_nt_native_path (), pc.isdir ());
730 /* Add the reparse point flag to known reparse points, otherwise we remove
731 the target, not the reparse point. */
732 if (pc.is_known_reparse_point ())
733 flags |= FILE_OPEN_REPARSE_POINT;
735 pc.get_object_attr (attr, sec_none_nih);
737 /* First check if we can use POSIX unlink semantics: W10 1709+, local NTFS.
738 For the OPEN_BY_FILE_ID flag, see MINIMAL_WIN_NTFS_FLAGS comment in
739 fs_info::update. With POSIX unlink semantics the entire job gets MUCH
740 easier and faster. Just try to do it and if it fails, it fails. */
741 if (wincap.has_posix_unlink_semantics ()
742 && !pc.isremote () && pc.fs_is_ntfs ()
743 && pc.has_attribute (FILE_SUPPORTS_OPEN_BY_FILE_ID))
745 FILE_DISPOSITION_INFORMATION_EX fdie;
747 /* POSIX unlink semantics are nice, but they still fail if the file has
748 the R/O attribute set. If so, ignoring might be an option: W10 1809+
749 Removing the file is very much a safe bet afterwards, so, no
750 transaction. */
751 if ((pc.file_attributes () & FILE_ATTRIBUTE_READONLY)
752 && !wincap.has_posix_unlink_semantics_with_ignore_readonly ())
753 access |= FILE_WRITE_ATTRIBUTES;
754 status = NtOpenFile (&fh, access, &attr, &io, FILE_SHARE_VALID_FLAGS,
755 flags);
756 if (!NT_SUCCESS (status))
757 goto out;
758 if (access & FILE_WRITE_ATTRIBUTES)
760 status = NtSetAttributesFile (fh, pc.file_attributes ()
761 & ~FILE_ATTRIBUTE_READONLY);
762 if (!NT_SUCCESS (status))
764 NtClose (fh);
765 goto out;
768 fdie.Flags = FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS;
769 if (wincap.has_posix_unlink_semantics_with_ignore_readonly ())
770 fdie.Flags |= FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE;
771 status = NtSetInformationFile (fh, &io, &fdie, sizeof fdie,
772 FileDispositionInformationEx);
773 /* Restore R/O attribute in case we have multiple hardlinks. */
774 if (access & FILE_WRITE_ATTRIBUTES)
775 NtSetAttributesFile (fh, pc.file_attributes ());
776 NtClose (fh);
777 /* Trying to delete in-use executables and DLLs using
778 FILE_DISPOSITION_POSIX_SEMANTICS returns STATUS_CANNOT_DELETE.
779 Fall back to the default method. */
780 /* Additionaly that returns STATUS_INVALID_PARAMETER
781 on a bind mounted fs in hyper-v container. Falling back too. */
782 if (status != STATUS_CANNOT_DELETE
783 && status != STATUS_INVALID_PARAMETER)
785 debug_printf ("NtSetInformationFile returns %y "
786 "with posix semantics. Disable it and retry.", status);
787 goto out;
791 /* If the R/O attribute is set, we have to open the file with
792 FILE_WRITE_ATTRIBUTES to be able to remove this flags before trying
793 to delete it. We do this separately because there are filesystems
794 out there (MVFS), which refuse a request to open a file for DELETE
795 if the DOS R/O attribute is set for the file. After removing the R/O
796 attribute, just re-open the file for DELETE and go ahead. */
797 if (pc.file_attributes () & FILE_ATTRIBUTE_READONLY)
799 FILE_STANDARD_INFORMATION fsi;
801 /* If possible, hide the non-atomicity of the "remove R/O flag, remove
802 link to file" operation behind a transaction. */
803 if ((pc.fs_flags () & FILE_SUPPORTS_TRANSACTIONS))
804 start_transaction (old_trans, trans);
805 retry_open:
806 status = NtOpenFile (&fh_ro,
807 FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
808 &attr, &io, FILE_SHARE_VALID_FLAGS, flags);
809 if (NT_SUCCESS (status))
811 debug_printf ("Opening %S for removing R/O succeeded",
812 pc.get_nt_native_path ());
813 NTSTATUS status2 = NtSetAttributesFile (fh_ro,
814 pc.file_attributes ()
815 & ~FILE_ATTRIBUTE_READONLY);
816 if (!NT_SUCCESS (status2))
817 debug_printf ("Removing R/O on %S failed, status = %y",
818 pc.get_nt_native_path (), status2);
819 pc.init_reopen_attr (attr, fh_ro);
821 else
823 debug_printf ("Opening %S for removing R/O failed, status = %y",
824 pc.get_nt_native_path (), status);
825 if (NT_TRANSACTIONAL_ERROR (status) && trans)
827 /* If NtOpenFile fails due to transactional problems, stop
828 transaction and go ahead without. */
829 stop_transaction (status, old_trans, trans);
830 debug_printf ("Transaction failure. Retry open.");
831 goto retry_open;
834 if (pc.is_lnk_symlink ())
836 status = NtQueryInformationFile (fh_ro, &io, &fsi, sizeof fsi,
837 FileStandardInformation);
838 if (NT_SUCCESS (status))
839 num_links = fsi.NumberOfLinks;
841 access |= FILE_WRITE_ATTRIBUTES;
843 /* First try to open the file with only allowing sharing for delete. If
844 the file has an open handle on it, other than just for deletion, this
845 will fail. That indicates that the file has to be moved to the recycle
846 bin so that it actually disappears from its directory even though its
847 in use. Otherwise, if opening doesn't fail, the file is not in use and
848 we can go straight to setting the delete disposition flag.
849 However, while we have the file open with FILE_SHARE_DELETE, using
850 this file via another hardlink for anything other than DELETE by
851 concurrent processes fails. The 'shareable' argument is to prevent this.
853 NOTE: The missing sharing modes FILE_SHARE_READ and FILE_SHARE_WRITE do
854 NOT result in a STATUS_SHARING_VIOLATION, if another handle is
855 opened for reading/writing metadata only. In other words, if
856 another handle is open, but does not have the file open with
857 FILE_READ_DATA or FILE_WRITE_DATA, the following NtOpenFile call
858 will succeed. So, apparently there is no reliable way to find out
859 if a file is already open elsewhere for other purposes than
860 reading and writing data. */
861 if (shareable)
862 status = STATUS_SHARING_VIOLATION;
863 else
864 status = NtOpenFile (&fh, access, &attr, &io, FILE_SHARE_DELETE, flags);
865 /* STATUS_SHARING_VIOLATION is what we expect. STATUS_LOCK_NOT_GRANTED can
866 be generated under not quite clear circumstances when trying to open a
867 file on NFS with FILE_SHARE_DELETE only. This has been observed with
868 SFU 3.5 if the NFS share has been mounted under a drive letter. It's
869 not generated for all files, but only for some. If it's generated once
870 for a file, it will be generated all the time. It looks as if wrong file
871 state information is stored within the NFS client which never times out.
872 Opening the file with FILE_SHARE_VALID_FLAGS will work, though, and it
873 is then possible to delete the file quite normally. */
874 if (status == STATUS_SHARING_VIOLATION || status == STATUS_LOCK_NOT_GRANTED)
876 debug_printf ("Sharing violation when opening %S",
877 pc.get_nt_native_path ());
878 /* We never call try_to_bin on NetApp. Netapp filesystems don't
879 understand the "move and delete" method at all and have all kinds
880 of weird effects. Just setting the delete dispositon usually
881 works fine, though.
883 NFS implements its own mechanism to remove in-use files, which looks
884 quite similar to what we do in try_to_bin for remote files. However,
885 apparently it doesn't work as desired in all cases. This has been
886 observed when running the gawk 4.1.62++ testcase "testext.awk" under
887 Windows 10. So for NFS we still call try_to_bin to rename the file,
888 at least to make room for subsequent creation of a file with the
889 same filename. */
890 if (!pc.fs_is_netapp ())
891 bin_stat = move_to_bin;
892 /* If the file is not a directory, of if we didn't set the move_to_bin
893 flag, just proceed with the FILE_SHARE_VALID_FLAGS set. */
894 if (!pc.isdir () || bin_stat == dont_move)
895 status = NtOpenFile (&fh, access, &attr, &io,
896 FILE_SHARE_VALID_FLAGS, flags);
897 else
899 /* Otherwise it's getting tricky. The directory is opened in some
900 process, so we're supposed to move it to the recycler and mark it
901 for deletion. But what if the directory is not empty? The move
902 will work, but the subsequent delete will fail. So we would
903 have to move it back. While we do that in try_to_bin, it's bad,
904 because the move results in a temporary inconsistent state.
905 So, we test first if the directory is empty. If not, we bail
906 out with STATUS_DIRECTORY_NOT_EMPTY. This avoids most of the
907 problems. */
908 status = NtOpenFile (&fh, access | FILE_LIST_DIRECTORY | SYNCHRONIZE,
909 &attr, &io, FILE_SHARE_VALID_FLAGS,
910 flags | FILE_SYNCHRONOUS_IO_NONALERT);
911 if (NT_SUCCESS (status))
913 status = check_dir_not_empty (fh, pc);
914 if (!NT_SUCCESS (status))
916 NtClose (fh);
917 if (fh_ro)
918 NtClose (fh_ro);
919 goto out;
924 if (fh_ro)
925 NtClose (fh_ro);
926 if (!NT_SUCCESS (status))
928 if (status == STATUS_DELETE_PENDING)
930 debug_printf ("Delete %S already pending", pc.get_nt_native_path ());
931 status = STATUS_SUCCESS;
932 goto out;
934 debug_printf ("Opening %S for delete failed, status = %y",
935 pc.get_nt_native_path (), status);
936 goto out;
938 /* Try to move to bin if a sharing violation occured. If that worked,
939 we're done. */
940 if (bin_stat == move_to_bin
941 && (bin_stat = try_to_bin (pc, fh, access, flags)) >= has_been_moved)
943 if (bin_stat == has_been_moved)
944 status = STATUS_SUCCESS;
945 else
947 status = STATUS_DIRECTORY_NOT_EMPTY;
948 NtClose (fh);
950 goto out;
953 try_again:
954 /* Try to set delete disposition. */
955 status = NtSetInformationFile (fh, &io, &disp, sizeof disp,
956 FileDispositionInformation);
957 if (!NT_SUCCESS (status))
959 debug_printf ("Setting delete disposition on %S failed, status = %y",
960 pc.get_nt_native_path (), status);
961 if (status == STATUS_DIRECTORY_NOT_EMPTY)
963 NTSTATUS status2 = STATUS_SUCCESS;
965 if (!reopened)
967 /* Have to close and reopen the file from scratch, otherwise
968 we collide with the delete-only sharing mode. */
969 pc.get_object_attr (attr, sec_none_nih);
970 NtClose (fh);
971 status2 = NtOpenFile (&fh, access | FILE_LIST_DIRECTORY
972 | SYNCHRONIZE,
973 &attr, &io, FILE_SHARE_VALID_FLAGS,
974 flags | FILE_SYNCHRONOUS_IO_NONALERT);
976 if (NT_SUCCESS (status2) && reopened < 20)
978 /* Workaround rm -r problem:
980 Sometimes a deleted directory lingers in its parent dir
981 after the deleting handle has already been closed. This
982 can break deleting the parent dir. See the comment in
983 check_dir_not_empty for more information.
985 What we do here is this: If check_dir_not_empty returns
986 STATUS_SUCCESS, the dir is either empty, or only inhabited
987 by already deleted entries. If so, we try to move the dir
988 into the bin. This usually works.
990 However, if we're on a filesystem which doesn't support
991 the try_to_bin method, or if moving to the bin doesn't work
992 for some reason, just try to delete the directory again,
993 with a very short grace period to free the CPU for a while.
994 This gives the OS time to clean up. 5ms is enough in my
995 testing to make sure that we don't have to try more than
996 once in practically all cases.
997 While this is an extrem bordercase, we don't want to hang
998 infinitely in case a file in the directory is in the "delete
999 pending" state but an application holds an open handle to it
1000 for a longer time. So we don't try this more than 20 times,
1001 which means a process time of 100-120ms. */
1002 if (check_dir_not_empty (fh, pc) == STATUS_SUCCESS)
1004 if (bin_stat == dont_move)
1006 bin_stat = move_to_bin;
1007 if (!pc.fs_is_nfs () && !pc.fs_is_netapp ())
1009 debug_printf ("Try-to-bin %S",
1010 pc.get_nt_native_path ());
1011 bin_stat = try_to_bin (pc, fh, access, flags);
1014 /* Do NOT handle bin_stat == dir_not_empty here! */
1015 if (bin_stat == has_been_moved)
1016 status = STATUS_SUCCESS;
1017 else
1019 debug_printf ("Try %S again", pc.get_nt_native_path ());
1020 ++reopened;
1021 Sleep (5L);
1022 goto try_again;
1026 else if (status2 != STATUS_OBJECT_PATH_NOT_FOUND
1027 && status2 != STATUS_OBJECT_NAME_NOT_FOUND)
1029 fh = NULL;
1030 debug_printf ("Opening dir %S for check_dir_not_empty failed, "
1031 "status = %y", pc.get_nt_native_path (), status2);
1033 else /* Directory disappeared between NtClose and NtOpenFile. */
1034 status = STATUS_SUCCESS;
1036 /* Trying to delete a hardlink to a file in use by the system in some
1037 way (for instance, font files) by setting the delete disposition fails
1038 with STATUS_CANNOT_DELETE. Strange enough, deleting these hardlinks
1039 using delete-on-close semantic works... most of the time.
1041 Don't use delete-on-close on remote shares. If two processes
1042 have open handles on a file and one of them calls unlink, the
1043 file is removed from the remote share even though the other
1044 process still has an open handle. That process than gets Win32
1045 error 59, ERROR_UNEXP_NET_ERR when trying to access the file.
1046 Microsoft KB 837665 describes this problem as a bug in 2K3, but
1047 I have reproduced it on other systems. */
1048 else if (status == STATUS_CANNOT_DELETE
1049 && (!pc.isremote () || pc.fs_is_ncfsd ()))
1051 HANDLE fh2;
1053 debug_printf ("Cannot delete %S, try delete-on-close",
1054 pc.get_nt_native_path ());
1055 /* Re-open from handle so we open the correct file no matter if it
1056 has been moved to the bin or not. */
1057 status = NtOpenFile (&fh2, DELETE,
1058 pc.init_reopen_attr (attr, fh), &io,
1059 bin_stat == move_to_bin ? FILE_SHARE_VALID_FLAGS
1060 : FILE_SHARE_DELETE,
1061 flags | FILE_DELETE_ON_CLOSE);
1062 if (!NT_SUCCESS (status))
1064 debug_printf ("Setting delete-on-close on %S failed, status = %y",
1065 pc.get_nt_native_path (), status);
1066 /* This is really the last chance. If it hasn't been moved
1067 to the bin already, try it now. If moving to the bin
1068 succeeds, we got rid of the file in some way, even if
1069 unlinking didn't work. */
1070 if (bin_stat == dont_move)
1071 bin_stat = try_to_bin (pc, fh, access, flags);
1072 if (bin_stat >= has_been_moved)
1073 status = bin_stat == has_been_moved
1074 ? STATUS_SUCCESS
1075 : STATUS_DIRECTORY_NOT_EMPTY;
1077 else
1078 NtClose (fh2);
1081 if (fh)
1083 if (access & FILE_WRITE_ATTRIBUTES)
1085 /* Restore R/O attribute if setting the delete disposition failed. */
1086 if (!NT_SUCCESS (status))
1087 NtSetAttributesFile (fh, pc.file_attributes ());
1088 /* If we succeeded, restore R/O attribute to accommodate hardlinks.
1089 Only ever try to do this for our own winsymlinks, because there's
1090 a problem with setting the delete disposition:
1091 http://msdn.microsoft.com/en-us/library/ff545765%28VS.85%29.aspx
1092 "Subsequently, the only legal operation by such a caller is
1093 to close the open file handle."
1095 FIXME? We could use FILE_HARD_LINK_INFORMATION to find all
1096 hardlinks and use one of them to restore the R/O bit, after the
1097 NtClose, but before we stop the transaction. This avoids the
1098 aforementioned problem entirely . */
1099 else if (pc.is_lnk_symlink () && num_links > 1)
1100 NtSetAttributesFile (fh, pc.file_attributes ());
1103 NtClose (fh);
1106 out:
1107 /* Stop transaction if we started one. */
1108 if (trans)
1109 stop_transaction (status, old_trans, trans);
1111 if (pc.isdir ())
1112 status = _unlink_nt_post_dir_check (status, &attr, pc);
1114 syscall_printf ("%S, return status = %y", pc.get_nt_native_path (), status);
1115 return status;
1118 extern "C" int
1119 unlink (const char *ourname)
1121 int res = -1;
1122 dev_t devn;
1123 NTSTATUS status;
1125 path_conv win32_name (ourname, PC_SYM_NOFOLLOW, stat_suffixes);
1127 if (win32_name.error)
1129 set_errno (win32_name.error);
1130 goto done;
1133 devn = win32_name.get_device ();
1134 if (isproc_dev (devn))
1136 set_errno (EROFS);
1137 goto done;
1139 if (isdevfd_dev (devn) || (win32_name.isdevice () && !win32_name.issocket ()))
1141 set_errno (EPERM);
1142 goto done;
1144 if (!win32_name.exists ())
1146 debug_printf ("unlinking a nonexistent file");
1147 set_errno (ENOENT);
1148 goto done;
1150 else if (win32_name.isdir ())
1152 debug_printf ("unlinking a directory");
1153 set_errno (EISDIR);
1154 goto done;
1157 status = unlink_nt (win32_name, false);
1158 if (NT_SUCCESS (status))
1159 res = 0;
1160 else
1161 __seterrno_from_nt_status (status);
1163 done:
1164 syscall_printf ("%R = unlink(%s)", res, ourname);
1165 return res;
1168 extern "C" int
1169 _remove_r (struct _reent *, const char *ourname)
1171 path_conv win32_name (ourname, PC_SYM_NOFOLLOW);
1173 if (win32_name.error)
1175 set_errno (win32_name.error);
1176 syscall_printf ("%R = remove(%s)",-1, ourname);
1177 return -1;
1180 int res = win32_name.isdir () ? rmdir (ourname) : unlink (ourname);
1181 syscall_printf ("%R = remove(%s)", res, ourname);
1182 return res;
1185 extern "C" int
1186 remove (const char *ourname)
1188 return _remove_r (_REENT, ourname);
1191 extern "C" pid_t
1192 getpid ()
1194 syscall_printf ("%d = getpid()", myself->pid);
1195 return myself->pid;
1198 extern "C" pid_t
1199 _getpid_r (struct _reent *)
1201 return getpid ();
1204 /* getppid: POSIX 4.1.1.1 */
1205 extern "C" pid_t
1206 getppid ()
1208 syscall_printf ("%d = getppid()", myself->ppid);
1209 return myself->ppid;
1212 /* setsid: POSIX 4.3.2.1 */
1213 extern "C" pid_t
1214 setsid (void)
1216 if (myself->pgid == myself->pid)
1217 syscall_printf ("hmm. pgid %d pid %d", myself->pgid, myself->pid);
1218 else
1220 myself->ctty = CTTY_RELEASED;
1221 myself->sid = myself->pid;
1222 myself->pgid = myself->pid;
1223 if (cygheap->ctty)
1224 cygheap->close_ctty ();
1225 syscall_printf ("sid %d, pgid %d, %s", myself->sid, myself->pgid, myctty ());
1226 return myself->sid;
1229 set_errno (EPERM);
1230 return -1;
1233 extern "C" pid_t
1234 getsid (pid_t pid)
1236 pid_t res;
1237 if (!pid)
1238 res = myself->sid;
1239 else
1241 pinfo p (pid);
1242 if (p)
1243 res = p->sid;
1244 else
1246 set_errno (ESRCH);
1247 res = -1;
1250 syscall_printf ("%R = getsid(%d)", pid);
1251 return res;
1254 extern "C" ssize_t
1255 read (int fd, void *ptr, size_t len)
1257 size_t res = (size_t) -1;
1259 pthread_testcancel ();
1261 __try
1263 cygheap_fdget cfd (fd);
1264 if (cfd < 0)
1265 __leave;
1267 if ((cfd->get_flags () & O_PATH)
1268 || (cfd->get_flags () & O_ACCMODE) == O_WRONLY)
1270 set_errno (EBADF);
1271 __leave;
1274 /* Could block, so let user know we at least got here. */
1275 syscall_printf ("read(%d, %p, %d) %sblocking",
1276 fd, ptr, len, cfd->is_nonblocking () ? "non" : "");
1278 cfd->read (ptr, len);
1279 res = len;
1281 __except (EFAULT) {}
1282 __endtry
1283 syscall_printf ("%lR = read(%d, %p, %d)", res, fd, ptr, len);
1284 return (ssize_t) res;
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 extern "C" ssize_t
1384 writev (const int fd, const struct iovec *const iov, const int iovcnt)
1386 ssize_t res = -1;
1388 pthread_testcancel ();
1390 __try
1392 const ssize_t tot = check_iovec_for_write (iov, iovcnt);
1394 cygheap_fdget cfd (fd);
1395 if (cfd < 0)
1396 __leave;
1398 if (tot <= 0)
1400 res = tot;
1401 __leave;
1404 if ((cfd->get_flags () & O_PATH)
1405 || (cfd->get_flags () & O_ACCMODE) == O_RDONLY)
1407 set_errno (EBADF);
1408 __leave;
1411 /* Could block, so let user know we at least got here. */
1412 if (fd == 1 || fd == 2)
1413 paranoid_printf ("writev(%d, %p, %d)", fd, iov, iovcnt);
1414 else
1415 syscall_printf ("writev(%d, %p, %d)", fd, iov, iovcnt);
1417 res = cfd->writev (iov, iovcnt, tot);
1419 __except (EFAULT) {}
1420 __endtry
1421 if (fd == 1 || fd == 2)
1422 paranoid_printf ("%lR = writev(%d, %p, %d)", res, fd, iov, iovcnt);
1423 else
1424 syscall_printf ("%lR = writev(%d, %p, %d)", res, fd, iov, iovcnt);
1425 return res;
1428 extern "C" ssize_t
1429 pwrite (int fd, const void *ptr, size_t len, off_t off)
1431 pthread_testcancel ();
1433 ssize_t res;
1434 cygheap_fdget cfd (fd);
1435 if (cfd < 0)
1436 res = -1;
1437 else if (cfd->get_flags () & O_PATH)
1439 set_errno (EBADF);
1440 res = -1;
1442 else
1443 res = cfd->pwrite (const_cast<void *> (ptr), len, off);
1445 syscall_printf ("%lR = pwrite(%d, %p, %d, %d)", res, fd, ptr, len, off);
1446 return res;
1449 /* _open */
1450 /* newlib's fcntl.h defines _open as taking variable args so we must
1451 correspond. The third arg if it exists is: mode_t mode. */
1452 extern "C" int
1453 open (const char *unix_path, int flags, ...)
1455 int res = -1;
1456 va_list ap;
1457 mode_t mode = 0;
1458 fhandler_base *fh = NULL;
1459 fhandler_base *fh_file = NULL;
1461 pthread_testcancel ();
1463 __try
1465 syscall_printf ("open(%s, %y)", unix_path, flags);
1466 if (!*unix_path)
1468 set_errno (ENOENT);
1469 __leave;
1472 /* check for optional mode argument */
1473 va_start (ap, flags);
1474 mode = va_arg (ap, mode_t);
1475 va_end (ap);
1477 cygheap_fdnew fd;
1479 if (fd < 0)
1480 __leave; /* errno already set */
1482 /* When O_PATH is specified in flags, flag bits other than O_CLOEXEC,
1483 O_DIRECTORY, and O_NOFOLLOW are ignored. */
1484 if (flags & O_PATH)
1485 flags &= (O_PATH | O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW);
1487 int opt = PC_OPEN | PC_SYM_NOFOLLOW_PROCFD;
1488 opt |= (flags & (O_NOFOLLOW | O_EXCL)) ? PC_SYM_NOFOLLOW
1489 : PC_SYM_FOLLOW;
1491 /* If we're opening a FIFO, we will call device_access_denied
1492 below. This leads to a call to fstat, which can use the
1493 path_conv handle. */
1494 opt |= PC_KEEP_HANDLE;
1495 if (!(fh = build_fh_name (unix_path, opt, stat_suffixes)))
1496 __leave; /* errno already set */
1497 opt &= ~PC_KEEP_HANDLE;
1498 if (!fh->isfifo ())
1499 fh->pc.close_conv_handle ();
1500 if ((flags & O_NOFOLLOW) && fh->issymlink () && !(flags & O_PATH))
1502 set_errno (ELOOP);
1503 __leave;
1505 if ((flags & O_DIRECTORY) && fh->exists () && !fh->pc.isdir ())
1507 set_errno (ENOTDIR);
1508 __leave;
1510 if (((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) && fh->exists ())
1512 set_errno (EEXIST);
1513 __leave;
1515 if (flags & O_TMPFILE)
1517 if ((flags & O_ACCMODE) != O_WRONLY && (flags & O_ACCMODE) != O_RDWR)
1519 set_errno (EINVAL);
1520 __leave;
1522 if (!fh->pc.isdir ())
1524 set_errno (fh->exists () ? ENOTDIR : ENOENT);
1525 __leave;
1527 /* Unfortunately Windows does not allow to create a nameless file.
1528 So create unique filename instead. It starts with ".cyg_tmp_",
1529 followed by an 8 byte unique hex number, followed by an 8 byte
1530 random hex number. */
1531 int64_t rnd;
1532 char *new_path;
1534 new_path = (char *) malloc (strlen (fh->get_name ())
1535 + 1 /* slash */
1536 + 10 /* prefix */
1537 + 16 /* 64 bit unique id as hex*/
1538 + 16 /* 64 bit random number as hex */
1539 + 1 /* trailing NUL */);
1540 if (!new_path)
1541 __leave;
1542 fh->set_unique_id ();
1543 RtlGenRandom (&rnd, sizeof rnd);
1544 __small_sprintf (new_path, "%s/%s%016X%016X",
1545 fh->get_name (), ".cyg_tmp_",
1546 fh->get_unique_id (), rnd);
1548 if (!(fh_file = build_fh_name (new_path, opt, NULL)))
1550 free (new_path);
1551 __leave; /* errno already set */
1553 delete fh;
1554 fh = fh_file;
1557 if (fh->dev () == FH_PROCESSFD && fh->pc.follow_fd_symlink ())
1559 /* Reopen file by descriptor */
1560 fh_file = fh->fd_reopen (flags, mode & 07777);
1561 if (!fh_file)
1562 __leave;
1563 delete fh;
1564 fh = fh_file;
1566 else
1568 if (fh->is_fs_special ())
1570 if (fh->device_access_denied (flags))
1571 __leave; /* errno already set */
1572 else if (fh->isfifo ())
1573 fh->pc.close_conv_handle ();
1575 if (!fh->open_with_arch (flags, mode & 07777))
1576 __leave; /* errno already set */
1578 /* Move O_TMPFILEs to the bin to avoid blocking the parent dir. */
1579 if ((flags & O_TMPFILE) && !fh->pc.isremote ())
1580 try_to_bin (fh->pc, fh->get_handle (), DELETE,
1581 FILE_OPEN_FOR_BACKUP_INTENT);
1582 fd = fh;
1583 if (fd <= 2)
1584 set_std_handle (fd);
1585 res = fd;
1587 __except (EFAULT) {}
1588 __endtry
1589 if (res < 0 && fh)
1590 delete fh;
1591 syscall_printf ("%R = open(%s, %y)", res, unix_path, flags);
1592 return res;
1595 static int
1596 posix_getdents_lseek (cygheap_fdget &cfd, off_t pos, int dir)
1598 long cur = cfd->telldir (cfd->getdents_dir ());
1599 long abs_pos;
1601 switch (dir)
1603 case SEEK_CUR:
1604 abs_pos = cur + pos;
1605 break;
1606 case SEEK_SET:
1607 case SEEK_DATA:
1608 abs_pos = pos;
1609 break;
1610 case SEEK_END:
1611 case SEEK_HOLE:
1612 /* First read full dir to learn end-of-dir position. */
1613 while (::readdir (cfd->getdents_dir ()))
1615 long eod = cfd->telldir (cfd->getdents_dir ());
1616 /* Seek back so it looks like nothing happend in error case */
1617 cfd->seekdir (cfd->getdents_dir (), cur);
1618 if (dir == SEEK_HOLE)
1620 if (pos > eod)
1622 set_errno (ENXIO);
1623 return -1;
1625 abs_pos = eod;
1627 else
1628 abs_pos = eod + pos;
1629 break;
1631 if (abs_pos < 0)
1633 set_errno (EINVAL);
1634 return -1;
1636 if (abs_pos != cur)
1638 cfd->seekdir (cfd->getdents_dir (), abs_pos);
1639 /* In SEEK_DATA case, check that we didn't seek beyond EOF */
1640 if (dir == SEEK_DATA || dir == SEEK_HOLE)
1642 pos = cfd->telldir (cfd->getdents_dir ());
1643 if (pos < abs_pos)
1645 /* Seek back so it looks like nothing happend */
1646 cfd->seekdir (cfd->getdents_dir (), cur);
1647 set_errno (ENXIO);
1648 return -1;
1652 return abs_pos;
1655 extern "C" off_t
1656 lseek (int fd, off_t pos, int dir)
1658 off_t res;
1660 if (dir < SEEK_SET || dir > SEEK_HOLE)
1662 set_errno (EINVAL);
1663 res = -1;
1665 else
1667 cygheap_fdget cfd (fd);
1668 if (cfd < 0)
1669 res = -1;
1670 else if (cfd->getdents_dir ())
1671 res = posix_getdents_lseek (cfd, pos, dir);
1672 else
1673 res = cfd->lseek (pos, dir);
1675 /* Can't use %R/%lR here since res is always 8 bytes */
1676 syscall_printf (res == -1 ? "%D = lseek(%d, %D, %d), errno %d"
1677 : "%D = lseek(%d, %D, %d)",
1678 res, fd, pos, dir, get_errno ());
1680 return res;
1683 extern "C" int
1684 close (int fd)
1686 int res;
1688 syscall_printf ("close(%d)", fd);
1690 pthread_testcancel ();
1692 cygheap_fdget cfd (fd, true);
1693 if (cfd < 0)
1694 res = -1;
1695 else
1697 cfd->isclosed (true);
1698 res = cfd->close_with_arch ();
1699 cfd.release ();
1702 syscall_printf ("%R = close(%d)", res, fd);
1703 return res;
1706 extern "C" int
1707 isatty (int fd)
1709 int res;
1711 cygheap_fdget cfd (fd);
1712 if (cfd < 0)
1713 res = 0;
1714 else
1715 res = cfd->is_tty ();
1716 syscall_printf ("%R = isatty(%d)", res, fd);
1717 return res;
1720 extern "C" int
1721 link (const char *oldpath, const char *newpath)
1723 int res = -1;
1724 fhandler_base *fh;
1726 if (!(fh = build_fh_name (oldpath, PC_SYM_NOFOLLOW | PC_KEEP_HANDLE,
1727 stat_suffixes)))
1728 goto error;
1730 if (fh->error ())
1732 debug_printf ("got %d error from build_fh_name", fh->error ());
1733 set_errno (fh->error ());
1735 else if (fh->pc.isdir ())
1736 set_errno (EPERM); /* We do not permit linking directories. */
1737 else if (!fh->pc.exists ())
1738 set_errno (ENOENT);
1739 else
1740 res = fh->link (newpath);
1742 delete fh;
1743 error:
1744 syscall_printf ("%R = link(%s, %s)", res, oldpath, newpath);
1745 return res;
1748 /* chown: POSIX 5.6.5.1 */
1750 * chown () is only implemented for Windows NT. Under other operating
1751 * systems, it is only a stub that always returns zero.
1753 static int
1754 chown_worker (const char *name, unsigned fmode, uid_t uid, gid_t gid)
1756 int res = -1;
1757 fhandler_base *fh;
1759 if (!(fh = build_fh_name (name, fmode, stat_suffixes)))
1760 goto error;
1762 if (fh->error ())
1764 debug_printf ("got %d error from build_fh_name", fh->error ());
1765 set_errno (fh->error ());
1767 else
1768 res = fh->fchown (uid, gid);
1770 delete fh;
1771 error:
1772 syscall_printf ("%R = %schown(%s,...)",
1773 res, (fmode & PC_SYM_NOFOLLOW) ? "l" : "", name);
1774 return res;
1777 extern "C" int
1778 chown (const char * name, uid_t uid, gid_t gid)
1780 return chown_worker (name, PC_SYM_FOLLOW, uid, gid);
1783 extern "C" int
1784 lchown (const char * name, uid_t uid, gid_t gid)
1786 return chown_worker (name, PC_SYM_NOFOLLOW, uid, gid);
1789 extern "C" int
1790 fchown (int fd, uid_t uid, gid_t gid)
1792 cygheap_fdget cfd (fd);
1793 if (cfd < 0)
1795 syscall_printf ("-1 = fchown (%d,...)", fd);
1796 return -1;
1798 else if (cfd->get_flags () & O_PATH)
1800 set_errno (EBADF);
1801 return -1;
1804 int res = cfd->fchown (uid, gid);
1806 syscall_printf ("%R = fchown(%s,...)", res, cfd->get_name ());
1807 return res;
1810 /* umask: POSIX 5.3.3.1 */
1811 extern "C" mode_t
1812 umask (mode_t mask)
1814 mode_t oldmask;
1816 oldmask = cygheap->umask;
1817 cygheap->umask = mask & 0777;
1818 return oldmask;
1821 #define FILTERED_MODE(m) ((m) & (S_ISUID | S_ISGID | S_ISVTX \
1822 | S_IRWXU | S_IRWXG | S_IRWXO))
1825 chmod_device (path_conv& pc, mode_t mode)
1827 return mknod_worker (pc, (pc.dev.mode () & S_IFMT) | FILTERED_MODE (mode),
1828 pc.dev.get_major (), pc.dev.get_minor ());
1831 /* chmod: POSIX 5.6.4.1 */
1832 extern "C" int
1833 chmod (const char *path, mode_t mode)
1835 int res = -1;
1836 fhandler_base *fh;
1837 if (!(fh = build_fh_name (path, PC_SYM_FOLLOW, stat_suffixes)))
1838 goto error;
1840 if (fh->error ())
1842 debug_printf ("got %d error from build_fh_name", fh->error ());
1843 set_errno (fh->error ());
1845 else
1846 res = fh->fchmod (FILTERED_MODE (mode));
1848 delete fh;
1849 error:
1850 syscall_printf ("%R = chmod(%s, 0%o)", res, path, mode);
1851 return res;
1854 /* fchmod: P96 5.6.4.1 */
1856 extern "C" int
1857 fchmod (int fd, mode_t mode)
1859 cygheap_fdget cfd (fd);
1860 if (cfd < 0)
1862 syscall_printf ("-1 = fchmod (%d, 0%o)", fd, mode);
1863 return -1;
1865 else if (cfd->get_flags () & O_PATH)
1867 set_errno (EBADF);
1868 return -1;
1871 return cfd->fchmod (FILTERED_MODE (mode));
1874 static struct stat dev_st;
1875 static bool dev_st_inited;
1877 void
1878 fhandler_base::stat_fixup (struct stat *buf)
1880 /* For devices, set inode number to device number. This gives us a valid,
1881 unique inode number without having to call hash_path_name. /dev/tty needs
1882 a bit of persuasion to get the same st_ino value in stat and fstat. */
1883 if (!buf->st_ino)
1885 if (get_major () == DEV_VIRTFS_MAJOR)
1886 buf->st_ino = get_ino ();
1887 else if (dev () == FH_TTY ||
1888 ((get_major () == DEV_PTYS_MAJOR
1889 || get_major () == DEV_CONS_MAJOR)
1890 && !strcmp (get_name (), "/dev/tty")))
1891 buf->st_ino = FH_TTY;
1892 else
1893 buf->st_ino = get_device ();
1895 /* For /dev-based devices, st_dev must be set to the device number of /dev,
1896 not it's own device major/minor numbers. What we do here to speed up
1897 the process is to fetch the device number of /dev only once, liberally
1898 assuming that /dev doesn't change over the lifetime of a process. */
1899 if (!buf->st_dev)
1901 if (dev ().is_dev_resident ())
1903 if (!dev_st_inited)
1905 stat ("/dev", &dev_st);
1906 dev_st_inited = true;
1908 buf->st_dev = dev_st.st_dev;
1910 else
1911 buf->st_dev = get_device ();
1913 /* Only set st_rdev if it's a device. */
1914 if (!buf->st_rdev && get_major () != DEV_VIRTFS_MAJOR)
1916 buf->st_rdev = get_device ();
1917 /* consX, console, conin, and conout point to the same device.
1918 Make sure the link count is correct. */
1919 if (buf->st_rdev == (dev_t) myself->ctty && iscons_dev (myself->ctty))
1920 buf->st_nlink = 4;
1921 /* CD-ROM drives have two links, /dev/srX and /dev/scdX. */
1922 else if (gnu_dev_major (buf->st_rdev) == DEV_CDROM_MAJOR)
1923 buf->st_nlink = 2;
1927 extern "C" int
1928 fstat (int fd, struct stat *buf)
1930 int res;
1932 cygheap_fdget cfd (fd);
1933 if (cfd < 0)
1934 res = -1;
1935 else
1937 memset (buf, 0, sizeof (struct stat));
1938 res = cfd->fstat (buf);
1939 if (!res)
1940 cfd->stat_fixup (buf);
1943 syscall_printf ("%R = fstat(%d, %p)", res, fd, buf);
1944 return res;
1947 extern "C" int
1948 _fstat_r (struct _reent *ptr, int fd, struct stat *buf)
1950 int ret;
1952 if ((ret = fstat (fd, buf)) == -1)
1953 _REENT_ERRNO(ptr) = get_errno ();
1954 return ret;
1957 /* fsync: P96 6.6.1.1 */
1958 extern "C" int
1959 fsync (int fd)
1961 pthread_testcancel ();
1962 cygheap_fdget cfd (fd);
1963 if (cfd < 0)
1965 syscall_printf ("-1 = fsync (%d)", fd);
1966 return -1;
1968 return cfd->fsync ();
1971 static void
1972 sync_worker (HANDLE dir, USHORT len, LPCWSTR vol)
1974 NTSTATUS status;
1975 HANDLE fh;
1976 IO_STATUS_BLOCK io;
1977 OBJECT_ATTRIBUTES attr;
1978 UNICODE_STRING uvol = { len, len, (WCHAR *) vol };
1980 InitializeObjectAttributes (&attr, &uvol, OBJ_CASE_INSENSITIVE, dir, NULL);
1981 status = NtOpenFile (&fh, GENERIC_WRITE, &attr, &io,
1982 FILE_SHARE_VALID_FLAGS, 0);
1983 if (!NT_SUCCESS (status))
1984 debug_printf ("NtOpenFile (%S), status %y", &uvol, status);
1985 else
1987 status = NtFlushBuffersFile (fh, &io);
1988 if (!NT_SUCCESS (status))
1989 debug_printf ("NtFlushBuffersFile (%S), status %y", &uvol, status);
1990 NtClose (fh);
1994 /* sync: SUSv3 */
1995 extern "C" void
1996 sync ()
1998 OBJECT_ATTRIBUTES attr;
1999 NTSTATUS status;
2000 HANDLE devhdl;
2001 UNICODE_STRING device;
2003 /* Open \Device object directory. */
2004 RtlInitUnicodeString (&device, L"\\Device");
2005 InitializeObjectAttributes (&attr, &device, OBJ_CASE_INSENSITIVE, NULL, NULL);
2006 status = NtOpenDirectoryObject (&devhdl, DIRECTORY_QUERY, &attr);
2007 if (!NT_SUCCESS (status))
2009 debug_printf ("NtOpenDirectoryObject, status %y", status);
2010 return;
2012 /* Traverse \Device directory ... */
2013 tmp_pathbuf tp;
2014 PDIRECTORY_BASIC_INFORMATION dbi_buf = (PDIRECTORY_BASIC_INFORMATION)
2015 tp.w_get ();
2016 BOOLEAN restart = TRUE;
2017 bool last_run = false;
2018 ULONG context = 0;
2019 while (!last_run)
2021 status = NtQueryDirectoryObject (devhdl, dbi_buf, 65536, FALSE, restart,
2022 &context, NULL);
2023 if (!NT_SUCCESS (status))
2025 debug_printf ("NtQueryDirectoryObject, status %y", status);
2026 break;
2028 if (status != STATUS_MORE_ENTRIES)
2029 last_run = true;
2030 restart = FALSE;
2031 for (PDIRECTORY_BASIC_INFORMATION dbi = dbi_buf;
2032 dbi->ObjectName.Length > 0;
2033 dbi++)
2035 /* ... and call sync_worker for each HarddiskVolumeX entry. */
2036 if (dbi->ObjectName.Length >= 15 * sizeof (WCHAR)
2037 && !wcsncasecmp (dbi->ObjectName.Buffer, L"HarddiskVolume", 14)
2038 && iswdigit (dbi->ObjectName.Buffer[14]))
2039 sync_worker (devhdl, dbi->ObjectName.Length,
2040 dbi->ObjectName.Buffer);
2043 NtClose (devhdl);
2046 /* Cygwin internal */
2048 stat_worker (path_conv &pc, struct stat *buf)
2050 int res = -1;
2052 __try
2054 if (pc.error)
2056 debug_printf ("got %d error from path_conv", pc.error);
2057 set_errno (pc.error);
2059 else if (pc.exists ())
2061 fhandler_base *fh;
2063 memset (buf, 0, sizeof (*buf));
2065 if (!(fh = build_fh_pc (pc)))
2066 __leave;
2068 debug_printf ("(%S, %p, %p), file_attributes %d",
2069 pc.get_nt_native_path (), buf, fh, (DWORD) *fh);
2071 res = fh->fstat (buf);
2072 if (!res)
2073 fh->stat_fixup (buf);
2074 delete fh;
2076 else
2077 set_errno (ENOENT);
2079 __except (EFAULT) {}
2080 __endtry
2081 syscall_printf ("%d = (%S,%p)", res, pc.get_nt_native_path (), buf);
2082 return res;
2085 extern "C" int
2086 stat (const char *__restrict name, struct stat *__restrict buf)
2088 syscall_printf ("entering");
2089 path_conv pc (name, PC_SYM_FOLLOW | PC_POSIX | PC_KEEP_HANDLE
2090 | PC_SYM_NOFOLLOW_PROCFD,
2091 stat_suffixes);
2092 return stat_worker (pc, buf);
2095 extern "C" int
2096 _stat_r (struct _reent *__restrict ptr, const char *__restrict name,
2097 struct stat *buf)
2099 int ret;
2101 if ((ret = stat (name, buf)) == -1)
2102 _REENT_ERRNO(ptr) = get_errno ();
2103 return ret;
2106 /* lstat: Provided by SVR4 and 4.3+BSD, POSIX? */
2107 extern "C" int
2108 lstat (const char *__restrict name, struct stat *__restrict buf)
2110 syscall_printf ("entering");
2111 path_conv pc (name, PC_SYM_NOFOLLOW | PC_POSIX | PC_KEEP_HANDLE,
2112 stat_suffixes);
2113 return stat_worker (pc, buf);
2116 extern "C" int
2117 access (const char *fn, int flags)
2119 // flags were incorrectly specified
2120 int res = -1;
2121 if (flags & ~(F_OK|R_OK|W_OK|X_OK))
2122 set_errno (EINVAL);
2123 else
2125 fhandler_base *fh = build_fh_name (fn, PC_SYM_FOLLOW | PC_KEEP_HANDLE,
2126 stat_suffixes);
2127 if (fh)
2129 res = fh->fhaccess (flags, false);
2130 delete fh;
2133 debug_printf ("returning %d", res);
2134 return res;
2137 /* Linux provides this extension; it is basically a wrapper around the
2138 POSIX:2008 faccessat (AT_FDCWD, fn, flags, AT_EACCESS). We also
2139 provide eaccess as an alias for this, in cygwin.din. */
2140 extern "C" int
2141 euidaccess (const char *fn, int flags)
2143 // flags were incorrectly specified
2144 int res = -1;
2145 if (flags & ~(F_OK|R_OK|W_OK|X_OK))
2146 set_errno (EINVAL);
2147 else
2149 fhandler_base *fh = build_fh_name (fn, PC_SYM_FOLLOW | PC_KEEP_HANDLE,
2150 stat_suffixes);
2151 if (fh)
2153 res = fh->fhaccess (flags, true);
2154 delete fh;
2157 debug_printf ("returning %d", res);
2158 return res;
2161 static void
2162 rename_append_suffix (path_conv &pc, const char *path, size_t len,
2163 const char *suffix)
2165 char buf[len + 5];
2167 if (ascii_strcasematch (path + len - 4, ".lnk")
2168 || ascii_strcasematch (path + len - 4, ".exe"))
2169 len -= 4;
2170 stpcpy (stpncpy (buf, path, len), suffix);
2171 pc.check (buf, PC_SYM_NOFOLLOW);
2174 /* This function tests if a filename has one of the "approved" executable
2175 suffix. This list is probably not complete... */
2176 static inline bool
2177 nt_path_has_executable_suffix (PUNICODE_STRING upath)
2179 static const PUNICODE_STRING blessed_executable_suffixes[] =
2181 &ro_u_exe,
2182 &ro_u_scr,
2183 &ro_u_sys,
2184 NULL
2187 USHORT pos = upath->Length / sizeof (WCHAR);
2188 PWCHAR path;
2189 UNICODE_STRING usuf;
2190 const PUNICODE_STRING *suf;
2192 /* Too short for a native path? */
2193 if (pos < 8)
2194 return false;
2195 /* Assumption: All executable suffixes have a length of three. */
2196 path = upath->Buffer + pos - 4;
2197 if (*path != L'.')
2198 return false;
2199 RtlInitCountedUnicodeString (&usuf, path, 4 * sizeof (WCHAR));
2200 for (suf = blessed_executable_suffixes; *suf; ++suf)
2201 if (RtlEqualUnicodeString (&usuf, *suf, TRUE))
2202 return true;
2203 return false;
2206 inline int
2207 set_same_file_return (bool noreplace)
2209 if (!noreplace)
2210 return 0;
2211 set_errno (EEXIST);
2212 return -1;
2214 /* If newpath names an existing file and the RENAME_NOREPLACE flag is
2215 specified, fail with EEXIST. Exception: Don't fail if the purpose
2216 of the rename is just to change the case of oldpath on a
2217 case-insensitive file system. */
2218 static int
2219 rename2 (const char *oldpath, const char *newpath, unsigned int at2flags)
2221 tmp_pathbuf tp;
2222 int res = -1;
2223 path_conv oldpc, newpc, new2pc, *dstpc, *removepc = NULL;
2224 bool old_dir_requested = false, new_dir_requested = false;
2225 bool old_explicit_suffix = false, new_explicit_suffix = false;
2226 bool use_posix_semantics;
2227 bool noreplace = at2flags & RENAME_NOREPLACE;
2228 size_t olen, nlen;
2229 bool equal_path;
2230 NTSTATUS status = STATUS_SUCCESS;
2231 HANDLE fh = NULL, nfh;
2232 HANDLE old_trans = NULL, trans = NULL;
2233 OBJECT_ATTRIBUTES attr;
2234 IO_STATUS_BLOCK io;
2235 FILE_STANDARD_INFORMATION ofsi;
2236 PFILE_RENAME_INFORMATION pfri;
2238 __try
2240 if (at2flags & ~RENAME_NOREPLACE)
2241 /* RENAME_NOREPLACE is the only flag currently supported. */
2243 set_errno (EINVAL);
2244 __leave;
2246 if (!*oldpath || !*newpath)
2248 /* Reject rename("","x"), rename("x",""). */
2249 set_errno (ENOENT);
2250 __leave;
2252 if (has_dot_last_component (oldpath, true))
2254 /* Reject rename("dir/.","x"). */
2255 oldpc.check (oldpath, PC_SYM_NOFOLLOW, stat_suffixes);
2256 set_errno (oldpc.isdir () ? EINVAL : ENOTDIR);
2257 __leave;
2259 if (has_dot_last_component (newpath, true))
2261 /* Reject rename("dir","x/."). */
2262 newpc.check (newpath, PC_SYM_NOFOLLOW, stat_suffixes);
2263 set_errno (!newpc.exists () ? ENOENT
2264 : newpc.isdir () ? EINVAL : ENOTDIR);
2265 __leave;
2268 /* A trailing slash requires that the pathname points to an existing
2269 directory. If it's not, it's a ENOTDIR condition. The same goes
2270 for newpath a bit further down this function. */
2271 olen = strlen (oldpath);
2272 if (isdirsep (oldpath[olen - 1]))
2274 char *buf;
2275 char *p = stpcpy (buf = tp.c_get (), oldpath) - 1;
2276 oldpath = buf;
2277 while (p >= oldpath && isdirsep (*p))
2278 *p-- = '\0';
2279 olen = p + 1 - oldpath;
2280 if (!olen)
2282 /* The root directory cannot be renamed. This also rejects
2283 the corner case of rename("/","/"), even though it is the
2284 same file. */
2285 set_errno (EINVAL);
2286 __leave;
2288 old_dir_requested = true;
2290 oldpc.check (oldpath, PC_SYM_NOFOLLOW, stat_suffixes);
2291 if (oldpc.error)
2293 set_errno (oldpc.error);
2294 __leave;
2296 if (!oldpc.exists ())
2298 set_errno (ENOENT);
2299 __leave;
2301 if (oldpc.isspecial () && !oldpc.issocket () && !oldpc.is_fs_special ())
2303 /* No renames from virtual FS */
2304 set_errno (EROFS);
2305 __leave;
2307 if (oldpc.has_attribute (FILE_ATTRIBUTE_REPARSE_POINT)
2308 && !oldpc.issymlink ())
2310 /* Volume mount point. If we try to rename a volume mount point, NT
2311 returns STATUS_NOT_SAME_DEVICE ==> Win32 ERROR_NOT_SAME_DEVICE ==>
2312 errno EXDEV. That's bad since mv(1) will now perform a
2313 cross-device move. So what we do here is to treat the volume
2314 mount point just like Linux treats a mount point. */
2315 set_errno (EBUSY);
2316 __leave;
2318 if (old_dir_requested && !oldpc.isdir ())
2320 /* Reject rename("file/","x"). */
2321 set_errno (ENOTDIR);
2322 __leave;
2324 if (oldpc.known_suffix ()
2325 && (ascii_strcasematch (oldpath + olen - 4, ".lnk")
2326 || ascii_strcasematch (oldpath + olen - 4, ".exe")))
2327 old_explicit_suffix = true;
2329 nlen = strlen (newpath);
2330 if (isdirsep (newpath[nlen - 1]))
2332 char *buf;
2333 char *p = stpcpy (buf = tp.c_get (), newpath) - 1;
2334 newpath = buf;
2335 while (p >= newpath && isdirsep (*p))
2336 *p-- = '\0';
2337 nlen = p + 1 - newpath;
2338 if (!nlen) /* The root directory is never empty. */
2340 set_errno (ENOTEMPTY);
2341 __leave;
2343 new_dir_requested = true;
2345 newpc.check (newpath, PC_SYM_NOFOLLOW, stat_suffixes);
2346 if (newpc.error)
2348 set_errno (newpc.error);
2349 __leave;
2351 if (newpc.isspecial () && !newpc.issocket ())
2353 /* No renames to virtual FSes */
2354 set_errno (EROFS);
2355 __leave;
2357 if (new_dir_requested && !(newpc.exists ()
2358 ? newpc.isdir () : oldpc.isdir ()))
2360 /* Reject rename("file1","file2/"), but allow rename("dir","d/"). */
2361 set_errno (newpc.exists () ? ENOTDIR : ENOENT);
2362 __leave;
2364 if (newpc.exists ()
2365 && (oldpc.isdir () ? !newpc.isdir () : newpc.isdir ()))
2367 /* Reject rename("file","dir") and rename("dir","file"). */
2368 set_errno (newpc.isdir () ? EISDIR : ENOTDIR);
2369 __leave;
2371 if (newpc.known_suffix ()
2372 && (ascii_strcasematch (newpath + nlen - 4, ".lnk")
2373 || ascii_strcasematch (newpath + nlen - 4, ".exe")))
2374 new_explicit_suffix = true;
2376 /* This test is necessary in almost every case, so do it once here. */
2377 equal_path = RtlEqualUnicodeString (oldpc.get_nt_native_path (),
2378 newpc.get_nt_native_path (),
2379 oldpc.objcaseinsensitive ());
2381 /* First check if oldpath and newpath only differ by case. If so, it's
2382 just a request to change the case of the filename. By simply setting
2383 the file attributes to INVALID_FILE_ATTRIBUTES (which translates to
2384 "file doesn't exist"), all later tests are skipped. */
2385 if (oldpc.objcaseinsensitive () && newpc.exists () && equal_path
2386 && old_explicit_suffix == new_explicit_suffix)
2388 if (RtlEqualUnicodeString (oldpc.get_nt_native_path (),
2389 newpc.get_nt_native_path (),
2390 FALSE))
2392 res = set_same_file_return (noreplace);
2393 __leave;
2395 newpc.file_attributes (INVALID_FILE_ATTRIBUTES);
2397 else if (oldpc.isdir ())
2399 /* Check for newpath being identical or a subdir of oldpath. */
2400 if (RtlPrefixUnicodeString (oldpc.get_nt_native_path (),
2401 newpc.get_nt_native_path (),
2402 oldpc.objcaseinsensitive ()))
2404 if (newpc.get_nt_native_path ()->Length
2405 == oldpc.get_nt_native_path ()->Length)
2407 res = set_same_file_return (noreplace);
2408 __leave;
2410 if (*(PWCHAR) ((PBYTE) newpc.get_nt_native_path ()->Buffer
2411 + oldpc.get_nt_native_path ()->Length) == L'\\')
2413 set_errno (EINVAL);
2414 __leave;
2418 else if (!newpc.exists ())
2420 if (equal_path && old_explicit_suffix != new_explicit_suffix)
2422 newpc.check (newpath, PC_SYM_NOFOLLOW);
2423 if (RtlEqualUnicodeString (oldpc.get_nt_native_path (),
2424 newpc.get_nt_native_path (),
2425 oldpc.objcaseinsensitive ()))
2427 res = set_same_file_return (noreplace);
2428 __leave;
2431 else if (oldpc.is_lnk_special ()
2432 && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
2433 &ro_u_lnk, TRUE))
2434 rename_append_suffix (newpc, newpath, nlen, ".lnk");
2435 else if (oldpc.is_binary () && !old_explicit_suffix
2436 && oldpc.known_suffix ()
2437 && !nt_path_has_executable_suffix
2438 (newpc.get_nt_native_path ()))
2439 /* Never append .exe suffix if oldpath had .exe suffix given
2440 explicitely, or if oldpath wasn't already a .exe file, or
2441 if the destination filename has one of the blessed executable
2442 suffixes.
2443 Note: To rename an executable foo.exe to bar-without-suffix,
2444 the .exe suffix must be given explicitly in oldpath. */
2445 rename_append_suffix (newpc, newpath, nlen, ".exe");
2447 else
2449 if (equal_path && old_explicit_suffix != new_explicit_suffix)
2451 newpc.check (newpath, PC_SYM_NOFOLLOW);
2452 if (RtlEqualUnicodeString (oldpc.get_nt_native_path (),
2453 newpc.get_nt_native_path (),
2454 oldpc.objcaseinsensitive ()))
2456 res = set_same_file_return (noreplace);
2457 __leave;
2460 else if (oldpc.is_lnk_special ())
2462 if (!newpc.is_lnk_special ()
2463 && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
2464 &ro_u_lnk, TRUE))
2466 rename_append_suffix (new2pc, newpath, nlen, ".lnk");
2467 removepc = &newpc;
2470 else if (oldpc.is_binary ())
2472 /* Never append .exe suffix if oldpath had .exe suffix given
2473 explicitely, or if newfile is a binary (in which case the given
2474 name probably makes sense as it is), or if the destination
2475 filename has one of the blessed executable suffixes. */
2476 if (!old_explicit_suffix && oldpc.known_suffix ()
2477 && !newpc.is_binary ()
2478 && !nt_path_has_executable_suffix
2479 (newpc.get_nt_native_path ()))
2481 rename_append_suffix (new2pc, newpath, nlen, ".exe");
2482 removepc = &newpc;
2485 else
2487 /* If the new path is an existing .lnk symlink or a .exe file,
2488 but the new path has not been specified with explicit suffix,
2489 rename to the new name without suffix, as expected, but also
2490 remove the clashing symlink or executable. Did I ever mention
2491 how I hate the file suffix idea? */
2492 if ((newpc.is_lnk_special ()
2493 || RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
2494 &ro_u_exe, TRUE))
2495 && !new_explicit_suffix)
2497 new2pc.check (newpath, PC_SYM_NOFOLLOW, stat_suffixes);
2498 newpc.get_nt_native_path ()->Length -= 4 * sizeof (WCHAR);
2499 if (new2pc.is_binary () || new2pc.is_lnk_special ())
2500 removepc = &new2pc;
2504 dstpc = (removepc == &newpc) ? &new2pc : &newpc;
2506 /* Check cross-device before touching anything. Otherwise we might end
2507 up with an unlinked target dir even if the actual rename didn't work.*/
2508 if (oldpc.fs_type () != dstpc->fs_type ()
2509 || oldpc.fs_serial_number () != dstpc->fs_serial_number ())
2511 set_errno (EXDEV);
2512 __leave;
2515 /* Should we replace an existing file? */
2516 if ((removepc || dstpc->exists ()) && noreplace)
2518 set_errno (EEXIST);
2519 __leave;
2522 /* POSIX semantics only on local NTFS drives. For the OPEN_BY_FILE_ID
2523 flag, see MINIMAL_WIN_NTFS_FLAGS comment in fs_info::update. */
2524 use_posix_semantics = wincap.has_posix_rename_semantics ()
2525 && !oldpc.isremote ()
2526 && oldpc.fs_is_ntfs ()
2527 && oldpc.has_attribute (FILE_SUPPORTS_OPEN_BY_FILE_ID);
2529 ignore_posix_semantics_retry:
2530 /* Opening the file must be part of the transaction. It's not sufficient
2531 to call only NtSetInformationFile under the transaction. Therefore we
2532 have to start the transaction here, if necessary. Don't start
2533 transaction on W10 1709 or later on local NTFS. Use POSIX semantics
2534 instead. */
2535 if (!use_posix_semantics
2536 && (dstpc->fs_flags () & FILE_SUPPORTS_TRANSACTIONS)
2537 && (dstpc->isdir ()
2538 || (!removepc && dstpc->has_attribute (FILE_ATTRIBUTE_READONLY))))
2539 start_transaction (old_trans, trans);
2541 int retry_count;
2542 retry_count = 0;
2543 retry:
2544 /* Talking about inconsistent behaviour...
2545 - DELETE is required to rename a file. So far, so good.
2546 - At least one cifs FS (Tru64) needs FILE_READ_ATTRIBUTE, otherwise the
2547 FileRenameInformation call fails with STATUS_ACCESS_DENIED. However,
2548 on NFS we get a STATUS_ACCESS_DENIED if FILE_READ_ATTRIBUTE is used
2549 and the file we try to rename is a symlink. Urgh.
2550 - Samba (only some versions?) doesn't like the FILE_SHARE_DELETE
2551 mode if the file has the R/O attribute set and returns
2552 STATUS_ACCESS_DENIED in that case. */
2554 ULONG access = DELETE
2555 | (oldpc.fs_is_cifs () ? FILE_READ_ATTRIBUTES : 0);
2556 ULONG sharing = FILE_SHARE_READ | FILE_SHARE_WRITE
2557 | (oldpc.fs_is_samba () ? 0 : FILE_SHARE_DELETE);
2558 ULONG flags = FILE_OPEN_FOR_BACKUP_INTENT
2559 | (oldpc.is_known_reparse_point ()
2560 ? FILE_OPEN_REPARSE_POINT : 0);
2561 status = NtOpenFile (&fh, access,
2562 oldpc.get_object_attr (attr, sec_none_nih),
2563 &io, sharing, flags);
2565 if (!NT_SUCCESS (status))
2567 debug_printf ("status %y", status);
2568 if (status == STATUS_SHARING_VIOLATION
2569 && cygwait (10L) != WAIT_SIGNALED)
2571 /* Typical BLODA problem. Some virus scanners check newly
2572 generated files and while doing that disallow DELETE access.
2573 That's really bad because it breaks applications which copy
2574 files by creating a temporary filename and then rename the
2575 temp filename to the target filename. This renaming fails due
2576 to the jealous virus scanner and the application fails to
2577 create the target file.
2579 This kludge tries to work around that by yielding until the
2580 sharing violation goes away, or a signal arrived, or after
2581 about a second, give or take. */
2582 if (++retry_count < 40)
2584 yield ();
2585 goto retry;
2588 else if (NT_TRANSACTIONAL_ERROR (status) && trans)
2590 /* If NtOpenFile fails due to transactional problems, stop
2591 transaction and go ahead without. */
2592 stop_transaction (status, old_trans, trans);
2593 debug_printf ("Transaction failure. Retry open.");
2594 goto retry;
2596 __seterrno_from_nt_status (status);
2597 __leave;
2600 if (use_posix_semantics)
2601 goto skip_pre_W10_checks;
2603 /* Renaming a dir to another, existing dir fails always, even if
2604 ReplaceIfExists is set to TRUE and the existing dir is empty. So
2605 we have to remove the destination dir first. This also covers the
2606 case that the destination directory is not empty. In that case,
2607 unlink_nt returns with STATUS_DIRECTORY_NOT_EMPTY. */
2608 if (dstpc->isdir ())
2610 status = unlink_nt (*dstpc, false);
2611 if (!NT_SUCCESS (status))
2613 __seterrno_from_nt_status (status);
2614 __leave;
2617 /* You can't copy a file if the destination exists and has the R/O
2618 attribute set. Remove the R/O attribute first. But first check
2619 if a removepc exists. If so, dstpc points to a non-existing file
2620 due to a mangled suffix. */
2621 else if (!removepc && dstpc->has_attribute (FILE_ATTRIBUTE_READONLY))
2623 status = NtOpenFile (&nfh, FILE_WRITE_ATTRIBUTES,
2624 dstpc->get_object_attr (attr, sec_none_nih),
2625 &io, FILE_SHARE_VALID_FLAGS,
2626 FILE_OPEN_FOR_BACKUP_INTENT
2627 | (dstpc->is_known_reparse_point ()
2628 ? FILE_OPEN_REPARSE_POINT : 0));
2629 if (!NT_SUCCESS (status))
2631 __seterrno_from_nt_status (status);
2632 __leave;
2634 status = NtSetAttributesFile (nfh, dstpc->file_attributes ()
2635 & ~FILE_ATTRIBUTE_READONLY);
2636 NtClose (nfh);
2637 if (!NT_SUCCESS (status))
2639 __seterrno_from_nt_status (status);
2640 __leave;
2644 skip_pre_W10_checks:
2646 /* SUSv3: If the old argument and the new argument resolve to the same
2647 existing file, rename() shall return successfully and perform no
2648 other action.
2649 The test tries to be as quick as possible. Due to the above cross
2650 device check we already know both files are on the same device. So
2651 it just tests if oldpath has more than 1 hardlink, then it opens
2652 newpath and tests for identical file ids. If so, oldpath and newpath
2653 refer to the same file. */
2654 if ((removepc || dstpc->exists ())
2655 && !oldpc.isdir ()
2656 && NT_SUCCESS (NtQueryInformationFile (fh, &io, &ofsi, sizeof ofsi,
2657 FileStandardInformation))
2658 && ofsi.NumberOfLinks > 1
2659 && NT_SUCCESS (NtOpenFile (&nfh, READ_CONTROL,
2660 (removepc ?: dstpc)->get_object_attr (attr, sec_none_nih),
2661 &io, FILE_SHARE_VALID_FLAGS,
2662 FILE_OPEN_FOR_BACKUP_INTENT
2663 | ((removepc ?: dstpc)->is_known_reparse_point ()
2664 ? FILE_OPEN_REPARSE_POINT : 0))))
2666 FILE_INTERNAL_INFORMATION ofii, nfii;
2668 if (NT_SUCCESS (NtQueryInformationFile (fh, &io, &ofii, sizeof ofii,
2669 FileInternalInformation))
2670 && NT_SUCCESS (NtQueryInformationFile (nfh, &io, &nfii,
2671 sizeof nfii,
2672 FileInternalInformation))
2673 && ofii.IndexNumber.QuadPart == nfii.IndexNumber.QuadPart)
2675 debug_printf ("%s and %s are the same file", oldpath, newpath);
2676 NtClose (nfh);
2677 res = set_same_file_return (noreplace);
2678 __leave;
2680 NtClose (nfh);
2682 /* Create FILE_RENAME_INFORMATION struct. Using a tmp_pathbuf area
2683 allows for paths of up to 32757 chars. This test is just for
2684 paranoia's sake. */
2685 if (dstpc->get_nt_native_path ()->Length
2686 > NT_MAX_PATH * sizeof (WCHAR) - sizeof (FILE_RENAME_INFORMATION))
2688 debug_printf ("target filename too long");
2689 set_errno (EINVAL);
2690 __leave;
2692 pfri = (PFILE_RENAME_INFORMATION) tp.w_get ();
2693 if (use_posix_semantics)
2694 pfri->Flags = noreplace ? 0
2695 : (FILE_RENAME_REPLACE_IF_EXISTS
2696 | FILE_RENAME_POSIX_SEMANTICS
2697 | FILE_RENAME_IGNORE_READONLY_ATTRIBUTE);
2698 else
2699 pfri->ReplaceIfExists = !noreplace;
2700 pfri->RootDirectory = NULL;
2701 pfri->FileNameLength = dstpc->get_nt_native_path ()->Length;
2702 memcpy (&pfri->FileName, dstpc->get_nt_native_path ()->Buffer,
2703 pfri->FileNameLength);
2704 /* If dstpc points to an existing file and RENAME_NOREPLACE has
2705 been specified, then we should get NT error
2706 STATUS_OBJECT_NAME_COLLISION ==> Win32 error
2707 ERROR_ALREADY_EXISTS ==> Cygwin error EEXIST. */
2708 status = NtSetInformationFile (fh, &io, pfri,
2709 sizeof *pfri + pfri->FileNameLength,
2710 use_posix_semantics
2711 ? FileRenameInformationEx
2712 : FileRenameInformation);
2713 /* This happens if the access rights don't allow deleting the destination.
2714 Even if the handle to the original file is opened with BACKUP
2715 and/or RECOVERY, these flags don't apply to the destination of the
2716 rename operation. So, a privileged user can't rename a file to an
2717 existing file, if the permissions of the existing file aren't right.
2718 Like directories, we have to handle this separately by removing the
2719 destination before renaming. */
2720 if (status == STATUS_ACCESS_DENIED && dstpc->exists ()
2721 && !dstpc->isdir ())
2723 bool need_open = false;
2725 if ((dstpc->fs_flags () & FILE_SUPPORTS_TRANSACTIONS) && !trans)
2727 /* As mentioned earlier, opening the file must be part of the
2728 transaction. Therefore we have to reopen the file here if the
2729 transaction hasn't been started already. Unfortunately we
2730 can't use the NT "reopen file from existing handle" feature.
2731 In that case NtOpenFile returns STATUS_TRANSACTIONAL_CONFLICT.
2732 We *have* to close the handle to the file first, *then* we can
2733 re-open it. Fortunately nothing has happened yet, so the
2734 atomicity of the rename functionality is not spoiled. */
2735 NtClose (fh);
2736 start_transaction (old_trans, trans);
2737 need_open = true;
2739 while (true)
2741 status = STATUS_SUCCESS;
2742 if (need_open)
2743 status = NtOpenFile (&fh, DELETE,
2744 oldpc.get_object_attr (attr, sec_none_nih),
2745 &io, FILE_SHARE_VALID_FLAGS,
2746 FILE_OPEN_FOR_BACKUP_INTENT
2747 | (oldpc.is_known_reparse_point ()
2748 ? FILE_OPEN_REPARSE_POINT : 0));
2749 if (NT_SUCCESS (status))
2751 status = unlink_nt (*dstpc, false);
2752 if (NT_SUCCESS (status))
2753 break;
2755 if (!NT_TRANSACTIONAL_ERROR (status) || !trans)
2756 break;
2757 /* If NtOpenFile or unlink_nt fail due to transactional problems,
2758 stop transaction and retry without. */
2759 NtClose (fh);
2760 stop_transaction (status, old_trans, trans);
2761 debug_printf ("Transaction failure %y. Retry open.", status);
2763 if (NT_SUCCESS (status))
2764 status = NtSetInformationFile (fh, &io, pfri,
2765 sizeof *pfri + pfri->FileNameLength,
2766 FileRenameInformation);
2768 if (NT_SUCCESS (status))
2770 if (removepc)
2771 unlink_nt (*removepc, false);
2772 res = 0;
2774 else if (use_posix_semantics && status == STATUS_INVALID_PARAMETER)
2776 /* NtSetInformationFile returns STATUS_INVALID_PARAMETER
2777 on a bind mounted file system in hyper-v container
2778 with FILE_RENAME_POSIX_SEMANTICS.
2779 Disable the use_posix semntics flag and retry. */
2780 debug_printf ("NtSetInformationFile failed with posix semantics. "
2781 "Disable it and retry.");
2782 use_posix_semantics = 0;
2783 goto ignore_posix_semantics_retry;
2785 else
2786 __seterrno_from_nt_status (status);
2788 __except (EFAULT)
2790 res = -1;
2792 __endtry
2793 if (fh)
2794 NtClose (fh);
2795 /* Stop transaction if we started one. */
2796 if (trans)
2797 stop_transaction (status, old_trans, trans);
2798 if (get_errno () != EFAULT)
2799 syscall_printf ("%R = rename(%s, %s)", res, oldpath, newpath);
2800 return res;
2803 extern "C" int
2804 rename (const char *oldpath, const char *newpath)
2806 return rename2 (oldpath, newpath, 0);
2809 extern "C" int
2810 system (const char *cmdstring)
2812 pthread_testcancel ();
2814 if (cmdstring == NULL)
2815 return 1;
2817 int res = -1;
2818 const char* command[4];
2820 __try
2822 command[0] = "sh";
2823 command[1] = "-c";
2824 command[2] = cmdstring;
2825 command[3] = (const char *) NULL;
2827 if ((res = spawnvp (_P_SYSTEM, "/bin/sh", command)) == -1)
2829 // when exec fails, return value should be as if shell
2830 // executed exit (127)
2831 res = 127;
2834 __except (EFAULT) {}
2835 __endtry
2836 return res;
2839 extern "C" int
2840 setdtablesize (int size)
2842 if (size < 0)
2844 set_errno (EINVAL);
2845 return -1;
2848 if (size <= (int) cygheap->fdtab.size
2849 || cygheap->fdtab.extend (size - cygheap->fdtab.size, OPEN_MAX))
2850 return 0;
2852 return -1;
2855 extern "C" int
2856 getdtablesize ()
2858 return OPEN_MAX;
2861 extern "C" int
2862 getpagesize ()
2864 return (size_t) wincap.allocation_granularity ();
2867 /* FIXME: not all values are correct... */
2868 extern "C" long int
2869 fpathconf (int fd, int v)
2871 cygheap_fdget cfd (fd);
2872 if (cfd < 0)
2873 return -1;
2874 return cfd->fpathconf (v);
2877 extern "C" long int
2878 pathconf (const char *file, int v)
2880 fhandler_base *fh = NULL;
2881 long ret = -1;
2883 __try
2885 if (!*file)
2887 set_errno (ENOENT);
2888 return -1;
2890 if (!(fh = build_fh_name (file, PC_SYM_FOLLOW, stat_suffixes)))
2891 return -1;
2892 if (!fh->exists ())
2893 set_errno (ENOENT);
2894 else
2895 ret = fh->fpathconf (v);
2897 __except (EFAULT) {}
2898 __endtry
2899 delete fh;
2900 return ret;
2903 extern "C" int
2904 ttyname_r (int fd, char *buf, size_t buflen)
2906 int ret = 0;
2908 __try
2910 cygheap_fdget cfd (fd, true);
2911 if (cfd < 0)
2912 ret = EBADF;
2913 else if (!cfd->is_tty ())
2914 ret = ENOTTY;
2915 else if (buflen < strlen (cfd->ttyname ()) + 1)
2916 ret = ERANGE;
2917 else
2918 strcpy (buf, cfd->ttyname ());
2919 debug_printf ("returning %d tty: %s", ret, ret ? "NULL" : buf);
2921 __except (NO_ERROR)
2923 ret = EFAULT;
2925 __endtry
2926 return ret;
2929 extern "C" char *
2930 ttyname (int fd)
2932 static char name[TTY_NAME_MAX];
2933 int ret = ttyname_r (fd, name, TTY_NAME_MAX);
2934 if (ret)
2936 set_errno (ret);
2937 return NULL;
2939 return name;
2942 extern "C" char *
2943 ctermid (char *str)
2945 if (str == NULL)
2946 str = _my_tls.locals.ttybuf;
2947 if (!CTTY_IS_VALID (myself->ctty))
2948 strcpy (str, "no tty");
2949 else
2951 device d;
2952 d.parse (myself->ctty);
2953 strcpy (str, d.name ());
2955 return str;
2958 /* Tells stdio if it should do the cr/lf conversion for this file */
2959 extern "C" int
2960 _cygwin_istext_for_stdio (int fd)
2962 cygheap_fdget cfd (fd, false, false);
2963 if (cfd < 0)
2965 syscall_printf ("fd %d: not open", fd);
2966 return 0;
2969 #if 0
2970 if (cfd->get_device () != FH_FS)
2972 syscall_printf ("fd not disk file. Defaulting to binary.");
2973 return 0;
2975 #endif
2977 if (cfd->wbinary () || cfd->rbinary ())
2979 syscall_printf ("fd %d: opened as binary", fd);
2980 return 0;
2983 syscall_printf ("fd %d: defaulting to text", fd);
2984 return 1;
2987 static int
2988 setmode_helper (struct _reent *ptr __unused, FILE *f)
2990 if (fileno (f) != _my_tls.locals.setmode_file)
2992 syscall_printf ("improbable, but %d != %d", fileno (f), _my_tls.locals.setmode_file);
2993 return 0;
2995 syscall_printf ("file was %s now %s", f->_flags & __SCLE ? "text" : "binary",
2996 _my_tls.locals.setmode_mode & O_TEXT ? "text" : "binary");
2997 if (_my_tls.locals.setmode_mode & O_TEXT)
2998 f->_flags |= __SCLE;
2999 else
3000 f->_flags &= ~__SCLE;
3001 return 0;
3004 extern "C" int
3005 getmode (int fd)
3007 cygheap_fdget cfd (fd);
3008 if (cfd < 0)
3009 return -1;
3011 return cfd->get_flags () & (O_BINARY | O_TEXT);
3014 /* Set a file descriptor into text or binary mode, returning the
3015 previous mode. */
3017 extern "C" int
3018 _setmode (int fd, int mode)
3020 cygheap_fdget cfd (fd);
3021 if (cfd < 0)
3022 return -1;
3023 if (mode != O_BINARY && mode != O_TEXT && mode != 0)
3025 set_errno (EINVAL);
3026 return -1;
3029 /* Note that we have no way to indicate the case that writes are
3030 binary but not reads, or vice-versa. These cases can arise when
3031 using the tty or console interface. People using those
3032 interfaces should not use setmode. */
3034 int res;
3035 if (cfd->wbinary () && cfd->rbinary ())
3036 res = O_BINARY;
3037 else if (cfd->wbinset () && cfd->rbinset ())
3038 res = O_TEXT; /* Specifically set O_TEXT */
3039 else
3040 res = 0;
3042 if (!mode)
3043 cfd->reset_to_open_binmode ();
3044 else
3045 cfd->set_flags ((cfd->get_flags () & ~(O_TEXT | O_BINARY)) | mode);
3047 syscall_printf ("(%d<%S>, %p) returning %s", fd,
3048 cfd->pc.get_nt_native_path (), mode,
3049 res & O_TEXT ? "text" : "binary");
3050 return res;
3053 extern "C" int
3054 cygwin_setmode (int fd, int mode)
3056 int res = _setmode (fd, mode);
3057 if (res != -1)
3059 _my_tls.locals.setmode_file = fd;
3060 if (_cygwin_istext_for_stdio (fd))
3061 _my_tls.locals.setmode_mode = O_TEXT;
3062 else
3063 _my_tls.locals.setmode_mode = O_BINARY;
3064 _fwalk_sglue (_GLOBAL_REENT, setmode_helper, &__sglue);
3066 return res;
3069 extern "C" int
3070 posix_fadvise (int fd, off_t offset, off_t len, int advice)
3072 int res = -1;
3073 cygheap_fdget cfd (fd);
3074 if (cfd >= 0)
3075 res = cfd->fadvise (offset, len, advice);
3076 else
3077 res = EBADF;
3078 syscall_printf ("%R = posix_fadvice(%d, %D, %D, %d)",
3079 res, fd, offset, len, advice);
3080 return res;
3083 extern "C" int
3084 fallocate (int fd, int mode, off_t offset, off_t len)
3086 int res = 0;
3088 /* First check mask of allowed flags */
3089 if (mode & ~(FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE
3090 | FALLOC_FL_UNSHARE_RANGE | FALLOC_FL_COLLAPSE_RANGE
3091 | FALLOC_FL_INSERT_RANGE | FALLOC_FL_KEEP_SIZE))
3092 res = EOPNOTSUPP;
3093 /* Either FALLOC_FL_PUNCH_HOLE or FALLOC_FL_ZERO_RANGE, never both */
3094 else if ((mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE))
3095 == (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE))
3096 res = EOPNOTSUPP;
3097 /* FALLOC_FL_PUNCH_HOLE must be ORed with FALLOC_FL_KEEP_SIZE */
3098 else if ((mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE))
3099 == FALLOC_FL_PUNCH_HOLE)
3100 res = EOPNOTSUPP;
3101 else if (offset < 0 || len <= 0)
3102 res = EINVAL;
3103 else if (INT64_MAX - len < offset)
3104 res = EFBIG;
3105 else
3107 cygheap_fdget cfd (fd);
3108 if (cfd >= 0)
3109 res = cfd->fallocate (mode, offset, len);
3110 else
3111 res = EBADF;
3112 if (res == EISDIR)
3113 res = ENODEV;
3115 if (res)
3117 set_errno (res);
3118 res = -1;
3120 syscall_printf ("%R = fallocate(%d, %y, %D, %D)", res, fd, mode, offset, len);
3121 return res;
3124 extern "C" int
3125 posix_fallocate (int fd, off_t offset, off_t len)
3127 int res = 0;
3128 if (offset < 0 || len <= 0)
3129 res = EINVAL;
3130 else if (INT64_MAX - len < offset)
3131 res = EFBIG;
3132 else
3134 cygheap_fdget cfd (fd);
3135 if (cfd >= 0)
3136 res = cfd->fallocate (0, offset, len);
3137 else
3138 res = EBADF;
3139 if (res == EISDIR)
3140 res = ENODEV;
3142 syscall_printf ("%R = posix_fallocate(%d, %D, %D)", res, fd, offset, len);
3143 return res;
3146 extern "C" int
3147 ftruncate (int fd, off_t length)
3149 int res = -1;
3150 cygheap_fdget cfd (fd);
3151 if (cfd >= 0)
3153 res = cfd->fallocate (__FALLOC_FL_TRUNCATE, 0, length);
3154 if (res)
3156 if (res == ENODEV)
3157 res = EINVAL;
3158 set_errno (res);
3159 res = -1;
3162 else
3163 set_errno (EBADF);
3164 syscall_printf ("%R = ftruncate(%d, %D)", res, fd, length);
3165 return res;
3168 /* truncate: Provided by SVR4 and 4.3+BSD. Not part of POSIX.1 or XPG3 */
3169 extern "C" int
3170 truncate (const char *pathname, off_t length)
3172 int fd;
3173 int res = -1;
3175 fd = open (pathname, O_RDWR);
3177 if (fd != -1)
3179 res = ftruncate (fd, length);
3180 close (fd);
3182 syscall_printf ("%R = truncate(%s, %D)", res, pathname, length);
3184 return res;
3187 extern "C" long
3188 _get_osfhandle (int fd)
3190 long res;
3192 cygheap_fdget cfd (fd);
3193 if (cfd >= 0)
3194 res = (long) cfd->get_handle ();
3195 else
3196 res = -1;
3198 syscall_printf ("%R = get_osfhandle(%d)", res, fd);
3199 return res;
3202 extern "C" int
3203 fstatvfs (int fd, struct statvfs *sfs)
3205 __try
3207 cygheap_fdget cfd (fd);
3208 if (cfd < 0)
3209 __leave;
3210 return cfd->fstatvfs (sfs);
3212 __except (EFAULT) {}
3213 __endtry
3214 return -1;
3217 extern "C" int
3218 statvfs (const char *name, struct statvfs *sfs)
3220 int res = -1;
3221 fhandler_base *fh = NULL;
3223 __try
3225 if (!(fh = build_fh_name (name, PC_SYM_FOLLOW, stat_suffixes)))
3226 __leave;
3228 if (fh->error ())
3230 debug_printf ("got %d error from build_fh_name", fh->error ());
3231 set_errno (fh->error ());
3233 else if (fh->exists ())
3235 debug_printf ("(%s, %p), file_attributes %d", name, sfs, (DWORD) *fh);
3236 res = fh->fstatvfs (sfs);
3238 else
3239 set_errno (ENOENT);
3242 __except (EFAULT) {}
3243 __endtry
3244 delete fh;
3245 if (get_errno () != EFAULT)
3246 syscall_printf ("%R = statvfs(%s,%p)", res, name, sfs);
3247 return res;
3250 extern "C" int
3251 fstatfs (int fd, struct statfs *sfs)
3253 struct statvfs vfs;
3254 int ret = fstatvfs (fd, &vfs);
3255 if (!ret)
3257 sfs->f_type = vfs.f_flag;
3258 sfs->f_bsize = vfs.f_bsize;
3259 sfs->f_blocks = vfs.f_blocks;
3260 sfs->f_bavail = vfs.f_bavail;
3261 sfs->f_bfree = vfs.f_bfree;
3262 sfs->f_files = -1;
3263 sfs->f_ffree = -1;
3264 sfs->f_fsid = vfs.f_fsid;
3265 sfs->f_namelen = vfs.f_namemax;
3267 return ret;
3270 extern "C" int
3271 statfs (const char *fname, struct statfs *sfs)
3273 struct statvfs vfs;
3274 int ret = statvfs (fname, &vfs);
3275 if (!ret)
3277 sfs->f_type = vfs.f_flag;
3278 sfs->f_bsize = vfs.f_bsize;
3279 sfs->f_blocks = vfs.f_blocks;
3280 sfs->f_bavail = vfs.f_bavail;
3281 sfs->f_bfree = vfs.f_bfree;
3282 sfs->f_files = -1;
3283 sfs->f_ffree = -1;
3284 sfs->f_fsid = vfs.f_fsid;
3285 sfs->f_namelen = vfs.f_namemax;
3287 return ret;
3290 /* setpgid: POSIX 4.3.3.1 */
3291 extern "C" int
3292 setpgid (pid_t pid, pid_t pgid)
3294 int res = -1;
3295 if (pid == 0)
3296 pid = getpid ();
3297 if (pgid == 0)
3298 pgid = pid;
3300 if (pgid < 0)
3301 set_errno (EINVAL);
3302 else
3304 pinfo p (pid, PID_MAP_RW);
3305 if (!p)
3306 set_errno (ESRCH);
3307 else if (p->pgid == pgid)
3308 res = 0;
3309 /* A process may only change the process group of itself and its children */
3310 else if (p != myself && p->ppid != myself->pid)
3311 set_errno (EPERM);
3312 else
3314 p->pgid = pgid;
3315 if (p->pid != p->pgid)
3316 p->set_has_pgid_children (0);
3317 res = 0;
3321 syscall_printf ("pid %d, pgid %d, res %d", pid, pgid, res);
3322 return res;
3325 extern "C" pid_t
3326 getpgid (pid_t pid)
3328 if (pid == 0)
3329 pid = getpid ();
3331 pinfo p (pid);
3332 if (!p)
3334 set_errno (ESRCH);
3335 return -1;
3337 return p->pgid;
3340 extern "C" int
3341 setpgrp (void)
3343 return setpgid (0, 0);
3346 extern "C" pid_t
3347 getpgrp (void)
3349 return getpgid (0);
3352 extern "C" char *
3353 ptsname (int fd)
3355 static char buf[TTY_NAME_MAX];
3356 return ptsname_r (fd, buf, sizeof (buf)) == 0 ? buf : NULL;
3359 extern "C" int
3360 ptsname_r (int fd, char *buf, size_t buflen)
3362 if (!buf)
3364 set_errno (EINVAL);
3365 return EINVAL;
3368 cygheap_fdget cfd (fd);
3369 if (cfd < 0)
3370 return EBADF;
3371 return cfd->ptsname_r (buf, buflen);
3374 static int
3375 mknod_worker (path_conv &pc, mode_t mode, _major_t major, _minor_t minor)
3377 char buf[sizeof (":\\00000000:00000000:00000000") + PATH_MAX];
3378 sprintf (buf, ":\\%x:%x:%x", major, minor, mode);
3379 return symlink_worker (buf, pc, true);
3382 extern "C" int
3383 mknod (const char *path, mode_t mode, dev_t dev)
3385 __try
3387 if (!*path)
3389 set_errno (ENOENT);
3390 __leave;
3393 if (strlen (path) >= PATH_MAX)
3394 __leave;
3396 /* Trailing dirsep is a no-no, only errno differs. */
3397 bool has_trailing_dirsep = isdirsep (path[strlen (path) - 1]);
3399 path_conv w32path (path, PC_SYM_NOFOLLOW | PC_SYM_NOFOLLOW_DIR
3400 | PC_POSIX, stat_suffixes);
3402 if (w32path.exists () || has_trailing_dirsep)
3404 set_errno (w32path.exists () ? EEXIST : ENOENT);
3405 __leave;
3408 mode_t type = mode & S_IFMT;
3409 _major_t major = _major (dev);
3410 _minor_t minor = _minor (dev);
3411 switch (type)
3413 case S_IFCHR:
3414 case S_IFBLK:
3415 break;
3417 case S_IFIFO:
3418 major = _major (FH_FIFO);
3419 minor = _minor (FH_FIFO);
3420 break;
3422 case 0:
3423 case S_IFREG:
3425 int fd = open (path, O_CREAT, mode);
3426 if (fd < 0)
3427 __leave;
3428 close (fd);
3429 return 0;
3432 default:
3433 set_errno (EINVAL);
3434 __leave;
3437 return mknod_worker (w32path, mode, major, minor);
3439 __except (EFAULT)
3440 __endtry
3441 return -1;
3444 extern "C" int
3445 mkfifo (const char *path, mode_t mode)
3447 return mknod (path, (mode & ~S_IFMT) | S_IFIFO, 0);
3450 /* seteuid: standards? */
3451 extern "C" int
3452 seteuid (uid_t uid)
3454 debug_printf ("uid: %u myself->uid: %u myself->gid: %u",
3455 uid, myself->uid, myself->gid);
3457 /* Same uid as we're just running under is usually a no-op.
3459 Except we have an external token which is a restricted token. Or,
3460 the external token is NULL, but the current impersonation token is
3461 a restricted token. This allows to restrict user rights temporarily
3462 like this:
3464 cygwin_internal(CW_SET_EXTERNAL_TOKEN, restricted_token,
3465 CW_TOKEN_RESTRICTED);
3466 setuid (getuid ());
3467 [...do stuff with restricted rights...]
3468 cygwin_internal(CW_SET_EXTERNAL_TOKEN, INVALID_HANDLE_VALUE,
3469 CW_TOKEN_RESTRICTED);
3470 setuid (getuid ());
3472 Note that using the current uid is a requirement! We have restricted
3473 tokens galore (UAC), so this is really just a special case to restrict
3474 your own processes to lesser rights. */
3475 bool request_restricted_uid_switch = (uid == myself->uid
3476 && cygheap->user.ext_token_is_restricted);
3477 if (uid == myself->uid && !cygheap->user.groups.ischanged
3478 && !request_restricted_uid_switch)
3480 debug_printf ("Nothing happens");
3481 return 0;
3484 cygsid usersid;
3485 user_groups &groups = cygheap->user.groups;
3486 HANDLE new_token = NULL;
3487 struct passwd * pw_new;
3488 bool token_is_internal, issamesid = false;
3490 pw_new = internal_getpwuid (uid);
3491 if (!usersid.getfrompw (pw_new))
3493 set_errno (EINVAL);
3494 return -1;
3497 cygheap->user.deimpersonate ();
3499 /* Verify if the process token is suitable. */
3500 /* First of all, skip all checks if a switch to a restricted token has been
3501 requested, or if trying to switch back from it. */
3502 if (request_restricted_uid_switch)
3504 if (cygheap->user.external_token != NO_IMPERSONATION)
3506 debug_printf ("Switch to restricted token");
3507 new_token = cygheap->user.external_token;
3509 else
3511 debug_printf ("Switch back from restricted token");
3512 new_token = hProcToken;
3513 cygheap->user.ext_token_is_restricted = false;
3516 /* TODO, CV 2008-11-25: The check against saved_sid is a kludge and a
3517 shortcut. We must check if it's really feasible in the long run.
3518 The reason to add this shortcut is this: sshd switches back to the
3519 privileged user running sshd at least twice in the process of
3520 authentication. It calls seteuid first, then setegid. Due to this
3521 order, the setgroups group list is still active when calling seteuid
3522 and verify_token treats the original token of the privileged user as
3523 insufficient. This in turn results in creating a new user token for
3524 the privileged user instead of using the original token. This can have
3525 unfortunate side effects. The created token has different group
3526 memberships, different user rights, and misses possible network
3527 credentials.
3528 Therefore we try this shortcut now. When switching back to the
3529 privileged user, we probably always want a correct (aka original)
3530 user token for this privileged user, not only in sshd. */
3531 else if ((uid == cygheap->user.saved_uid
3532 && usersid == cygheap->user.saved_sid ())
3533 || verify_token (hProcToken, usersid, groups))
3534 new_token = hProcToken;
3535 /* Verify if the external token is suitable */
3536 else if (cygheap->user.external_token != NO_IMPERSONATION
3537 && verify_token (cygheap->user.external_token, usersid, groups))
3538 new_token = cygheap->user.external_token;
3539 /* Verify if the current token (internal or former external) is suitable */
3540 else if (cygheap->user.curr_primary_token != NO_IMPERSONATION
3541 && cygheap->user.curr_primary_token != cygheap->user.external_token
3542 && verify_token (cygheap->user.curr_primary_token, usersid, groups,
3543 &token_is_internal))
3544 new_token = cygheap->user.curr_primary_token;
3545 /* Verify if the internal token is suitable */
3546 else if (cygheap->user.internal_token != NO_IMPERSONATION
3547 && cygheap->user.internal_token != cygheap->user.curr_primary_token
3548 && verify_token (cygheap->user.internal_token, usersid, groups,
3549 &token_is_internal))
3550 new_token = cygheap->user.internal_token;
3552 debug_printf ("Found token %p", new_token);
3554 /* If no impersonation token is available, try to authenticate using
3555 LSA private data stored password, or, if that fails, S4U logon. */
3556 if (new_token == NULL)
3558 if (!(new_token = lsaprivkeyauth (pw_new)))
3560 NTSTATUS status;
3561 WCHAR domain[MAX_DOMAIN_NAME_LEN + 1];
3562 WCHAR user[UNLEN + 1];
3564 debug_printf ("lsaprivkeyauth failed, try s4uauth.");
3565 extract_nt_dom_user (pw_new, domain, user);
3566 if (!(new_token = s4uauth (true, domain, user, status)))
3568 debug_printf ("s4uauth failed, bail out");
3569 cygheap->user.reimpersonate ();
3570 return -1;
3574 /* Keep at most one internal token */
3575 if (cygheap->user.internal_token != NO_IMPERSONATION)
3576 CloseHandle (cygheap->user.internal_token);
3577 cygheap->user.internal_token = new_token;
3580 if (new_token != hProcToken)
3582 NTSTATUS status;
3584 if (!request_restricted_uid_switch)
3585 load_user_profile (new_token, pw_new, usersid);
3587 /* Try setting owner to same value as user. */
3588 status = NtSetInformationToken (new_token, TokenOwner,
3589 &usersid, sizeof usersid);
3590 if (!NT_SUCCESS (status))
3591 debug_printf ("NtSetInformationToken (user.token, TokenOwner), %y",
3592 status);
3593 /* Try setting primary group in token to current group */
3594 status = NtSetInformationToken (new_token, TokenPrimaryGroup,
3595 &groups.pgsid, sizeof (cygsid));
3596 if (!NT_SUCCESS (status))
3597 debug_printf ("NtSetInformationToken (user.token, TokenPrimaryGroup),"
3598 "%y", status);
3599 /* Try setting default DACL */
3600 PACL dacl_buf = (PACL) alloca (MAX_DACL_LEN (5));
3601 if (sec_acl (dacl_buf, true, true, usersid))
3603 TOKEN_DEFAULT_DACL tdacl = { dacl_buf };
3604 status = NtSetInformationToken (new_token, TokenDefaultDacl,
3605 &tdacl, sizeof (tdacl));
3606 if (!NT_SUCCESS (status))
3607 debug_printf ("NtSetInformationToken (TokenDefaultDacl), %y",
3608 status);
3612 issamesid = (usersid == cygheap->user.sid ());
3613 cygheap->user.set_sid (usersid);
3614 cygheap->user.curr_primary_token = new_token == hProcToken ? NO_IMPERSONATION
3615 : new_token;
3616 cygheap->user.curr_token_is_restricted = false;
3617 cygheap->user.setuid_to_restricted = false;
3618 if (cygheap->user.curr_imp_token != NO_IMPERSONATION)
3620 CloseHandle (cygheap->user.curr_imp_token);
3621 cygheap->user.curr_imp_token = NO_IMPERSONATION;
3623 if (cygheap->user.curr_primary_token != NO_IMPERSONATION)
3625 /* HANDLE_FLAG_INHERIT may be missing in external token. */
3626 if (!SetHandleInformation (cygheap->user.curr_primary_token,
3627 HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)
3628 || !DuplicateTokenEx (cygheap->user.curr_primary_token,
3629 MAXIMUM_ALLOWED, &sec_none,
3630 SecurityImpersonation, TokenImpersonation,
3631 &cygheap->user.curr_imp_token))
3633 __seterrno ();
3634 cygheap->user.curr_primary_token = NO_IMPERSONATION;
3635 return -1;
3637 cygheap->user.curr_token_is_restricted = request_restricted_uid_switch;
3638 set_cygwin_privileges (cygheap->user.curr_primary_token);
3639 set_cygwin_privileges (cygheap->user.curr_imp_token);
3641 if (!cygheap->user.reimpersonate ())
3643 __seterrno ();
3644 return -1;
3647 cygheap->user.set_name (pw_new->pw_name);
3648 myself->uid = uid;
3649 groups.ischanged = FALSE;
3650 if (!issamesid)
3651 /* Recreate and fill out the user shared region for a new user. */
3652 user_info::create (true);
3653 return 0;
3656 /* setuid: POSIX 4.2.2.1 */
3657 extern "C" int
3658 setuid (uid_t uid)
3660 int ret = seteuid (uid);
3661 if (!ret)
3663 cygheap->user.real_uid = myself->uid;
3664 /* If restricted token, forget original privileges on exec (). */
3665 cygheap->user.setuid_to_restricted = cygheap->user.curr_token_is_restricted;
3667 debug_printf ("real: %d, effective: %d", cygheap->user.real_uid, myself->uid);
3668 return ret;
3671 extern "C" int
3672 setreuid (uid_t ruid, uid_t euid)
3674 int ret = 0;
3675 bool tried = false;
3676 uid_t old_euid = myself->uid;
3678 if (ruid != ILLEGAL_UID && cygheap->user.real_uid != ruid && euid != ruid)
3679 tried = !(ret = seteuid (ruid));
3680 if (!ret && euid != ILLEGAL_UID)
3681 ret = seteuid (euid);
3682 if (tried && (ret || euid == ILLEGAL_UID) && seteuid (old_euid))
3683 system_printf ("Cannot restore original euid %u", old_euid);
3684 if (!ret && ruid != ILLEGAL_UID)
3685 cygheap->user.real_uid = ruid;
3686 debug_printf ("real: %u, effective: %u", cygheap->user.real_uid, myself->uid);
3687 return ret;
3690 /* setegid: from System V. */
3691 extern "C" int
3692 setegid (gid_t gid)
3694 debug_printf ("new egid: %u current: %u", gid, myself->gid);
3696 if (gid == myself->gid)
3698 myself->gid = gid;
3699 return 0;
3702 NTSTATUS status;
3703 user_groups * groups = &cygheap->user.groups;
3704 cygsid gsid;
3705 struct group * gr = internal_getgrgid (gid);
3707 if (!gsid.getfromgr (gr))
3709 set_errno (EINVAL);
3710 return -1;
3712 myself->gid = gid;
3714 groups->update_pgrp (gsid);
3715 if (cygheap->user.issetuid ())
3717 /* If impersonated, update impersonation token... */
3718 status = NtSetInformationToken (cygheap->user.primary_token (),
3719 TokenPrimaryGroup, &gsid, sizeof gsid);
3720 if (!NT_SUCCESS (status))
3721 debug_printf ("NtSetInformationToken (primary_token, "
3722 "TokenPrimaryGroup), %y", status);
3723 status = NtSetInformationToken (cygheap->user.imp_token (),
3724 TokenPrimaryGroup, &gsid, sizeof gsid);
3725 if (!NT_SUCCESS (status))
3726 debug_printf ("NtSetInformationToken (token, TokenPrimaryGroup), %y",
3727 status);
3729 cygheap->user.deimpersonate ();
3730 status = NtSetInformationToken (hProcToken, TokenPrimaryGroup,
3731 &gsid, sizeof gsid);
3732 if (!NT_SUCCESS (status))
3733 debug_printf ("NtSetInformationToken (hProcToken, TokenPrimaryGroup), %y",
3734 status);
3735 clear_procimptoken ();
3736 cygheap->user.reimpersonate ();
3737 return 0;
3740 /* setgid: POSIX 4.2.2.1 */
3741 extern "C" int
3742 setgid (gid_t gid)
3744 int ret = setegid (gid);
3745 if (!ret)
3746 cygheap->user.real_gid = myself->gid;
3747 return ret;
3750 extern "C" int
3751 setregid (gid_t rgid, gid_t egid)
3753 int ret = 0;
3754 bool tried = false;
3755 gid_t old_egid = myself->gid;
3757 if (rgid != ILLEGAL_GID && cygheap->user.real_gid != rgid && egid != rgid)
3758 tried = !(ret = setegid (rgid));
3759 if (!ret && egid != ILLEGAL_GID)
3760 ret = setegid (egid);
3761 if (tried && (ret || egid == ILLEGAL_GID) && setegid (old_egid))
3762 system_printf ("Cannot restore original egid %u", old_egid);
3763 if (!ret && rgid != ILLEGAL_GID)
3764 cygheap->user.real_gid = rgid;
3765 debug_printf ("real: %u, effective: %u", cygheap->user.real_gid, myself->gid);
3766 return ret;
3769 /* chroot: privileged Unix system call. */
3770 /* FIXME: Not privileged here. How should this be done? */
3771 extern "C" int
3772 chroot (const char *newroot)
3774 path_conv path (newroot, PC_SYM_FOLLOW | PC_POSIX);
3776 int ret = -1;
3777 if (path.error)
3778 set_errno (path.error);
3779 else if (!path.exists ())
3780 set_errno (ENOENT);
3781 else if (!path.isdir ())
3782 set_errno (ENOTDIR);
3783 else if (path.isspecial ())
3784 set_errno (EPERM);
3785 else
3787 getwinenv("PATH="); /* Save the native PATH */
3788 cygheap->root.set (path.get_posix (), path.get_win32 (),
3789 !!path.objcaseinsensitive ());
3790 ret = 0;
3793 syscall_printf ("%R = chroot(%s)", ret, newroot ?: "NULL");
3794 return ret;
3797 extern "C" int
3798 creat (const char *path, mode_t mode)
3800 return open (path, O_WRONLY | O_CREAT | O_TRUNC, mode);
3803 extern "C" void
3804 __assertfail ()
3806 exit (99);
3809 extern "C" int
3810 vhangup ()
3812 set_errno (ENOSYS);
3813 return -1;
3816 extern "C" int
3817 setpriority (int which, id_t who, int value)
3819 DWORD prio = nice_to_winprio (value);
3820 int error = 0;
3822 switch (which)
3824 case PRIO_PROCESS:
3825 if (!who)
3826 who = myself->pid;
3827 if ((pid_t) who == myself->pid)
3829 if (!SetPriorityClass (GetCurrentProcess (), prio))
3831 set_errno (EACCES);
3832 return -1;
3834 myself->nice = value;
3835 debug_printf ("Set nice to %d", myself->nice);
3836 return 0;
3838 break;
3839 case PRIO_PGRP:
3840 if (!who)
3841 who = myself->pgid;
3842 break;
3843 case PRIO_USER:
3844 if (!who)
3845 who = myself->uid;
3846 break;
3847 default:
3848 set_errno (EINVAL);
3849 return -1;
3851 winpids pids ((DWORD) PID_MAP_RW);
3852 for (DWORD i = 0; i < pids.npids; ++i)
3854 _pinfo *p = pids[i];
3855 if (p)
3857 switch (which)
3859 case PRIO_PROCESS:
3860 if ((pid_t) who != p->pid)
3861 continue;
3862 break;
3863 case PRIO_PGRP:
3864 if ((pid_t) who != p->pgid)
3865 continue;
3866 break;
3867 case PRIO_USER:
3868 if ((uid_t) who != p->uid)
3869 continue;
3870 break;
3872 HANDLE proc_h = OpenProcess (PROCESS_SET_INFORMATION, FALSE,
3873 p->dwProcessId);
3874 if (!proc_h)
3875 error = EPERM;
3876 else
3878 if (!SetPriorityClass (proc_h, prio))
3879 error = EACCES;
3880 else
3881 p->nice = value;
3882 CloseHandle (proc_h);
3886 pids.reset ();
3887 if (error)
3889 set_errno (error);
3890 return -1;
3892 return 0;
3895 extern "C" int
3896 getpriority (int which, id_t who)
3898 int nice = NZERO * 2; /* Illegal value */
3900 switch (which)
3902 case PRIO_PROCESS:
3903 if (!who)
3904 who = myself->pid;
3905 if ((pid_t) who == myself->pid)
3907 DWORD winprio = GetPriorityClass(GetCurrentProcess());
3908 if (winprio != nice_to_winprio(myself->nice))
3909 myself->nice = winprio_to_nice(winprio);
3910 return myself->nice;
3912 break;
3913 case PRIO_PGRP:
3914 if (!who)
3915 who = myself->pgid;
3916 break;
3917 case PRIO_USER:
3918 if (!who)
3919 who = myself->uid;
3920 break;
3921 default:
3922 set_errno (EINVAL);
3923 return -1;
3925 winpids pids ((DWORD) 0);
3926 for (DWORD i = 0; i < pids.npids; ++i)
3928 _pinfo *p = pids[i];
3929 if (p)
3930 switch (which)
3932 case PRIO_PROCESS:
3933 if ((pid_t) who == p->pid)
3935 nice = p->nice;
3936 goto out;
3938 break;
3939 case PRIO_PGRP:
3940 if ((pid_t) who == p->pgid && p->nice < nice)
3941 nice = p->nice;
3942 break;
3943 case PRIO_USER:
3944 if ((uid_t) who == p->uid && p->nice < nice)
3945 nice = p->nice;
3946 break;
3949 out:
3950 pids.reset ();
3951 if (nice == NZERO * 2)
3953 set_errno (ESRCH);
3954 return -1;
3956 return nice;
3959 extern "C" int
3960 nice (int incr)
3962 return setpriority (PRIO_PROCESS, myself->pid, myself->nice + incr);
3965 static void
3966 locked_append (int fd, const void * buf, size_t size)
3968 struct flock lock_buffer = {F_WRLCK, SEEK_SET, 0, 0, 0};
3969 int count = 0;
3972 if ((lock_buffer.l_start = lseek (fd, 0, SEEK_END)) != (off_t) -1
3973 && fcntl (fd, F_SETLKW, &lock_buffer) != -1)
3975 if (lseek (fd, 0, SEEK_END) != (off_t) -1)
3976 write (fd, buf, size);
3977 lock_buffer.l_type = F_UNLCK;
3978 fcntl (fd, F_SETLK, &lock_buffer);
3979 break;
3981 while (count++ < 1000
3982 && (errno == EACCES || errno == EAGAIN)
3983 && !usleep (1000));
3986 extern "C" void
3987 updwtmp (const char *wtmp_file, const struct utmp *ut)
3989 int fd;
3991 if ((fd = open (wtmp_file, O_WRONLY | O_BINARY, 0)) >= 0)
3993 locked_append (fd, ut, sizeof *ut);
3994 close (fd);
3998 static int utmp_fd = -1;
3999 static bool utmp_readonly = false;
4000 static char *utmp_file = (char *) _PATH_UTMP;
4002 static void
4003 internal_setutent (bool force_readwrite)
4005 if (force_readwrite && utmp_readonly)
4006 endutent ();
4007 if (utmp_fd < 0)
4009 utmp_fd = open (utmp_file, O_RDWR | O_BINARY);
4010 /* If open fails, we assume an unprivileged process (who?). In this
4011 case we try again for reading only unless the process calls
4012 pututline() (==force_readwrite) in which case opening just fails. */
4013 if (utmp_fd < 0 && !force_readwrite)
4015 utmp_fd = open (utmp_file, O_RDONLY | O_BINARY);
4016 if (utmp_fd >= 0)
4017 utmp_readonly = true;
4020 else
4021 lseek (utmp_fd, 0, SEEK_SET);
4024 extern "C" void
4025 setutent ()
4027 internal_setutent (false);
4030 extern "C" void
4031 endutent ()
4033 if (utmp_fd >= 0)
4035 close (utmp_fd);
4036 utmp_fd = -1;
4037 utmp_readonly = false;
4041 extern "C" int
4042 utmpname (const char *file)
4044 __try
4046 if (*file)
4048 endutent ();
4049 utmp_file = strdup (file);
4050 if (utmp_file)
4052 debug_printf ("New UTMP file: %s", utmp_file);
4053 return 0;
4057 __except (EFAULT) {}
4058 __endtry
4059 debug_printf ("Setting UTMP file failed");
4060 return -1;
4063 /* Note: do not make NO_COPY */
4064 static struct utmp utmp_data_buf[16];
4065 static unsigned utix = 0;
4066 #define nutdbuf (sizeof (utmp_data_buf) / sizeof (utmp_data_buf[0]))
4067 #define utmp_data ({ \
4068 if (utix >= nutdbuf) \
4069 utix = 0; \
4070 utmp_data_buf + utix++; \
4073 static struct utmpx *
4074 copy_ut_to_utx (struct utmp *ut, struct utmpx *utx)
4076 if (!ut)
4077 return NULL;
4078 memcpy (utx, ut, sizeof *ut);
4079 utx->ut_tv.tv_sec = ut->ut_time;
4080 utx->ut_tv.tv_usec = 0;
4081 return utx;
4084 extern "C" struct utmp *
4085 getutent ()
4087 if (utmp_fd < 0)
4089 internal_setutent (false);
4090 if (utmp_fd < 0)
4091 return NULL;
4094 utmp *ut = utmp_data;
4095 if (read (utmp_fd, ut, sizeof *ut) != sizeof *ut)
4096 return NULL;
4097 return ut;
4100 extern "C" struct utmp *
4101 getutid (const struct utmp *id)
4103 __try
4105 if (utmp_fd < 0)
4107 internal_setutent (false);
4108 if (utmp_fd < 0)
4109 __leave;
4111 utmp *ut = utmp_data;
4112 while (read (utmp_fd, ut, sizeof *ut) == sizeof *ut)
4114 switch (id->ut_type)
4116 case RUN_LVL:
4117 case BOOT_TIME:
4118 case OLD_TIME:
4119 case NEW_TIME:
4120 if (id->ut_type == ut->ut_type)
4121 return ut;
4122 break;
4123 case INIT_PROCESS:
4124 case LOGIN_PROCESS:
4125 case USER_PROCESS:
4126 case DEAD_PROCESS:
4127 if (strncmp (id->ut_id, ut->ut_id, UT_IDLEN) == 0)
4128 return ut;
4129 break;
4130 default:
4131 break;
4135 __except (EFAULT) {}
4136 __endtry
4137 return NULL;
4140 extern "C" struct utmp *
4141 getutline (const struct utmp *line)
4143 __try
4145 if (utmp_fd < 0)
4147 internal_setutent (false);
4148 if (utmp_fd < 0)
4149 __leave;
4152 utmp *ut = utmp_data;
4153 while (read (utmp_fd, ut, sizeof *ut) == sizeof *ut)
4154 if ((ut->ut_type == LOGIN_PROCESS ||
4155 ut->ut_type == USER_PROCESS) &&
4156 !strncmp (ut->ut_line, line->ut_line, sizeof (ut->ut_line)))
4157 return ut;
4159 __except (EFAULT) {}
4160 __endtry
4161 return NULL;
4164 extern "C" struct utmp *
4165 pututline (const struct utmp *ut)
4167 __try
4169 internal_setutent (true);
4170 if (utmp_fd < 0)
4172 debug_printf ("error: utmp_fd %d", utmp_fd);
4173 __leave;
4175 debug_printf ("ut->ut_type %d, ut->ut_pid %d, ut->ut_line '%s', ut->ut_id '%s'\n",
4176 ut->ut_type, ut->ut_pid, ut->ut_line, ut->ut_id);
4177 debug_printf ("ut->ut_user '%s', ut->ut_host '%s'\n",
4178 ut->ut_user, ut->ut_host);
4180 struct utmp *u;
4181 if ((u = getutid (ut)))
4183 lseek (utmp_fd, -sizeof *ut, SEEK_CUR);
4184 write (utmp_fd, ut, sizeof *ut);
4186 else
4187 locked_append (utmp_fd, ut, sizeof *ut);
4188 /* The documentation says to return a pointer to this which implies that
4189 this has to be cast from a const. That doesn't seem right but the
4190 documentation seems pretty clear on this. */
4191 return (struct utmp *) ut;
4193 __except (EFAULT) {}
4194 __endtry
4195 return NULL;
4198 extern "C" void
4199 setutxent ()
4201 internal_setutent (false);
4204 extern "C" void
4205 endutxent ()
4207 endutent ();
4210 extern "C" struct utmpx *
4211 getutxent ()
4213 /* POSIX: Not required to be thread safe. */
4214 static struct utmpx utx;
4215 return copy_ut_to_utx (getutent (), &utx);
4218 extern "C" struct utmpx *
4219 getutxid (const struct utmpx *id)
4221 /* POSIX: Not required to be thread safe. */
4222 static struct utmpx utx;
4224 __try
4226 ((struct utmpx *)id)->ut_time = id->ut_tv.tv_sec;
4227 return copy_ut_to_utx (getutid ((struct utmp *) id), &utx);
4229 __except (EFAULT) {}
4230 __endtry
4231 return NULL;
4234 extern "C" struct utmpx *
4235 getutxline (const struct utmpx *line)
4237 /* POSIX: Not required to be thread safe. */
4238 static struct utmpx utx;
4240 __try
4242 ((struct utmpx *)line)->ut_time = line->ut_tv.tv_sec;
4243 return copy_ut_to_utx (getutline ((struct utmp *) line), &utx);
4245 __except (EFAULT) {}
4246 __endtry
4247 return NULL;
4250 extern "C" struct utmpx *
4251 pututxline (const struct utmpx *utmpx)
4253 /* POSIX: Not required to be thread safe. */
4254 static struct utmpx utx;
4256 __try
4258 ((struct utmpx *)utmpx)->ut_time = utmpx->ut_tv.tv_sec;
4259 return copy_ut_to_utx (pututline ((struct utmp *) utmpx), &utx);
4261 __except (EFAULT) {}
4262 __endtry
4263 return NULL;
4266 extern "C" void
4267 updwtmpx (const char *wtmpx_file, const struct utmpx *utmpx)
4269 ((struct utmpx *)utmpx)->ut_time = utmpx->ut_tv.tv_sec;
4270 updwtmp (wtmpx_file, (const struct utmp *) utmpx);
4273 extern "C" long
4274 gethostid (void)
4276 /* Fetch the globally unique MachineGuid value from
4277 HKLM/Software/Microsoft/Cryptography and hash it. */
4278 int32_t hostid = 0x40291372; /* Choose a nice start value */
4279 WCHAR wguid[38];
4281 reg_key key (HKEY_LOCAL_MACHINE, KEY_READ,
4282 L"SOFTWARE", L"Microsoft", L"Cryptography", NULL);
4283 key.get_string (L"MachineGuid", wguid, 38,
4284 L"00000000-0000-0000-0000-000000000000");
4285 /* SDBM hash */
4286 for (PWCHAR wp = wguid; *wp; ++wp)
4287 hostid = *wp + (hostid << 6) + (hostid << 16) - hostid;
4288 debug_printf ("hostid %08y from MachineGuid %W", hostid, wguid);
4289 return (int32_t) hostid; /* Avoid sign extension. */
4292 #define ETC_SHELLS "/etc/shells"
4293 static int shell_index;
4294 static FILE *shell_fp;
4296 extern "C" char *
4297 getusershell ()
4299 /* List of default shells if no /etc/shells exists, defined as on Linux.
4300 FIXME: SunOS has a far longer list, containing all shells which
4301 might be shipped with the OS. Should we do the same for the Cygwin
4302 distro, adding bash, tcsh, ksh, pdksh and zsh? */
4303 static const char *def_shells[] = {
4304 "/bin/sh",
4305 "/bin/csh",
4306 "/usr/bin/sh",
4307 "/usr/bin/csh",
4308 NULL
4310 static char buf[PATH_MAX];
4311 int ch, buf_idx;
4313 if (!shell_fp && !(shell_fp = fopen (ETC_SHELLS, "rt")))
4315 if (def_shells[shell_index])
4316 return strcpy (buf, def_shells[shell_index++]);
4317 return NULL;
4319 /* Skip white space characters. */
4320 while ((ch = getc (shell_fp)) != EOF && isspace (ch))
4322 /* Get each non-whitespace character as part of the shell path as long as
4323 it fits in buf. */
4324 for (buf_idx = 0;
4325 ch != EOF && !isspace (ch) && buf_idx < (PATH_MAX - 1);
4326 buf_idx++, ch = getc (shell_fp))
4327 buf[buf_idx] = ch;
4328 /* Skip any trailing non-whitespace character not fitting in buf. If the
4329 path is longer than PATH_MAX, it's invalid anyway. */
4330 while (ch != EOF && !isspace (ch))
4331 ch = getc (shell_fp);
4332 if (buf_idx)
4334 buf[buf_idx] = '\0';
4335 return buf;
4337 return NULL;
4340 extern "C" void
4341 setusershell ()
4343 if (shell_fp)
4344 fseek (shell_fp, 0L, SEEK_SET);
4345 shell_index = 0;
4348 extern "C" void
4349 endusershell ()
4351 if (shell_fp)
4353 fclose (shell_fp);
4354 shell_fp = NULL;
4356 shell_index = 0;
4359 extern "C" void
4360 flockfile (FILE *file)
4362 _flockfile (file);
4365 extern "C" int
4366 ftrylockfile (FILE *file)
4368 return _ftrylockfile (file);
4371 extern "C" void
4372 funlockfile (FILE *file)
4374 _funlockfile (file);
4377 extern "C" FILE *
4378 popen (const char *command, const char *in_type)
4380 const char *type = in_type;
4381 char fdopen_flags[3] = "\0\0";
4382 int pipe_flags = 0;
4384 #define rw fdopen_flags[0]
4385 #define bintext fdopen_flags[1]
4387 /* Sanity check. GLibc allows any order and any number of repetition,
4388 as long as the string doesn't contradict itself. We do the same here. */
4389 while (*type)
4391 if (*type == 'r' || *type == 'w')
4393 if (rw && rw != *type)
4394 break;
4395 rw = *type++;
4397 else if (*type == 'b' || *type == 't')
4399 if (bintext && bintext != *type)
4400 break;
4401 bintext = *type++;
4403 else if (*type == 'e')
4405 pipe_flags = O_CLOEXEC;
4406 ++type;
4408 else
4409 break;
4411 if ((rw != 'r' && rw != 'w') || (*type != '\0'))
4413 set_errno (EINVAL);
4414 return NULL;
4417 int fds[2];
4418 if (pipe2 (fds, pipe_flags) < 0)
4419 return NULL;
4421 int myix = rw == 'r' ? 0 : 1;
4423 lock_process now;
4424 FILE *fp = fdopen (fds[myix], fdopen_flags);
4425 if (fp)
4427 /* If fds are in the range of stdin/stdout/stderr, move them
4428 out of the way (possibly temporarily). Otherwise, spawn_guts
4429 will be confused. We do this here rather than adding logic to
4430 spawn_guts because spawn_guts is likely to be a more frequently
4431 used routine and having stdin/stdout/stderr closed and reassigned
4432 to pipe handles is an unlikely event. */
4433 int orig_fds[2] = {fds[0], fds[1]};
4434 for (int i = 0; i < 2; i++)
4435 if (fds[i] <= 2)
4437 cygheap_fdnew newfd(3);
4438 cygheap->fdtab.move_fd (fds[i], newfd);
4439 fds[i] = newfd;
4442 int myfd = fds[myix]; /* myfd - convenience variable for manipulation
4443 of the "parent" end of the pipe. */
4444 int stdchild = myix ^ 1; /* stdchild denotes the index into fd for the
4445 handle which will be redirected to
4446 stdin/stdout */
4447 int __std[2];
4448 __std[myix] = -1; /* -1 means don't pass this fd to the child
4449 process */
4450 __std[stdchild] = fds[stdchild]; /* Do pass this as the std handle */
4452 const char *argv[4] =
4454 "/bin/sh",
4455 "-c",
4456 command,
4457 NULL
4460 /* With 'e' flag given, we have to revert the close-on-exec on the child
4461 end of the pipe. Otherwise don't pass our end of the pipe to the
4462 child process. */
4463 if (pipe_flags & O_CLOEXEC)
4464 fcntl (__std[stdchild], F_SETFD, 0);
4465 else
4466 fcntl (myfd, F_SETFD, FD_CLOEXEC);
4468 /* Also don't pass the file handle currently associated with stdin/stdout
4469 to the child. This function may actually fail if the stdchild fd
4470 is closed. But that's ok. */
4471 int stdchild_state = fcntl (stdchild, F_GETFD, 0);
4472 fcntl (stdchild, F_SETFD, stdchild_state | FD_CLOEXEC);
4474 /* Start a shell process to run the given command without forking. */
4475 pid_t pid = ch_spawn.worker ("/bin/sh", argv, environ, _P_NOWAIT,
4476 __std[0], __std[1]);
4478 /* Reinstate the close-on-exec state */
4479 fcntl (stdchild, F_SETFD, stdchild_state);
4481 /* If pid >= 0 then spawn_guts succeeded. */
4482 if (pid >= 0)
4484 close (fds[stdchild]); /* Close the child end of the pipe. */
4485 /* Move the fd back to its original slot if it has been moved since
4486 we're always supposed to open the lowest numbered available fd
4487 and, if fds[mix] != orig_fds[myix] then orig_fds[myix] is
4488 presumably lower. */
4489 if (fds[myix] != orig_fds[myix])
4490 cygheap->fdtab.move_fd (fds[myix], myfd = orig_fds[myix]);
4491 fhandler_pipe *fh = (fhandler_pipe *) cygheap->fdtab[myfd];
4492 /* Flag that this handle is associated with popen. */
4493 fh->set_popen_pid (pid);
4494 return fp;
4498 /* If we reach here we've seen an error but the pipe handles are open.
4499 Close them and return NULL. */
4500 int save_errno = get_errno ();
4501 if (fp)
4503 /* Must fclose fp to avoid memory leak. */
4504 fclose (fp);
4505 close (fds[myix ^ 1]);
4507 else
4509 close (fds[0]);
4510 close (fds[1]);
4512 set_errno (save_errno);
4514 #undef rw
4515 #undef bintext
4517 return NULL;
4521 pclose (FILE *fp)
4523 fhandler_pipe *fh = (fhandler_pipe *) cygheap->fdtab[fileno(fp)];
4525 if (fh->get_device () != FH_PIPEW && fh->get_device () != FH_PIPER)
4527 set_errno (EBADF);
4528 return -1;
4531 int pid = fh->get_popen_pid ();
4532 if (!pid)
4534 set_errno (ECHILD);
4535 return -1;
4538 if (fclose (fp))
4539 return -1;
4541 int status;
4542 while (1)
4543 if (waitpid (pid, &status, 0) == pid)
4544 break;
4545 else if (get_errno () == EINTR)
4546 continue;
4547 else
4548 return -1;
4550 return status;
4553 /* Preliminary(?) implementation of the openat family of functions. */
4555 static int
4556 gen_full_path_at (char *path_ret, int dirfd, const char *pathname,
4557 int flags = 0)
4559 /* futimesat allows a NULL pathname. */
4560 if (!pathname && !(flags & _AT_NULL_PATHNAME_ALLOWED))
4562 set_errno (EFAULT);
4563 return -1;
4565 if (pathname && isabspath_strict (pathname))
4566 stpcpy (path_ret, pathname);
4567 else
4569 char *p;
4571 if (dirfd == AT_FDCWD)
4573 cwdstuff::acquire_read ();
4574 p = stpcpy (path_ret, cygheap->cwd.get_posix ());
4575 cwdstuff::release_read ();
4577 else
4579 cygheap_fdget cfd (dirfd);
4580 if (cfd < 0)
4581 return -1;
4582 if (!cfd->pc.isdir () && !(flags & AT_EMPTY_PATH))
4584 set_errno (ENOTDIR);
4585 return -1;
4587 p = stpcpy (path_ret, cfd->get_name ());
4589 if (pathname)
4591 if (!*pathname)
4593 if (flags & AT_EMPTY_PATH)
4594 return 0;
4595 set_errno (ENOENT);
4596 return -1;
4598 if (strlen (pathname) >= PATH_MAX)
4600 set_errno (ENAMETOOLONG);
4601 return -1;
4603 if (p[-1] != '/')
4604 *p++ = '/';
4605 stpcpy (p, pathname);
4608 return 0;
4611 extern "C" int
4612 openat (int dirfd, const char *pathname, int flags, ...)
4614 tmp_pathbuf tp;
4615 __try
4617 char *path = tp.c_get ();
4618 if (gen_full_path_at (path, dirfd, pathname))
4619 __leave;
4621 va_list ap;
4622 mode_t mode;
4624 va_start (ap, flags);
4625 mode = va_arg (ap, mode_t);
4626 va_end (ap);
4627 return open (path, flags, mode);
4629 __except (EFAULT) {}
4630 __endtry
4631 return -1;
4634 extern "C" int
4635 faccessat (int dirfd, const char *pathname, int mode, int flags)
4637 tmp_pathbuf tp;
4638 int res = -1;
4640 __try
4642 char *path = tp.c_get ();
4643 if (!gen_full_path_at (path, dirfd, pathname))
4645 if ((mode & ~(F_OK|R_OK|W_OK|X_OK))
4646 || (flags & ~(AT_SYMLINK_NOFOLLOW|AT_EACCESS)))
4647 set_errno (EINVAL);
4648 else
4650 fhandler_base *fh = build_fh_name (path,
4651 (flags & AT_SYMLINK_NOFOLLOW
4652 ? PC_SYM_NOFOLLOW
4653 : PC_SYM_FOLLOW)
4654 | PC_KEEP_HANDLE,
4655 stat_suffixes);
4656 if (fh)
4658 res = fh->fhaccess (mode, !!(flags & AT_EACCESS));
4659 delete fh;
4664 __except (EFAULT) {}
4665 __endtry
4666 debug_printf ("returning %d", res);
4667 return res;
4670 extern "C" int
4671 fchmodat (int dirfd, const char *pathname, mode_t mode, int flags)
4673 tmp_pathbuf tp;
4674 __try
4676 if (flags & ~AT_SYMLINK_NOFOLLOW)
4678 set_errno (EINVAL);
4679 __leave;
4681 char *path = tp.c_get ();
4682 if (gen_full_path_at (path, dirfd, pathname))
4683 __leave;
4684 if (flags & AT_SYMLINK_NOFOLLOW)
4686 /* BSD has lchmod, but Linux does not. POSIX says
4687 AT_SYMLINK_NOFOLLOW is allowed to fail on symlinks.
4688 Linux blindly fails even for non-symlinks, but we allow
4689 it to succeed. */
4690 path_conv pc (path, PC_SYM_NOFOLLOW, stat_suffixes);
4691 if (pc.issymlink ())
4693 set_errno (EOPNOTSUPP);
4694 __leave;
4697 return chmod (path, mode);
4699 __except (EFAULT) {}
4700 __endtry
4701 return -1;
4704 extern "C" int
4705 fchownat (int dirfd, const char *pathname, uid_t uid, gid_t gid, int flags)
4707 tmp_pathbuf tp;
4708 __try
4710 if (flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH))
4712 set_errno (EINVAL);
4713 __leave;
4715 char *path = tp.c_get ();
4716 int res = gen_full_path_at (path, dirfd, pathname, flags);
4717 if (res)
4718 __leave;
4719 if (!*pathname) /* Implies AT_EMPTY_PATH */
4721 /* If dirfd refers to a symlink (which was necessarily opened with
4722 O_PATH | O_NOFOLLOW), we must operate directly on that symlink. */
4723 flags = AT_SYMLINK_NOFOLLOW;
4725 return chown_worker (path, (flags & AT_SYMLINK_NOFOLLOW)
4726 ? PC_SYM_NOFOLLOW : PC_SYM_FOLLOW, uid, gid);
4728 __except (EFAULT) {}
4729 __endtry
4730 return -1;
4733 extern "C" int
4734 fstatat (int dirfd, const char *__restrict pathname, struct stat *__restrict st,
4735 int flags)
4737 tmp_pathbuf tp;
4738 __try
4740 if (flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH))
4742 set_errno (EINVAL);
4743 __leave;
4745 char *path = tp.c_get ();
4746 int res = gen_full_path_at (path, dirfd, pathname, flags);
4747 if (res)
4748 __leave;
4749 path_conv pc (path, ((flags & AT_SYMLINK_NOFOLLOW)
4750 ? PC_SYM_NOFOLLOW : PC_SYM_FOLLOW)
4751 | PC_POSIX | PC_KEEP_HANDLE, stat_suffixes);
4752 return stat_worker (pc, st);
4754 __except (EFAULT) {}
4755 __endtry
4756 return -1;
4759 extern int utimens_worker (path_conv &, const struct timespec *);
4761 extern "C" int
4762 utimensat (int dirfd, const char *pathname, const struct timespec *times,
4763 int flags)
4765 tmp_pathbuf tp;
4766 __try
4768 char *path = tp.c_get ();
4769 if (flags & ~AT_SYMLINK_NOFOLLOW)
4771 set_errno (EINVAL);
4772 __leave;
4774 if (gen_full_path_at (path, dirfd, pathname))
4775 __leave;
4776 path_conv win32 (path, PC_POSIX | ((flags & AT_SYMLINK_NOFOLLOW)
4777 ? PC_SYM_NOFOLLOW : PC_SYM_FOLLOW),
4778 stat_suffixes);
4779 return utimens_worker (win32, times);
4781 __except (EFAULT) {}
4782 __endtry
4783 return -1;
4786 extern "C" int
4787 futimesat (int dirfd, const char *pathname, const struct timeval times[2])
4789 tmp_pathbuf tp;
4790 __try
4792 char *path = tp.c_get ();
4793 if (gen_full_path_at (path, dirfd, pathname, _AT_NULL_PATHNAME_ALLOWED))
4794 __leave;
4795 return utimes (path, times);
4797 __except (EFAULT) {}
4798 __endtry
4799 return -1;
4802 extern "C" int
4803 linkat (int olddirfd, const char *oldpathname,
4804 int newdirfd, const char *newpathname,
4805 int flags)
4807 tmp_pathbuf tp;
4808 fhandler_base *fh = NULL;
4810 __try
4812 if (flags & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH))
4814 set_errno (EINVAL);
4815 __leave;
4817 char *oldpath = tp.c_get ();
4818 if ((flags & AT_EMPTY_PATH) && oldpathname && oldpathname[0] == '\0')
4820 /* Operate directly on olddirfd, which can be anything
4821 except a directory. */
4822 if (olddirfd == AT_FDCWD)
4824 set_errno (EPERM);
4825 __leave;
4827 cygheap_fdget cfd (olddirfd);
4828 if (cfd < 0)
4829 __leave;
4830 if (cfd->pc.isdir ())
4832 set_errno (EPERM);
4833 __leave;
4835 fh = cfd;
4836 flags = 0; /* In case AT_SYMLINK_FOLLOW was set. */
4838 else if (gen_full_path_at (oldpath, olddirfd, oldpathname))
4839 __leave;
4840 char *newpath = tp.c_get ();
4841 if (gen_full_path_at (newpath, newdirfd, newpathname))
4842 __leave;
4843 if (flags & AT_SYMLINK_FOLLOW)
4845 path_conv old_name (oldpath,
4846 PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_PROCFD | PC_POSIX,
4847 stat_suffixes);
4848 if (old_name.error)
4850 set_errno (old_name.error);
4851 __leave;
4853 strcpy (oldpath, old_name.get_posix ());
4855 if (fh)
4856 return fh->link (newpath);
4857 return link (oldpath, newpath);
4859 __except (EFAULT) {}
4860 __endtry
4861 return -1;
4864 extern "C" int
4865 mkdirat (int dirfd, const char *pathname, mode_t mode)
4867 tmp_pathbuf tp;
4868 __try
4870 char *path = tp.c_get ();
4871 if (gen_full_path_at (path, dirfd, pathname))
4872 __leave;
4873 return mkdir (path, mode);
4875 __except (EFAULT) {}
4876 __endtry
4877 return -1;
4880 extern "C" int
4881 mkfifoat (int dirfd, const char *pathname, mode_t mode)
4883 tmp_pathbuf tp;
4884 __try
4886 char *path = tp.c_get ();
4887 if (gen_full_path_at (path, dirfd, pathname))
4888 __leave;
4889 return mkfifo (path, mode);
4891 __except (EFAULT) {}
4892 __endtry
4893 return -1;
4896 extern "C" int
4897 mknodat (int dirfd, const char *pathname, mode_t mode, dev_t dev)
4899 tmp_pathbuf tp;
4900 __try
4902 char *path = tp.c_get ();
4903 if (gen_full_path_at (path, dirfd, pathname))
4904 __leave;
4905 return mknod (path, mode, dev);
4907 __except (EFAULT) {}
4908 __endtry
4909 return -1;
4912 extern "C" ssize_t
4913 readlinkat (int dirfd, const char *__restrict pathname, char *__restrict buf,
4914 size_t bufsize)
4916 tmp_pathbuf tp;
4917 __try
4919 char *path = tp.c_get ();
4920 int save_errno = errno;
4921 int res = gen_full_path_at (path, dirfd, pathname);
4922 if (res)
4924 if (errno != ENOENT && errno != ENOTDIR)
4925 __leave;
4926 /* pathname is an empty string. This is OK if dirfd refers
4927 to a symlink that was opened with O_PATH | O_NOFOLLOW.
4928 In this case, readlinkat operates on the symlink.
4929 Don't propagate errors from gen_full_path_at after this point. */
4930 errno = save_errno;
4931 cygheap_fdget cfd (dirfd);
4932 if (cfd < 0
4933 || (!(cfd->issymlink ()
4934 && cfd->get_flags () & O_PATH
4935 && cfd->get_flags () & O_NOFOLLOW)))
4937 set_errno (ENOENT);
4938 __leave;
4940 strcpy (path, cfd->get_name ());
4942 return readlink (path, buf, bufsize);
4944 __except (EFAULT) {}
4945 __endtry
4946 return -1;
4949 extern "C" int
4950 renameat2 (int olddirfd, const char *oldpathname,
4951 int newdirfd, const char *newpathname, unsigned int flags)
4953 tmp_pathbuf tp;
4954 __try
4956 char *oldpath = tp.c_get ();
4957 if (gen_full_path_at (oldpath, olddirfd, oldpathname))
4958 __leave;
4959 char *newpath = tp.c_get ();
4960 if (gen_full_path_at (newpath, newdirfd, newpathname))
4961 __leave;
4962 return rename2 (oldpath, newpath, flags);
4964 __except (EFAULT) {}
4965 __endtry
4966 return -1;
4969 extern "C" int
4970 renameat (int olddirfd, const char *oldpathname,
4971 int newdirfd, const char *newpathname)
4973 return renameat2 (olddirfd, oldpathname, newdirfd, newpathname, 0);
4976 extern "C" int
4977 scandirat (int dirfd, const char *pathname, struct dirent ***namelist,
4978 int (*select) (const struct dirent *),
4979 int (*compar) (const struct dirent **, const struct dirent **))
4981 tmp_pathbuf tp;
4982 __try
4984 char *path = tp.c_get ();
4985 if (gen_full_path_at (path, dirfd, pathname))
4986 __leave;
4987 return scandir (path, namelist, select, compar);
4989 __except (EFAULT) {}
4990 __endtry
4991 return -1;
4994 extern "C" int
4995 symlinkat (const char *oldpath, int newdirfd, const char *newpathname)
4997 tmp_pathbuf tp;
4998 __try
5000 char *newpath = tp.c_get ();
5001 if (gen_full_path_at (newpath, newdirfd, newpathname))
5002 __leave;
5003 return symlink (oldpath, newpath);
5005 __except (EFAULT) {}
5006 __endtry
5007 return -1;
5010 extern "C" int
5011 unlinkat (int dirfd, const char *pathname, int flags)
5013 tmp_pathbuf tp;
5014 __try
5016 if (flags & ~AT_REMOVEDIR)
5018 set_errno (EINVAL);
5019 __leave;
5021 char *path = tp.c_get ();
5022 if (gen_full_path_at (path, dirfd, pathname))
5023 __leave;
5024 return (flags & AT_REMOVEDIR) ? rmdir (path) : unlink (path);
5026 __except (EFAULT) {}
5027 __endtry
5028 return -1;
5031 static int
5032 pipe_worker (int filedes[2], unsigned int psize, int mode)
5034 fhandler_pipe *fhs[2];
5035 int res = fhandler_pipe::create (fhs, psize, mode);
5036 if (!res)
5038 cygheap_fdnew fdin;
5039 cygheap_fdnew fdout (fdin, false);
5040 char buf[sizeof ("pipe:[9223372036854775807]")];
5041 __small_sprintf (buf, "pipe:[%D]", fhs[0]->get_plain_ino ());
5042 fhs[0]->pc.set_posix (buf);
5043 __small_sprintf (buf, "pipe:[%D]", fhs[1]->get_plain_ino ());
5044 fhs[1]->pc.set_posix (buf);
5045 fdin = fhs[0];
5046 fdout = fhs[1];
5047 filedes[0] = fdin;
5048 filedes[1] = fdout;
5050 return res;
5053 /* MS compatible version of pipe. Hopefully nobody is using it... */
5054 extern "C" int
5055 _pipe (int filedes[2], unsigned int psize, int mode)
5057 int res = pipe_worker (filedes, psize, mode);
5058 int read, write;
5059 if (res != 0)
5060 read = write = -1;
5061 else
5063 read = filedes[0];
5064 write = filedes[1];
5066 syscall_printf ("%R = _pipe([%d, %d], %u, %y)", res, read, write, psize, mode);
5067 return res;
5070 extern "C" int
5071 pipe (int filedes[2])
5073 int res = pipe_worker (filedes, DEFAULT_PIPEBUFSIZE, O_BINARY);
5074 int read, write;
5075 if (res != 0)
5076 read = write = -1;
5077 else
5079 read = filedes[0];
5080 write = filedes[1];
5082 syscall_printf ("%R = pipe([%d, %d])", res, read, write);
5083 return res;
5086 extern "C" int
5087 pipe2 (int filedes[2], int mode)
5089 int res = pipe_worker (filedes, DEFAULT_PIPEBUFSIZE, mode);
5090 int read, write;
5091 if (res != 0)
5092 read = write = -1;
5093 else
5095 read = filedes[0];
5096 write = filedes[1];
5098 syscall_printf ("%R = pipe2([%d, %d], %y)", res, read, write, mode);
5099 return res;
5102 extern "C" FILE *
5103 tmpfile (void)
5105 char *dir = getenv ("TMPDIR");
5106 if (!dir)
5107 dir = P_tmpdir;
5108 int fd = open (dir, O_RDWR | O_BINARY | O_TMPFILE, S_IRUSR | S_IWUSR);
5109 if (fd < 0)
5110 return NULL;
5111 FILE *fp = fdopen (fd, "wb+");
5112 int e = errno;
5113 if (!fp)
5114 close (fd); // ..will remove tmp file
5115 set_errno (e);
5116 return fp;
5119 EXPORT_ALIAS (close, _close)
5120 EXPORT_ALIAS (fsync, fdatasync)
5121 EXPORT_ALIAS (isatty, _isatty)
5122 EXPORT_ALIAS (lseek, _lseek)
5123 EXPORT_ALIAS (open, _open)
5124 EXPORT_ALIAS (read, _read)
5125 EXPORT_ALIAS (utmpname, utmpxname)
5126 EXPORT_ALIAS (write, _write)