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