diff options
Diffstat (limited to 'winsup/cygwin/syscalls.cc')
-rw-r--r-- | winsup/cygwin/syscalls.cc | 267 |
1 files changed, 202 insertions, 65 deletions
diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc index 89cff28f9..2ac5f267f 100644 --- a/winsup/cygwin/syscalls.cc +++ b/winsup/cygwin/syscalls.cc @@ -139,94 +139,231 @@ dup2 (int oldfd, int newfd) return cygheap->fdtab.dup2 (oldfd, newfd); } +static char desktop_ini[] = + "[.ShellClassInfo]\r\nCLSID={645FF040-5081-101B-9F08-00AA002F954E}\r\n"; +static BYTE info2[] = +{ + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + static void try_to_bin (path_conv &win32_path, HANDLE h) { NTSTATUS status; + OBJECT_ATTRIBUTES attr; IO_STATUS_BLOCK io; - char recycler[CYG_MAX_PATH + 20]; + HANDLE rootdir = NULL, recyclerdir = NULL; + USHORT recycler_base_len = 0, recycler_user_len = 0; + UNICODE_STRING root, recycler, fname; + WCHAR recyclerbuf[NAME_MAX + 1]; /* Enough for recycler + SID + filename */ + PFILE_NAME_INFORMATION pfni; + PFILE_INTERNAL_INFORMATION pfii; + PFILE_RENAME_INFORMATION pfri; + BYTE infobuf[sizeof (FILE_NAME_INFORMATION ) + 32767 * sizeof (WCHAR)]; - rootdir (win32_path, recycler); - char *c = recycler + strlen (recycler); - if (wincap.has_recycle_dot_bin ()) + pfni = (PFILE_NAME_INFORMATION) infobuf; + status = NtQueryInformationFile (h, &io, pfni, sizeof infobuf, + FileNameInformation); + if (!NT_SUCCESS (status)) { - strcpy (c, "$Recycle.Bin"); /* NTFS and FAT since Vista */ - c += 12; + debug_printf ("NtQueryInformationFile (FileNameInformation) failed, %08x", + status); + goto out; } - else if (win32_path.fs_is_ntfs ()) + /* The filename could change, the parent dir not. So we split both paths + and take the prefix. However, there are two special cases: + - The handle refers to the root dir of the volume. + - The handle refers to the recycler or a subdir. + Both cases are handled by just returning and not even trying to move + them into the recycler. */ + if (pfni->FileNameLength == 2) /* root dir. */ + goto out; + /* Initialize recycler path. */ + RtlInitEmptyUnicodeString (&recycler, recyclerbuf, sizeof recyclerbuf); + if (wincap.has_recycle_dot_bin ()) /* NTFS and FAT since Vista */ + RtlAppendUnicodeToString (&recycler, L"\\$Recycle.Bin\\"); + else if (win32_path.fs_is_ntfs ()) /* NTFS up to 2K3 */ + RtlAppendUnicodeToString (&recycler, L"\\RECYCLER\\"); + else if (win32_path.fs_is_fat ()) /* FAT up to 2K3 */ + RtlAppendUnicodeToString (&recycler, L"\\Recycled\\"); + else + goto out; + /* Is the file a subdir of the recycler? */ + RtlInitCountedUnicodeString(&fname, pfni->FileName, pfni->FileNameLength); + if (RtlEqualUnicodePathPrefix (&fname, recycler.Buffer, TRUE)) + goto out; + /* Is fname the recycler? Temporarily hide trailing backslash. */ + recycler.Length -= sizeof (WCHAR); + if (RtlEqualUnicodeString (&fname, &recycler, TRUE)) + goto out; + + /* Create root dir path from file name information. */ + RtlSplitUnicodePath (&fname, &fname, NULL); + RtlSplitUnicodePath (win32_path.get_nt_native_path (), &root, NULL); + root.Length -= fname.Length - sizeof (WCHAR); + + /* Open root directory. */ + InitializeObjectAttributes (&attr, &root, OBJ_CASE_INSENSITIVE, NULL, NULL); + status = NtOpenFile (&rootdir, FILE_TRAVERSE, &attr, &io, + FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT); + if (!NT_SUCCESS (status)) { - strcpy (c, "RECYCLER"); /* NTFS up to 2K3 */ - c += 8; + debug_printf ("NtOpenFile (%S) failed, %08x", &root, status); + goto out; } - else if (win32_path.fs_is_fat ()) + + /* Strip leading backslash */ + ++recycler.Buffer; + recycler.Length -= sizeof (WCHAR); + /* Store length of recycler base dir, should it be necessary to create it. */ + recycler_base_len = recycler.Length; + /* On NTFS the recycler dir contains user specific subdirs, which are the + actual recycle bins per user. The name if this dir is the string + representation of the user SID. */ + if (win32_path.fs_is_ntfs ()) { - strcpy (c, "Recycled"); /* FAT up to 2K3 */ - c += 8; + UNICODE_STRING sid; + WCHAR sidbuf[128]; + /* Unhide trailing backslash. */ + recycler.Length += sizeof (WCHAR); + RtlInitEmptyUnicodeString (&sid, sidbuf, sizeof sidbuf); + /* In contrast to what MSDN claims, this function is already available + since NT4. */ + RtlConvertSidToUnicodeString (&sid, cygheap->user.sid (), FALSE); + RtlAppendUnicodeStringToString (&recycler, &sid); + recycler_user_len = recycler.Length; } - else - return; - - /* Yes, we can really do that. Typically the recycle bin is created - by the first user actually using the bin. The permissions are the - default permissions propagated from the root directory. */ - if (GetFileAttributes (recycler) == INVALID_FILE_ATTRIBUTES) + /* Create hopefully unique filename. */ + RtlAppendUnicodeToString (&recycler, L"\\cyg"); + pfii = (PFILE_INTERNAL_INFORMATION) infobuf; + status = NtQueryInformationFile (h, &io, pfii, sizeof infobuf, + FileInternalInformation); + if (!NT_SUCCESS (status)) { - if (!CreateDirectory (recycler, NULL)) + debug_printf ("NtQueryInformationFile (FileInternalInformation) failed, " + "%08x", status); + goto out; + } + RtlInt64ToHexUnicodeString (pfii->FileId.QuadPart, &recycler, TRUE); + /* Shoot. */ + pfri = (PFILE_RENAME_INFORMATION) infobuf; + pfri->ReplaceIfExists = TRUE; + pfri->RootDirectory = rootdir; + pfri->FileNameLength = recycler.Length; + memcpy (pfri->FileName, recycler.Buffer, recycler.Length); + status = NtSetInformationFile (h, &io, pfri, sizeof infobuf, + FileRenameInformation); + if (status == STATUS_OBJECT_PATH_NOT_FOUND) + { + /* Ok, so the recycler and/or the recycler/SID directory don't exist. + First reopen root dir with permission to create subdirs. */ + NtClose (rootdir); + status = NtOpenFile (&rootdir, FILE_ADD_SUBDIRECTORY, &attr, &io, + FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT); + if (!NT_SUCCESS (status)) { - debug_printf ("Can't create folder %s, %E", recycler); - return; + debug_printf ("NtOpenFile (%S) failed, %08x", &recycler, status); + goto out; } - SetFileAttributes (recycler, - FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN); - } - - /* Up to Windows 2003 Server, the default settings for the top level recycle - bin are so that everybody has the right to create files in it. Starting - with Vista, users are by default not allowed to create files in that - directory, only subdirectories. Too bad, but that requires to move - files to the user's own recycler subdir. Instead of adding yet another - special case, we just move the stuff to the user's recycler, especially - since only shared files are moved at all. */ - if (win32_path.fs_is_ntfs ()) - { - *c++ = '\\'; - cygheap->user.get_windows_id (c); - while (*c) - ++c; - if (GetFileAttributes (recycler) == INVALID_FILE_ATTRIBUTES) + /* Then check if recycler exists by opening and potentially creating it. + Yes, we can really do that. Typically the recycle bin is created + by the first user actually using the bin. The permissions are the + default permissions propagated from the root directory. */ + InitializeObjectAttributes (&attr, &recycler, OBJ_CASE_INSENSITIVE, + rootdir, NULL); + recycler.Length = recycler_base_len; + status = NtCreateFile (&recyclerdir, + READ_CONTROL + | (win32_path.fs_is_ntfs () ? 0 : FILE_ADD_FILE), + &attr, &io, NULL, + FILE_ATTRIBUTE_DIRECTORY + | FILE_ATTRIBUTE_SYSTEM + | FILE_ATTRIBUTE_HIDDEN, + FILE_SHARE_VALID_FLAGS, FILE_OPEN_IF, + FILE_DIRECTORY_FILE, NULL, 0); + if (!NT_SUCCESS (status)) { - if (!CreateDirectory (recycler, - sec_user ((PSECURITY_ATTRIBUTES) alloca (1024), - cygheap->user.sid ()))) + debug_printf ("NtCreateFile (%S) failed, %08x", &recycler, status); + goto out; + } + /* Next, if necessary, check if the recycler/SID dir exists and + create it if not. */ + if (win32_path.fs_is_ntfs ()) + { + NtClose (recyclerdir); + recycler.Length = recycler_user_len; + status = NtCreateFile (&recyclerdir, READ_CONTROL | FILE_ADD_FILE, + &attr, &io, NULL, FILE_ATTRIBUTE_DIRECTORY + | FILE_ATTRIBUTE_SYSTEM + | FILE_ATTRIBUTE_HIDDEN, + FILE_SHARE_VALID_FLAGS, FILE_OPEN_IF, + FILE_DIRECTORY_FILE, NULL, 0); + if (!NT_SUCCESS (status)) + { + debug_printf ("NtCreateFile (%S) failed, %08x", + &recycler, status); + goto out; + } + } + /* The desktop.ini and INFO2 (pre-Vista) files are expected by + Windows Explorer. Otherwise, the created bin is treated as + corrupted */ + if (io.Information == FILE_CREATED) + { + HANDLE fh; + RtlInitUnicodeString (&fname, L"desktop.ini"); + InitializeObjectAttributes (&attr, &fname, OBJ_CASE_INSENSITIVE, + recyclerdir, NULL); + status = NtCreateFile (&fh, FILE_GENERIC_WRITE, &attr, &io, NULL, + FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN, + FILE_SHARE_VALID_FLAGS, FILE_CREATE, + FILE_SYNCHRONOUS_IO_NONALERT + | FILE_NON_DIRECTORY_FILE, NULL, 0); + if (!NT_SUCCESS (status)) + debug_printf ("NtCreateFile (%S) failed, %08x", &recycler, status); + else + { + status = NtWriteFile (fh, NULL, NULL, NULL, &io, desktop_ini, + sizeof desktop_ini - 1, NULL, NULL); + if (!NT_SUCCESS (status)) + debug_printf ("NtWriteFile (%S) failed, %08x", &fname, status); + NtClose (fh); + } + if (!wincap.has_recycle_dot_bin ()) /* No INFO2 file since Vista */ { - debug_printf ("Can't create folder %s, %E", recycler); - return; + RtlInitUnicodeString (&fname, L"INFO2"); + status = NtCreateFile (&fh, FILE_GENERIC_WRITE, &attr, &io, NULL, + FILE_ATTRIBUTE_ARCHIVE + | FILE_ATTRIBUTE_HIDDEN, + FILE_SHARE_VALID_FLAGS, FILE_CREATE, + FILE_SYNCHRONOUS_IO_NONALERT + | FILE_NON_DIRECTORY_FILE, NULL, 0); + if (!NT_SUCCESS (status)) + debug_printf ("NtCreateFile (%S) failed, %08x", + &recycler, status); + else + { + status = NtWriteFile (fh, NULL, NULL, NULL, &io, info2, + sizeof info2, NULL, NULL); + if (!NT_SUCCESS (status)) + debug_printf ("NtWriteFile (%S) failed, %08x", + &fname, status); + NtClose (fh); + } } - SetFileAttributes (recycler, - FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN); } + NtClose (recyclerdir); + /* Shoot again. */ + status = NtSetInformationFile (h, &io, pfri, sizeof infobuf, + FileRenameInformation); } - - /* Create hopefully unique filename. */ - __small_sprintf (c, "\\cyg%016X", hash_path_name (myself->uid, - win32_path.get_win32 ())); - c += 20; - - /* Length of the WCHAR path in bytes. */ - ULONG len = 2 * (c - recycler); - /* Choose size big enough to fit a local native NT path into it. */ - ULONG size = sizeof (FILE_RENAME_INFORMATION) + len + 10; - PFILE_RENAME_INFORMATION pfri = (PFILE_RENAME_INFORMATION) alloca (size); - - pfri->ReplaceIfExists = TRUE; - pfri->RootDirectory = NULL; - UNICODE_STRING uname = { 0, len + 10, pfri->FileName }; - get_nt_native_path (recycler, uname); - pfri->FileNameLength = uname.Length; - status = NtSetInformationFile (h, &io, pfri, size, FileRenameInformation); if (!NT_SUCCESS (status)) debug_printf ("Move %s to %s failed, status = %p", win32_path.get_win32 (), recycler, status); +out: + if (rootdir) + NtClose (rootdir); } static NTSTATUS |