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