From cbfb7b1b23fe1e382298b45cbcb214c8d088ac77 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Sun, 10 Dec 2006 16:43:30 +0000 Subject: [PATCH] * autoload.cc (SHFileOperationA): Drop definition. * ntdll.h (struct _FILE_RENAME_INFORMATION): Define. * path.cc (fs_info::update): Note length of rootdir prefix in root_len. (get_nt_native_path): New function, taking over functionality of path_conv::get_nt_native_path. (path_conv::get_nt_native_path): Just call get_nt_native_path. * path.h (get_nt_native_path): Declare. (struct fs_info): New member root_len. (fs_info::length): New inline method returning root_len. (path_conv::rootdir): New inline method returning rootdir prefix. * syscalls.cc (try_to_bin): Rewrite using only system calls. (unlink_nt): Call try_to_bin with additional handle to open file parameter. (statvfs): Use path_conv::rootdir method. * wincap.h: Define has_recycle_dot_bin throughout. * wincap.cc: Ditto. --- winsup/cygwin/ChangeLog | 20 +++++++ winsup/cygwin/autoload.cc | 1 - winsup/cygwin/ntdll.h | 7 +++ winsup/cygwin/path.cc | 11 +++- winsup/cygwin/path.h | 16 +++++ winsup/cygwin/syscalls.cc | 123 ++++++++++++++++++++++++++------------ winsup/cygwin/wincap.cc | 13 ++++ winsup/cygwin/wincap.h | 2 + 8 files changed, 151 insertions(+), 42 deletions(-) diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 79abea9bf..b947ae2df 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,23 @@ +2006-12-10 Corinna Vinschen + + * autoload.cc (SHFileOperationA): Drop definition. + * ntdll.h (struct _FILE_RENAME_INFORMATION): Define. + * path.cc (fs_info::update): Note length of rootdir prefix in + root_len. + (get_nt_native_path): New function, taking over functionality of + path_conv::get_nt_native_path. + (path_conv::get_nt_native_path): Just call get_nt_native_path. + * path.h (get_nt_native_path): Declare. + (struct fs_info): New member root_len. + (fs_info::length): New inline method returning root_len. + (path_conv::rootdir): New inline method returning rootdir prefix. + * syscalls.cc (try_to_bin): Rewrite using only system calls. + (unlink_nt): Call try_to_bin with additional handle to open file + parameter. + (statvfs): Use path_conv::rootdir method. + * wincap.h: Define has_recycle_dot_bin throughout. + * wincap.cc: Ditto. + 2006-12-10 Corinna Vinschen * fhandler.cc (rootdir): Clarify comment. diff --git a/winsup/cygwin/autoload.cc b/winsup/cygwin/autoload.cc index 2a0653174..1e7fb783a 100644 --- a/winsup/cygwin/autoload.cc +++ b/winsup/cygwin/autoload.cc @@ -527,7 +527,6 @@ LoadDLLfuncEx (Wow64DisableWow64FsRedirection, 4, kernel32, 1) LoadDLLfuncEx (Wow64RevertWow64FsRedirection, 4, kernel32, 1) LoadDLLfunc (SHGetDesktopFolder, 4, shell32) -LoadDLLfunc (SHFileOperationA, 4, shell32) LoadDLLfuncEx (waveOutGetNumDevs, 0, winmm, 1) LoadDLLfuncEx (waveOutOpen, 24, winmm, 1) diff --git a/winsup/cygwin/ntdll.h b/winsup/cygwin/ntdll.h index a72965905..180ed99b0 100644 --- a/winsup/cygwin/ntdll.h +++ b/winsup/cygwin/ntdll.h @@ -506,6 +506,13 @@ typedef struct _FILE_NAME_INFORMATION { WCHAR FileName[1]; } FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION; +typedef struct _FILE_RENAME_INFORMATION { + BOOLEAN ReplaceIfExists; + HANDLE RootDirectory; + ULONG FileNameLength; + WCHAR FileName[1]; +} FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION; + typedef struct _FILE_ALL_INFORMATION { FILE_BASIC_INFORMATION BasicInformation; FILE_STANDARD_INFORMATION StandardInformation; diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc index 49d3aecc3..9b8cd0d78 100644 --- a/winsup/cygwin/path.cc +++ b/winsup/cygwin/path.cc @@ -389,7 +389,7 @@ fs_info::update (const char *win32_path) char root_dir [CYG_MAX_PATH]; bool ret; - if (!rootdir (win32_path, root_dir)) + if (!::rootdir (win32_path, root_dir)) { debug_printf ("Cannot get root component of path %s", win32_path); clear (); @@ -411,6 +411,7 @@ fs_info::update (const char *win32_path) ++idx; } name_hash = tmp_name_hash; + root_len = strlen (root_dir); /* I have no idea why, but some machines require SeChangeNotifyPrivilege to access volume information. */ @@ -549,7 +550,7 @@ path_conv::set_normalized_path (const char *path_copy, bool strip_tail) } PUNICODE_STRING -path_conv::get_nt_native_path (UNICODE_STRING &upath) +get_nt_native_path (const char *path, UNICODE_STRING &upath) { if (path[0] != '\\') /* X:\... or NUL, etc. */ { @@ -572,6 +573,12 @@ path_conv::get_nt_native_path (UNICODE_STRING &upath) return &upath; } +PUNICODE_STRING +path_conv::get_nt_native_path (UNICODE_STRING &upath) +{ + return ::get_nt_native_path (path, upath); +} + void warn_msdos (const char *src) { diff --git a/winsup/cygwin/path.h b/winsup/cygwin/path.h index e011575f2..9b4c85ea2 100644 --- a/winsup/cygwin/path.h +++ b/winsup/cygwin/path.h @@ -15,6 +15,8 @@ details. */ #include #include +extern PUNICODE_STRING get_nt_native_path (const char *, UNICODE_STRING &); + inline bool has_attribute (DWORD attributes, DWORD attribs_to_test) { @@ -85,6 +87,7 @@ class symlink_info; struct fs_info { private: + int root_len; __ino64_t name_hash; struct status_flags { @@ -106,6 +109,7 @@ struct fs_info void clear () { name_hash = 0; + root_len = 0; flags () = serial () = 0; is_remote_drive (false); has_buggy_open (false); @@ -121,6 +125,7 @@ struct fs_info } inline DWORD& flags () {return status.flags;}; inline DWORD& serial () {return status.serial;}; + inline int length () const {return root_len;} IMPLEMENT_STATUS_FLAG (bool, is_remote_drive) IMPLEMENT_STATUS_FLAG (bool, has_buggy_open) @@ -150,6 +155,17 @@ class path_conv device dev; bool case_clash; + int rootdir (char *buf) const + { + if (!fs.length ()) + return fs.length (); + strncpy (buf, path, fs.length ()); + /* The length is always stored with trailing backslash. Make sure the + backslash is actually present in the returned path. */ + buf[fs.length () - 1] = '\\'; + buf[fs.length ()] = '\0'; + return fs.length (); + } bool isremote () const {return fs.is_remote_drive ();} bool has_acls () const {return fs.has_acls (); } bool hasgood_inode () const {return fs.hasgood_inode (); } diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc index e126588cb..8111d270d 100644 --- a/winsup/cygwin/syscalls.cc +++ b/winsup/cygwin/syscalls.cc @@ -139,47 +139,92 @@ dup2 (int oldfd, int newfd) return cygheap->fdtab.dup2 (oldfd, newfd); } -#ifndef FOF_NORECURSION -#define FOF_NORECURSION 0x1000 -#endif -#ifndef FOF_NORECURSEREPARSE -#define FOF_NORECURSEREPARSE 0x8000 -#endif - static void -try_to_bin (const char *win32_path) +try_to_bin (path_conv &win32_path, HANDLE h) { -/* TODO: Using SHFileOperation for this functionality is incredibly slow. - Given the fact that we have an open handle to the file at this - point, there must be some quicker way to move the file to the - bin or something bin-like. I keep this activated to remind me - by its slowness, that this can't go into a release version as - is. Back to the drawing board. */ + NTSTATUS status; + IO_STATUS_BLOCK io; + char recycler[CYG_MAX_PATH + 20]; - /* The op.pFrom parameter must be double \0 terminated since it's not - just a filename, but a list of filenames. If the double \0 is - missing, SHFileOperationA returns with error number 1026 (which is - not a valid system error number). */ - char file[CYG_MAX_PATH + 1] = { 0 }; - SHFILEOPSTRUCT op; - int ret; + char *c = recycler + win32_path.rootdir (recycler); + if (wincap.has_recycle_dot_bin ()) + { + strcpy (c, "$Recycle.Bin"); /* NTFS and FAT since Vista */ + c += 12; + } + else if (win32_path.fs_is_ntfs ()) + { + strcpy (c, "RECYCLER"); /* NTFS up to 2K3 */ + c += 8; + } + else if (win32_path.fs_is_fat ()) + { + strcpy (c, "Recycled"); /* FAT up to 2K3 */ + c += 8; + } + else + return; - op.hwnd = NULL; - op.wFunc = FO_DELETE; - op.pFrom = strcpy (file, win32_path); - op.pTo = NULL; - op.fFlags = FOF_ALLOWUNDO - | FOF_NOCONFIRMATION - | FOF_NOCONFIRMMKDIR - | FOF_NOERRORUI - | FOF_NORECURSION - | FOF_NORECURSEREPARSE - | FOF_SILENT; - op.fAnyOperationsAborted = FALSE; - op.hNameMappings = NULL; - op.lpszProgressTitle = NULL; - ret = SHFileOperationA (&op); - debug_printf ("SHFileOperation (%s) = %d\n", win32_path, ret); + /* 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) + { + if (!CreateDirectory (recycler, NULL)) + { + debug_printf ("Can't create folder %s, %E", recycler); + return; + } + SetFileAttributes (recycler, + FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN); + } + +#if 0 + /* The default settings for the top level recycle bin are so that + everybody has the right to create files in it. Should that be + insufficient at one point, we can enable the following code to + move the file into the user's own bin subdir. At this point, + I'm going to opt for speed, though. */ + if (win32_path.fs_is_ntfs ()) + { + *c++ = '\\'; + cygheap->user.get_windows_id (c); + while (*c) + ++c; + if (GetFileAttributes (recycler) == INVALID_FILE_ATTRIBUTES) + { + if (!CreateDirectory (recycler, + sec_user ((PSECURITY_ATTRIBUTES) alloca (1024), + cygheap->user.sid ()))) + { + debug_printf ("Can't create folder %s, %E", recycler); + return; + } + SetFileAttributes (recycler, + FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN); + } + } +#endif + + /* Create hopefully unique filename. */ + __small_sprintf (c, "\\cyg%016X", hash_path_name (myself->uid, + win32_path.get_win32 ())); + c += 20; + + /* Length of thr 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", status); } static DWORD @@ -238,7 +283,7 @@ unlink_nt (path_conv &win32_name, bool setattrs) SetFileAttributes (win32_name, (DWORD) win32_name); if (!win32_name.isremote ()) - try_to_bin (win32_name.get_win32 ()); + try_to_bin (win32_name, h); DWORD lasterr = 0; @@ -1848,7 +1893,7 @@ statvfs (const char *fname, struct statvfs *sfs) } path_conv full_path (fname, PC_SYM_FOLLOW); - if (!rootdir (full_path, root)) + if (!full_path.rootdir (root)) { set_errno (ENOTDIR); return -1; diff --git a/winsup/cygwin/wincap.cc b/winsup/cygwin/wincap.cc index 939e41585..730263415 100644 --- a/winsup/cygwin/wincap.cc +++ b/winsup/cygwin/wincap.cc @@ -71,6 +71,7 @@ static NO_COPY wincaps wincap_unknown = { has_mandatory_integrity_control:false, needs_logon_sid_in_sid_list:false, needs_count_in_si_lpres2:false, + has_recycle_dot_bin:false, }; static NO_COPY wincaps wincap_95 = { @@ -133,6 +134,7 @@ static NO_COPY wincaps wincap_95 = { has_mandatory_integrity_control:false, needs_logon_sid_in_sid_list:false, needs_count_in_si_lpres2:false, + has_recycle_dot_bin:false, }; static NO_COPY wincaps wincap_95osr2 = { @@ -195,6 +197,7 @@ static NO_COPY wincaps wincap_95osr2 = { has_mandatory_integrity_control:false, needs_logon_sid_in_sid_list:false, needs_count_in_si_lpres2:false, + has_recycle_dot_bin:false, }; static NO_COPY wincaps wincap_98 = { @@ -257,6 +260,7 @@ static NO_COPY wincaps wincap_98 = { has_mandatory_integrity_control:false, needs_logon_sid_in_sid_list:false, needs_count_in_si_lpres2:false, + has_recycle_dot_bin:false, }; static NO_COPY wincaps wincap_98se = { @@ -319,6 +323,7 @@ static NO_COPY wincaps wincap_98se = { has_mandatory_integrity_control:false, needs_logon_sid_in_sid_list:false, needs_count_in_si_lpres2:false, + has_recycle_dot_bin:false, }; static NO_COPY wincaps wincap_me = { @@ -381,6 +386,7 @@ static NO_COPY wincaps wincap_me = { has_mandatory_integrity_control:false, needs_logon_sid_in_sid_list:false, needs_count_in_si_lpres2:false, + has_recycle_dot_bin:false, }; static NO_COPY wincaps wincap_nt3 = { @@ -443,6 +449,7 @@ static NO_COPY wincaps wincap_nt3 = { has_mandatory_integrity_control:false, needs_logon_sid_in_sid_list:true, needs_count_in_si_lpres2:false, + has_recycle_dot_bin:false, }; static NO_COPY wincaps wincap_nt4 = { @@ -505,6 +512,7 @@ static NO_COPY wincaps wincap_nt4 = { has_mandatory_integrity_control:false, needs_logon_sid_in_sid_list:true, needs_count_in_si_lpres2:false, + has_recycle_dot_bin:false, }; static NO_COPY wincaps wincap_nt4sp4 = { @@ -567,6 +575,7 @@ static NO_COPY wincaps wincap_nt4sp4 = { has_mandatory_integrity_control:false, needs_logon_sid_in_sid_list:true, needs_count_in_si_lpres2:false, + has_recycle_dot_bin:false, }; static NO_COPY wincaps wincap_2000 = { @@ -629,6 +638,7 @@ static NO_COPY wincaps wincap_2000 = { has_mandatory_integrity_control:false, needs_logon_sid_in_sid_list:true, needs_count_in_si_lpres2:false, + has_recycle_dot_bin:false, }; static NO_COPY wincaps wincap_xp = { @@ -691,6 +701,7 @@ static NO_COPY wincaps wincap_xp = { has_mandatory_integrity_control:false, needs_logon_sid_in_sid_list:false, needs_count_in_si_lpres2:false, + has_recycle_dot_bin:false, }; static NO_COPY wincaps wincap_2003 = { @@ -753,6 +764,7 @@ static NO_COPY wincaps wincap_2003 = { has_mandatory_integrity_control:false, needs_logon_sid_in_sid_list:false, needs_count_in_si_lpres2:false, + has_recycle_dot_bin:false, }; static NO_COPY wincaps wincap_vista = { @@ -815,6 +827,7 @@ static NO_COPY wincaps wincap_vista = { has_mandatory_integrity_control:true, needs_logon_sid_in_sid_list:false, needs_count_in_si_lpres2:true, + has_recycle_dot_bin:true, }; wincapc wincap __attribute__((section (".cygwin_dll_common"), shared)); diff --git a/winsup/cygwin/wincap.h b/winsup/cygwin/wincap.h index 2928f8d62..896f61109 100644 --- a/winsup/cygwin/wincap.h +++ b/winsup/cygwin/wincap.h @@ -72,6 +72,7 @@ struct wincaps unsigned has_mandatory_integrity_control : 1; unsigned needs_logon_sid_in_sid_list : 1; unsigned needs_count_in_si_lpres2 : 1; + unsigned has_recycle_dot_bin : 1; }; class wincapc @@ -150,6 +151,7 @@ public: bool IMPLEMENT (has_mandatory_integrity_control) bool IMPLEMENT (needs_logon_sid_in_sid_list) bool IMPLEMENT (needs_count_in_si_lpres2) + bool IMPLEMENT (has_recycle_dot_bin) #undef IMPLEMENT };