3 Copyright 2015 Red Hat, Inc.
5 This software is a copyrighted work licensed under the terms of the
6 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
11 #include "perprocess.h"
20 #include "shared_info.h"
22 #include "child_info.h"
24 #include "exception.h"
26 #include <sys/reent.h>
33 /* Create the lastsepcount directories found in ntdirname, where
34 counting is done along path separators (including trailing ones).
35 Returns true when these directories exist afterwards, false otherways.
36 The ntdirname is used for the path-splitting. */
38 mkdirs (PWCHAR ntdirname
, int lastsepcount
)
44 PWCHAR lastsep
= wcsrchr (ntdirname
, L
'\\');
50 for (++i
; i
<= lastsepcount
; ++i
)
52 if (success
&& (i
== 0 || wcslen (wcsrchr (ntdirname
, L
'\\')) > 1))
55 RtlInitUnicodeString (&dn
, ntdirname
);
57 InitializeObjectAttributes (&oa
, &dn
, 0, NULL
, NULL
);
61 status
= NtCreateFile (&dh
, GENERIC_READ
| SYNCHRONIZE
,
62 &oa
, &iosb
, NULL
, FILE_ATTRIBUTE_NORMAL
,
63 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
66 | FILE_SYNCHRONOUS_IO_NONALERT
,
68 if (NT_SUCCESS(status
))
70 else if (status
!= STATUS_OBJECT_NAME_COLLISION
) /* already exists */
72 debug_printf ("%y = NtCreateFile (%p, dir %W)", status
, dh
, ntdirname
);
75 ntdirname
[wcslen (ntdirname
)] = L
'\\'; /* restore original value */
80 /* Recursively remove the directory specified in ntmaxpathbuf,
81 using ntmaxpathbuf as the buffer to form subsequent filenames. */
83 rmdirs (WCHAR ntmaxpathbuf
[NT_MAX_PATH
])
85 PWCHAR basebuf
= wcsrchr (ntmaxpathbuf
, L
'\\'); /* find last pathsep */
86 if (basebuf
&& *(basebuf
+1))
87 basebuf
+= wcslen (basebuf
); /* last pathsep is not trailing one */
89 basebuf
= ntmaxpathbuf
+ wcslen (ntmaxpathbuf
);
90 *basebuf
= L
'\0'; /* kill trailing pathsep, if any */
93 HANDLE hdir
= dll_list::ntopenfile (ntmaxpathbuf
, &status
,
95 FILE_DELETE_ON_CLOSE
);
96 if (hdir
== INVALID_HANDLE_VALUE
)
99 *basebuf
++ = L
'\\'; /* (re-)add trailing pathsep */
102 FILE_DIRECTORY_INFORMATION fdi
;
105 IO_STATUS_BLOCK iosb
;
107 while (NT_SUCCESS (status
= NtQueryDirectoryFile (hdir
, NULL
, NULL
, NULL
,
109 &fdibuf
, sizeof (fdibuf
),
110 FileDirectoryInformation
,
111 FALSE
, NULL
, FALSE
)))
113 PFILE_DIRECTORY_INFORMATION pfdi
= &fdibuf
.fdi
;
116 int namelen
= pfdi
->FileNameLength
/ sizeof (WCHAR
);
117 wcsncpy (basebuf
, pfdi
->FileName
, namelen
);
118 basebuf
[namelen
] = L
'\0';
120 if (pfdi
->FileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
122 if (wcscmp (basebuf
, L
".") && wcscmp (basebuf
, L
".."))
123 rmdirs (ntmaxpathbuf
);
128 RtlInitUnicodeString (&fn
, ntmaxpathbuf
);
131 unlink_nt (pc
, true); /* move to bin */
134 if (!pfdi
->NextEntryOffset
)
136 pfdi
= (PFILE_DIRECTORY_INFORMATION
)((caddr_t
)pfdi
137 + pfdi
->NextEntryOffset
);
140 if (status
!= STATUS_NO_MORE_FILES
)
141 debug_printf ("%y = NtQueryDirectoryFile (%p, io %y, info %d)",
142 status
, hdir
, iosb
.Status
, iosb
.Information
);
147 /* Get the NTFS file id for the real file behind the dll handle.
148 As we may open a wrong (or no) file while the dll is renamed,
149 we retry until we get the same file id a second time.
150 We use NtQueryVirtualMemory (MemorySectionName) for the current
151 file name, as GetModuleFileNameW () yields the as-loaded name.
152 While we have the file handle open, also read the attributes.
153 NOTE: Uses dll_list::nt_max_path_buf (). */
155 dll::stat_real_file_once ()
157 if (fii
.IndexNumber
.QuadPart
!= -1LL)
161 HANDLE fhandle
= dll_list::ntopenfile (ntname
, &fstatus
);
162 if (fhandle
== INVALID_HANDLE_VALUE
)
164 system_printf ("WARNING: Unable (ntstatus %y) to open real file %W",
169 if (!dll_list::read_fii (fhandle
, &fii
))
170 system_printf ("WARNING: Unable to read real file attributes for %W",
174 return fii
.IndexNumber
.QuadPart
!= -1LL;
177 /* easy use of NtOpenFile */
179 dll_list::ntopenfile (PCWCHAR ntname
, NTSTATUS
*pstatus
, ULONG openopts
,
180 ACCESS_MASK access
, HANDLE rootDir
)
187 if (openopts
& FILE_OPEN_BY_FILE_ID
)
188 RtlInitCountedUnicodeString (&fn
, ntname
, 8);
190 RtlInitUnicodeString (&fn
, ntname
);
192 OBJECT_ATTRIBUTES oa
;
193 InitializeObjectAttributes (&oa
, &fn
, 0, rootDir
, NULL
);
195 access
|= FILE_READ_ATTRIBUTES
;
196 if (openopts
& FILE_DELETE_ON_CLOSE
)
198 if (openopts
& FILE_DIRECTORY_FILE
)
199 access
|= FILE_LIST_DIRECTORY
;
201 openopts
|= FILE_NON_DIRECTORY_FILE
;
203 access
|= SYNCHRONIZE
;
204 openopts
|= FILE_SYNCHRONOUS_IO_NONALERT
;
206 HANDLE fh
= INVALID_HANDLE_VALUE
;
207 ULONG share
= FILE_SHARE_VALID_FLAGS
;
208 IO_STATUS_BLOCK iosb
;
209 *pstatus
= NtOpenFile (&fh
, access
, &oa
, &iosb
, share
, openopts
);
210 if (openopts
& FILE_OPEN_BY_FILE_ID
)
211 debug_printf ("%y = NtOpenFile (%p, a %xh, sh %xh, o %xh, io %y, by id %llX)",
212 *pstatus
, fh
, access
, share
, openopts
, iosb
.Status
, *(LONGLONG
*)fn
.Buffer
);
214 debug_printf ("%y = NtOpenFile (%p, a %xh, sh %xh, o %xh, io %y, '%W')",
215 *pstatus
, fh
, access
, share
, openopts
, iosb
.Status
, fn
.Buffer
);
217 return NT_SUCCESS(*pstatus
) ? fh
: INVALID_HANDLE_VALUE
;
221 dll_list::read_fii (HANDLE fh
, PFILE_INTERNAL_INFORMATION pfii
)
223 pfii
->IndexNumber
.QuadPart
= -1LL;
226 IO_STATUS_BLOCK iosb
;
227 status
= NtQueryInformationFile (fh
, &iosb
,
228 pfii
, sizeof (*pfii
),
229 FileInternalInformation
);
230 if (!NT_SUCCESS (status
))
232 system_printf ("WARNING: %y = NtQueryInformationFile (%p,"
233 " InternalInfo, io.Status %y)",
234 status
, fh
, iosb
.Status
);
235 pfii
->IndexNumber
.QuadPart
= -1LL;
241 /* Into buf if not NULL, write the IndexNumber in pli.
242 Return the number of characters (that would be) written. */
244 format_IndexNumber (PWCHAR buf
, ssize_t bufsize
, LARGE_INTEGER
const *pli
)
248 if (bufsize
>= 0 && bufsize
<= 16)
250 return __small_swprintf (buf
, L
"%016X", pli
->QuadPart
);
253 /* Into buf if not NULL, write the ntname of cygwin installation_root.
254 Return the number of characters (that would be) written. */
256 rootname (PWCHAR buf
, ssize_t bufsize
)
258 UNICODE_STRING
&cygroot
= cygheap
->installation_root
;
260 return 6 /* "\??\UN" */ + cygroot
.Length
/ sizeof (WCHAR
);
261 return dll_list::form_ntname (buf
, bufsize
, cygroot
.Buffer
) - buf
;
264 /* Into buf if not NULL, write the string representation of current user sid.
265 Return the number of characters (that would be) written. */
267 sidname (PWCHAR buf
, ssize_t bufsize
)
271 if (bufsize
>= 0 && bufsize
<= 128)
275 RtlInitEmptyUnicodeString (&sid
, sidbuf
, sizeof (sidbuf
));
276 RtlConvertSidToUnicodeString (&sid
, cygheap
->user
.sid (), FALSE
);
277 return wcpcpy (buf
, sid
.Buffer
) - buf
;
280 /* Into buf if not NULL, write the IndexNumber of the main executable.
281 Return the number of characters (that would be) written. */
283 exename (PWCHAR buf
, ssize_t bufsize
)
286 return format_IndexNumber (NULL
, bufsize
, NULL
);
287 dll
*d
= dlls
.main_executable
;
288 return format_IndexNumber (buf
, bufsize
, &d
->fii
.IndexNumber
);
291 /* Into buf if not NULL, write the current Windows Thread Identifier.
292 Return the number of characters (that would be) written. */
294 winthrname (PWCHAR buf
, ssize_t bufsize
)
297 return sizeof (DWORD
) * 4;
298 if (bufsize
>= 0 && bufsize
<= (int)sizeof (DWORD
) * 4)
301 return __small_swprintf (buf
, L
"%08X%08X",
302 GetCurrentProcessId(), GetCurrentThreadId());
306 PCWCHAR text
; /* used when no pathfunc, description otherwise */
307 int (*textfunc
)(PWCHAR buf
, ssize_t bufsize
);
308 bool mutex_from_dir
; /* on path-separators add mutex-separator */
311 /* mutex name is formed along dir names */
312 static namepart
const
313 forkable_nameparts
[] = {
314 /* text textfunc mutex_from_dir create */
315 { L
"<cygroot>", rootname
, false, false, },
316 { L
"\\var\\run\\", NULL
, false, false, },
317 { L
"cygfork", NULL
, true, false, },
318 { L
"<sid>", sidname
, true, true, },
319 { L
"<exe>", exename
, false, false, },
320 { MUTEXSEP
, NULL
, false, false, },
321 { L
"<winthr>", winthrname
, true, true, },
326 /* Nominate the hardlink to an individual DLL inside dirx_name,
327 that ends with the path separator (hence the "x" varname).
328 With NULL as dirx_name, never nominate the hardlink any more.
329 With "" as dirx_name, denominate the hardlink. */
331 dll::nominate_forkable (PCWCHAR dirx_name
)
335 debug_printf ("type %d disable %W", type
, ntname
);
336 forkable_ntname
= NULL
; /* never create a hardlink for this dll */
339 if (!forkable_ntname
)
342 PWCHAR next
= wcpcpy (forkable_ntname
, dirx_name
);
344 if (!*forkable_ntname
)
345 return; /* denominate */
347 if (type
== DLL_LOAD
)
349 /* Multiple dynamically loaded dlls can have identical basenames
350 * when loaded from different directories. But still the original
351 * basename may serve as linked dependency for another dynamically
352 * loaded dll. So we have to create a separate directory for the
353 * dynamically loaded dll - using the dll's IndexNumber as name. */
354 next
+= format_IndexNumber (next
, -1, &fii
.IndexNumber
);
355 next
= wcpcpy (next
, L
"\\");
357 wcpcpy (next
, modname
);
360 /* Create the nominated hardlink for one indivitual dll,
361 inside another subdirectory when dynamically loaded.
363 We've not found a performant way yet to protect fork against
364 updates to main executables and/or dlls that do not reside on
365 the same NTFS filesystem as the <cygroot>/var/run/cygfork/
366 directory. But as long as the main executable can be hardlinked,
367 dll redirection works for any other hardlink-able dll, while
368 non-hardlink-able dlls are used from their original location. */
370 dll::create_forkable ()
372 if (!forkable_ntname
|| !*forkable_ntname
)
373 return true; /* disabled */
375 PWCHAR ntname
= forkable_ntname
;
379 if (type
>= DLL_LOAD
)
381 last
= wcsrchr (ntname
, L
'\\');
385 success
= mkdirs (ntname
, 1);
391 /* open device as parent handle for FILE_OPEN_BY_FILE_ID */
392 PWCHAR devname
= dll_list::nt_max_path_buf ();
398 if (*d
== L
'\\' && ++pathseps
> 4)
399 break; // "\\??\\UNC\\server\\share"
406 HANDLE devhandle
= dll_list::ntopenfile (devname
);
407 if (devhandle
== INVALID_HANDLE_VALUE
)
408 return false; /* impossible */
410 int ntlen
= wcslen (ntname
);
411 int bufsize
= sizeof (FILE_LINK_INFORMATION
) + ntlen
* sizeof (*ntname
);
412 PFILE_LINK_INFORMATION pfli
= (PFILE_LINK_INFORMATION
) alloca (bufsize
);
414 wcscpy (pfli
->FileName
, ntname
);
416 pfli
->FileNameLength
= ntlen
* sizeof (*ntname
);
417 pfli
->ReplaceIfExists
= FALSE
; /* allow concurrency */
418 pfli
->RootDirectory
= NULL
;
420 /* When we get STATUS_TRANSACTION_NOT_ACTIVE from hardlink creation,
421 the current process has renamed the file while it had the readonly
422 attribute. The rename() function uses a transaction for combined
423 writeable+rename action if possible to provide atomicity.
424 Although the transaction is closed afterwards, creating a hardlink
425 for this file requires the FILE_WRITE_ATTRIBUTES access, for unknown
426 reason. On the other hand, always requesting FILE_WRITE_ATTRIBUTES
427 would fail for users that do not own the original file. */
429 int access
= 0; /* first attempt */
432 HANDLE fh
= dll_list::ntopenfile ((PCWCHAR
)&fii
.IndexNumber
, NULL
,
433 FILE_OPEN_BY_FILE_ID
,
436 if (fh
== INVALID_HANDLE_VALUE
)
437 break; /* impossible */
439 IO_STATUS_BLOCK iosb
;
440 NTSTATUS status
= NtSetInformationFile (fh
, &iosb
, pfli
, bufsize
,
441 FileLinkInformation
);
443 debug_printf ("%y = NtSetInformationFile (%p, FileLink %W, iosb.Status %y)",
444 status
, fh
, pfli
->FileName
, iosb
.Status
);
445 if (NT_SUCCESS (status
) || status
== STATUS_OBJECT_NAME_COLLISION
)
451 if (status
!= STATUS_TRANSACTION_NOT_ACTIVE
||
452 access
== FILE_WRITE_ATTRIBUTES
)
455 access
= FILE_WRITE_ATTRIBUTES
; /* second attempt */
463 /* return the number of characters necessary to store one forkable name */
465 dll_list::forkable_ntnamesize (dll_type type
, PCWCHAR fullntname
, PCWCHAR modname
)
467 /* per process, this is the first forkables-method ever called */
468 if (cygwin_shared
->forkable_hardlink_support
== 0) /* Unknown */
470 /* check existence of forkables dir */
471 /* nt_max_path_buf () is already used in dll_list::alloc.
472 But as this is run in the very first cygwin process only,
473 using some heap is not a performance issue here. */
474 PWCHAR pbuf
= (PWCHAR
) cmalloc_abort (HEAP_BUF
,
475 NT_MAX_PATH
* sizeof (WCHAR
));
477 for (namepart
const *part
= forkable_nameparts
; part
->text
; ++part
)
480 pnext
+= part
->textfunc (pnext
, -1);
482 pnext
+= __small_swprintf (pnext
, L
"%W", part
->text
);
483 if (part
->mutex_from_dir
)
484 break; /* up to first mutex-naming dir */
488 RtlInitUnicodeString (&fn
, pbuf
);
490 HANDLE dh
= INVALID_HANDLE_VALUE
;
492 if (fsi
.update (&fn
, NULL
) &&
493 /* FIXME: !fsi.is_readonly () && */
495 dh
= ntopenfile (pbuf
, NULL
, FILE_DIRECTORY_FILE
);
496 if (dh
!= INVALID_HANDLE_VALUE
)
498 cygwin_shared
->forkable_hardlink_support
= 1; /* Yes */
500 debug_printf ("enabled");
504 cygwin_shared
->forkable_hardlink_support
= -1; /* No */
505 debug_printf ("disabled, missing or not on NTFS %W", fn
.Buffer
);
510 if (!forkables_supported ())
513 if (!forkables_dirx_size
)
515 DWORD forkables_mutex_size
= 0;
516 bool needsep
= false;
517 for (namepart
const *part
= forkable_nameparts
; part
->text
; ++part
)
521 forkables_dirx_size
+= wcslen (PATHSEP
);
522 forkables_mutex_size
+= wcslen (MUTEXSEP
);
524 needsep
= part
->mutex_from_dir
;
527 len
= part
->textfunc (NULL
, 0);
529 len
= wcslen (part
->text
);
530 forkables_dirx_size
+= len
;
531 forkables_mutex_size
+= len
;
533 /* trailing path sep */
534 forkables_dirx_size
+= wcslen (PATHSEP
);
536 ++forkables_dirx_size
;
537 ++forkables_mutex_size
;
539 /* allocate here, to avoid cygheap size changes during fork */
540 forkables_dirx_ntname
= (PWCHAR
) cmalloc (HEAP_2_DLL
,
541 (forkables_dirx_size
+ forkables_mutex_size
) *
542 sizeof (*forkables_dirx_ntname
));
543 *forkables_dirx_ntname
= L
'\0';
545 forkables_mutex_name
= forkables_dirx_ntname
+ forkables_dirx_size
;
546 *forkables_mutex_name
= L
'\0';
549 size_t ret
= forkables_dirx_size
;
550 if (type
>= DLL_LOAD
)
551 ret
+= format_IndexNumber (NULL
, -1, NULL
) + 1; /* one more directory */
552 return ret
+ wcslen (modname
);
555 /* Prepare top-level names necessary to nominate individual DLL hardlinks,
556 eventually releasing/removing previous forkable hardlinks. */
558 dll_list::prepare_forkables_nomination ()
560 PWCHAR pbuf
= nt_max_path_buf ();
562 bool needsep
= false;
563 bool domutex
= false;
564 namepart
const *part
;
565 for (part
= forkable_nameparts
; part
->text
; ++part
)
567 if (part
->mutex_from_dir
)
568 domutex
= true; /* mutex naming starts with first mutex_from_dir */
572 pbuf
+= __small_swprintf (pbuf
, L
"%W", MUTEXSEP
);
573 needsep
= part
->mutex_from_dir
;
575 pbuf
+= part
->textfunc (pbuf
, -1);
577 pbuf
+= __small_swprintf (pbuf
, L
"%W", part
->text
);
580 if (!wcscmp (forkables_mutex_name
, nt_max_path_buf ()))
581 return; /* nothing changed */
583 if (*forkables_mutex_name
&&
584 wcscmp (forkables_mutex_name
, nt_max_path_buf ()))
586 /* The mutex name has changed since last fork and we either have
587 dlopen'ed a more recent or dlclose'd the most recent dll,
588 so we will not use the current forkable hardlinks any more.
589 Removing from the file system is done later, upon exit. */
591 denominate_forkables ();
593 wcscpy (forkables_mutex_name
, nt_max_path_buf ());
595 pbuf
= forkables_dirx_ntname
;
597 for (namepart
const *part
= forkable_nameparts
; part
->text
; ++part
)
600 pbuf
+= __small_swprintf (pbuf
, L
"%W", PATHSEP
);
601 needsep
= part
->mutex_from_dir
;
603 pbuf
+= part
->textfunc (pbuf
, -1);
605 pbuf
+= __small_swprintf (pbuf
, L
"%W", part
->text
);
607 pbuf
+= __small_swprintf (pbuf
, L
"%W", PATHSEP
);
609 debug_printf ("forkables dir %W", forkables_dirx_ntname
);
610 debug_printf ("forkables mutex %W", forkables_mutex_name
);
613 /* Test if creating hardlinks is necessary. If creating hardlinks is possible
614 in general, each individual dll is tested if its previously created
615 hardlink (if any, or the original file) still is the same.
616 Testing is protected against hardlink removal by concurrent processes. */
618 dll_list::update_forkables_needs ()
620 if (forkables_created
)
622 /* already have created hardlinks in this process, ... */
624 while ((d
= d
->next
) != NULL
)
625 if (d
->forkable_ntname
&& !*d
->forkable_ntname
)
627 /* ... but another dll was loaded since last fork */
628 debug_printf ("needed, since last fork loaded %W", d
->ntname
);
629 forkables_created
= false;
635 /* Create the nominated forkable hardlinks and directories as necessary,
636 mutex-protected to avoid concurrent processes removing them. */
638 dll_list::update_forkables ()
640 /* existence of mutex indicates that we use these hardlinks */
641 if (!forkables_mutex
)
643 /* neither my parent nor myself did have need for hardlinks yet */
644 forkables_mutex
= CreateMutexW (&sec_none
, FALSE
,
645 forkables_mutex_name
);
646 debug_printf ("%p = CreateMutexW (%W): %E",
647 forkables_mutex
, forkables_mutex_name
);
648 if (!forkables_mutex
)
651 /* Make sure another process does not rmdirs_synchronized () */
652 debug_printf ("WFSO (%p, %W, inf)...",
653 forkables_mutex
, forkables_mutex_name
);
654 DWORD ret
= WaitForSingleObject (forkables_mutex
, INFINITE
);
655 debug_printf ("%u = WFSO (%p, %W)",
656 ret
, forkables_mutex
, forkables_mutex_name
);
663 system_printf ("cannot wait for mutex %W: %E",
664 forkables_mutex_name
);
668 BOOL bret
= ReleaseMutex (forkables_mutex
);
669 debug_printf ("%d = ReleaseMutex (%p, %W)",
670 bret
, forkables_mutex
, forkables_mutex_name
);
673 return create_forkables ();
676 /* Create the nominated forkable hardlinks and directories as necessary,
677 as well as the .local file for dll-redirection. */
679 dll_list::create_forkables ()
683 int lastsepcount
= 1; /* we have trailing pathsep */
684 for (namepart
const *part
= forkable_nameparts
; part
->text
; ++part
)
685 if (part
->create_dir
)
688 PWCHAR ntname
= nt_max_path_buf ();
689 wcsncpy (ntname
, forkables_dirx_ntname
, NT_MAX_PATH
);
691 if (!mkdirs (ntname
, lastsepcount
))
696 /* create the DotLocal file as empty file */
697 wcsncat (ntname
, main_executable
->modname
, NT_MAX_PATH
);
698 wcsncat (ntname
, L
".local", NT_MAX_PATH
);
701 RtlInitUnicodeString (&fn
, ntname
);
703 OBJECT_ATTRIBUTES oa
;
704 InitializeObjectAttributes (&oa
, &fn
, 0, NULL
, NULL
);
705 HANDLE hlocal
= NULL
;
707 IO_STATUS_BLOCK iosb
;
708 status
= NtCreateFile (&hlocal
, GENERIC_WRITE
| SYNCHRONIZE
,
709 &oa
, &iosb
, NULL
, FILE_ATTRIBUTE_NORMAL
,
710 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
712 FILE_NON_DIRECTORY_FILE
713 | FILE_SYNCHRONOUS_IO_NONALERT
,
715 if (NT_SUCCESS (status
))
716 CloseHandle (hlocal
);
717 else if (status
!= STATUS_OBJECT_NAME_COLLISION
) /* already exists */
719 debug_printf ("%y = NtCreateFile (%p, %W)", status
, hlocal
, ntname
);
725 while ((d
= d
->next
))
726 if (!d
->create_forkable ())
727 d
->nominate_forkable (NULL
); /* never again */
728 debug_printf ("hardlinks created");
735 rmdirs_synchronized (WCHAR ntbuf
[NT_MAX_PATH
], int depth
, int maxdepth
,
736 PFILE_DIRECTORY_INFORMATION pfdi
, ULONG fdisize
)
738 if (depth
== maxdepth
)
740 debug_printf ("sync on %W", ntbuf
);
741 /* calculate mutex name from path parts, using
742 full path name length to allocate mutex name buffer */
743 WCHAR mutexname
[wcslen (ntbuf
)];
744 mutexname
[0] = L
'\0';
745 PWCHAR mutexnext
= mutexname
;
747 /* mutex name is formed by dir names */
749 for (namepart
const *part
= forkable_nameparts
; part
->text
; ++part
)
750 if (part
->mutex_from_dir
)
753 PWCHAR pathseps
[pathcount
];
755 /* along the path separators split needed path parts */
758 if ((pathseps
[i
] = wcsrchr (ntbuf
, L
'\\')))
759 *pathseps
[i
] = L
'\0';
761 return; /* something's wrong */
763 /* build the mutex name from dir names */
764 for (i
= 0; i
< pathcount
; ++i
)
767 mutexnext
= wcpcpy (mutexnext
, MUTEXSEP
);
768 mutexnext
= wcpcpy (mutexnext
, &pathseps
[i
][1]);
769 *pathseps
[i
] = L
'\\'; /* restore full path */
772 HANDLE mutex
= CreateMutexW (&sec_none_nih
, TRUE
, mutexname
);
773 DWORD lasterror
= GetLastError ();
774 debug_printf ("%p = CreateMutexW (%W): %E", mutex
, mutexname
);
777 if (lasterror
!= ERROR_ALREADY_EXISTS
)
779 debug_printf ("cleaning up for mutex %W", mutexname
);
782 BOOL bret
= CloseHandle (mutex
);
783 debug_printf ("%d = CloseHandle (%p, %W): %E",
784 bret
, mutex
, mutexname
);
789 IO_STATUS_BLOCK iosb
;
792 HANDLE hdir
= dll_list::ntopenfile (ntbuf
, &status
,
793 FILE_DIRECTORY_FILE
|
794 (depth
? FILE_DELETE_ON_CLOSE
: 0));
795 if (hdir
== INVALID_HANDLE_VALUE
)
798 PWCHAR plast
= ntbuf
+ wcslen (ntbuf
);
799 while (NT_SUCCESS (status
= NtQueryDirectoryFile (hdir
,
800 NULL
, NULL
, NULL
, &iosb
,
802 FileDirectoryInformation
,
804 if (pfdi
->FileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
806 int namelen
= pfdi
->FileNameLength
/ sizeof (WCHAR
);
807 if (!wcsncmp (pfdi
->FileName
, L
".", namelen
) ||
808 !wcsncmp (pfdi
->FileName
, L
"..", namelen
))
811 wcsncpy (plast
+1, pfdi
->FileName
, namelen
);
812 plast
[1+namelen
] = L
'\0';
813 rmdirs_synchronized (ntbuf
, depth
+1, maxdepth
, pfdi
, fdisize
);
816 if (status
!= STATUS_NO_MORE_FILES
)
817 debug_printf ("%y = NtQueryDirectoryFile (%p, io %y, info %d)",
818 status
, hdir
, iosb
.Status
, iosb
.Information
);
822 /* Try to lock the mutex handle with almost no timeout, then close the
823 mutex handle. Locking before closing is to get the mutex closing
824 promoted synchronously, otherways we might end up with no one
825 succeeding in create-with-lock, which is the precondition
826 to actually remove the hardlinks from the filesystem. */
828 dll_list::close_mutex ()
830 if (!forkables_mutex
|| !*forkables_mutex_name
)
833 HANDLE hmutex
= forkables_mutex
;
834 forkables_mutex
= NULL
;
837 DWORD ret
= WaitForSingleObject (hmutex
, 1);
838 debug_printf ("%u = WFSO (%p, %W, 1)",
839 ret
, hmutex
, forkables_mutex_name
);
849 system_printf ("error locking mutex %W: %E", forkables_mutex_name
);
852 BOOL bret
= CloseHandle (hmutex
);
853 debug_printf ("%d = CloseHandle (%p, %W): %E",
854 bret
, hmutex
, forkables_mutex_name
);
858 /* Release the forkable hardlinks, and remove them if the
859 mutex can be create-locked after locked-closing. */
861 dll_list::cleanup_forkables ()
863 if (!forkables_supported ())
866 bool locked
= close_mutex ();
868 /* Start the removal below with current forkables dir,
869 which is cleaned in denominate_forkables (). */
870 PWCHAR buf
= nt_max_path_buf ();
871 PWCHAR pathsep
= wcpncpy (buf
, forkables_dirx_ntname
, NT_MAX_PATH
);
872 buf
[NT_MAX_PATH
-1] = L
'\0';
874 denominate_forkables ();
879 /* drop last path separator */
880 while (--pathsep
>= buf
&& *pathsep
!= L
'\\');
883 try_remove_forkables (buf
, pathsep
- buf
, NT_MAX_PATH
);
887 dll_list::try_remove_forkables (PWCHAR dirbuf
, size_t dirlen
, size_t dirbufsize
)
889 /* Instead of just the current forkables, try to remove any forkables
890 found, to ensure some cleanup even in situations like power-loss. */
891 PWCHAR end
= dirbuf
+ wcslen (dirbuf
);
893 for (namepart
const *part
= forkable_nameparts
; part
->text
; ++part
)
894 if (part
->create_dir
)
896 /* drop one path separator per create_dir */
897 while (--end
>= dirbuf
&& *end
!= L
'\\');
904 /* reading one at a time to reduce stack pressure */
906 FILE_DIRECTORY_INFORMATION fdi
;
909 rmdirs_synchronized (dirbuf
, 0, backcount
, &fdibuf
.fdi
, sizeof (fdibuf
));
913 dll_list::denominate_forkables ()
915 *forkables_dirx_ntname
= L
'\0';
916 *forkables_mutex_name
= L
'\0';
919 while ((d
= d
->next
))
920 d
->nominate_forkable (forkables_dirx_ntname
);
923 /* Set or clear HANDLE_FLAG_INHERIT for all handles necessary
924 to maintain forkables-hardlinks. */
926 dll_list::set_forkables_inheritance (bool inherit
)
928 DWORD mask
= HANDLE_FLAG_INHERIT
;
929 DWORD flags
= inherit
? HANDLE_FLAG_INHERIT
: 0;
932 SetHandleInformation (forkables_mutex
, mask
, flags
);
935 /* create the forkable hardlinks, if necessary */
937 dll_list::request_forkables ()
939 if (!forkables_supported ())
942 prepare_forkables_nomination ();
944 update_forkables_needs ();
946 set_forkables_inheritance (true);
948 if (forkables_created
)
949 return; /* nothing new to create */
952 while ((d
= d
->next
))
953 d
->nominate_forkable (forkables_dirx_ntname
);
955 if (update_forkables ())
956 forkables_created
= true;
961 dll_list::release_forkables ()
963 if (!forkables_supported ())
966 set_forkables_inheritance (false);