* dtable.cc (dtable::dup_worker): Reset path_conv handle in duplicated
fhandler. * fhandler.cc (fhandler_base::fstatvfs): Keep handle in created path_conv. * fhandler.h (fhandler_base::get_stat_access): New method. (fhandler_base::get_stat_handle): New method. * fhandler_disk_file.cc (fhandler_base::fstat_by_handle): Use handle returned by get_stat_handle. Only request inode from system if it isn't already set in the fhandler, and only for filesystems supporting them. (fhandler_base::fstat_fs): Use handle returned by get_stat_handle. Change the way open_fs is called. Explain why. (fhandler_base::fstat_helper): Use handle returned by get_stat_handle. Never use 0 inode number. Simplify executable recognition by re-using get_stat_handle if file could be opened with sufficient rights. (fhandler_disk_file::fstatvfs): Use handle returned by get_stat_handle. (fhandler_disk_file::facl): Use handle returned by get_stat_handle in GETACL and GETACLCNT cases. (fhandler_disk_file::link): Use handle returned by get_stat_handle instead of opening file here again. Add comment. (readdir_get_ino): Keep handle in created path_conv and drop opening file. * ntdll.h (wait_pending): New helper function. * path.cc (symlink_info::check): Drop unused 'opt' parameter from declaration. Add path_conv_handle argument. (path_conv::check): Make sure conv_handle is closed. Keep PC_KEEP_HANDLE flag in pflags_or. Accommodate call to sym.check to new args. (path_conv::~path_conv): Close conv_handle. (symlink_info::check_shortcut): Don't re-open file here, just use incoming handle. Drop goto's and label out. (symlink_info::check_sysfile): Don't re-open file here, just use incoming handle. Keep track of file position to accommodate the fact that file has been opened asynchronously in calling function. (symlink_info::check_nfs_symlink): Don't re-open file here, just use incoming handle. (symlink_info::check): Drop unused 'opt' parameter. Add path_conv_handle argument. Always try to open file with GENERIC_READ rights first to allow reading file content w/o having to re-open the file. Drop back to READ_CONTROL | FILE_READ_ATTRIBUTES otherwise. Call symlink test functions (except for check_reparse_point) only if file could be opened with GENERIC_READ. Keep file handle open if PC_KEEP_HANDLE is set in pflags. * path.h (enum pathconv_arg): Add PC_KEEP_HANDLE flag. (class path_conv_handle): New class. (class path_conv): Add conv_handle member. (path_conv::operator =): Duplicate conv_handle. (path_conv::handle): New method. (path_conv::access): New method. (path_conv::reset_conv_handle): New method. (path_conv::close_conv_handle): New method.
This commit is contained in:
		| @@ -1,3 +1,57 @@ | |||||||
|  | 2010-06-15  Corinna Vinschen  <corinna@vinschen.de> | ||||||
|  |  | ||||||
|  | 	* dtable.cc (dtable::dup_worker): Reset path_conv handle in duplicated | ||||||
|  | 	fhandler. | ||||||
|  | 	* fhandler.cc (fhandler_base::fstatvfs): Keep handle in created | ||||||
|  | 	path_conv. | ||||||
|  | 	* fhandler.h (fhandler_base::get_stat_access): New method. | ||||||
|  | 	(fhandler_base::get_stat_handle): New method. | ||||||
|  | 	* fhandler_disk_file.cc (fhandler_base::fstat_by_handle): Use handle | ||||||
|  | 	returned by get_stat_handle.  Only request inode from system if it | ||||||
|  | 	isn't already set in the fhandler, and only for filesystems supporting | ||||||
|  | 	them. | ||||||
|  | 	(fhandler_base::fstat_fs): Use handle returned by get_stat_handle. | ||||||
|  | 	Change the way open_fs is called.  Explain why. | ||||||
|  | 	(fhandler_base::fstat_helper): Use handle returned by get_stat_handle. | ||||||
|  | 	Never use 0 inode number.  Simplify executable recognition by re-using | ||||||
|  | 	get_stat_handle if file could be opened with sufficient rights. | ||||||
|  | 	(fhandler_disk_file::fstatvfs): Use handle returned by get_stat_handle. | ||||||
|  | 	(fhandler_disk_file::facl): Use handle returned by get_stat_handle in | ||||||
|  | 	GETACL and GETACLCNT cases. | ||||||
|  | 	(fhandler_disk_file::link): Use handle returned by get_stat_handle | ||||||
|  | 	instead of opening file here again.  Add comment. | ||||||
|  | 	(readdir_get_ino): Keep handle in created path_conv and drop | ||||||
|  | 	opening file. | ||||||
|  | 	* ntdll.h (wait_pending): New helper function. | ||||||
|  | 	* path.cc (symlink_info::check): Drop unused 'opt' parameter from | ||||||
|  | 	declaration.  Add path_conv_handle argument. | ||||||
|  | 	(path_conv::check): Make sure conv_handle is closed.  Keep | ||||||
|  | 	PC_KEEP_HANDLE flag in pflags_or.  Accommodate call to sym.check to | ||||||
|  | 	new args. | ||||||
|  | 	(path_conv::~path_conv): Close conv_handle. | ||||||
|  | 	(symlink_info::check_shortcut): Don't re-open file here, just use | ||||||
|  | 	incoming handle.  Drop goto's and label out. | ||||||
|  | 	(symlink_info::check_sysfile): Don't re-open file here, just use | ||||||
|  | 	incoming handle.  Keep track of file position to accommodate the fact | ||||||
|  | 	that file has been opened asynchronously in calling function. | ||||||
|  | 	(symlink_info::check_nfs_symlink): Don't re-open file here, just use | ||||||
|  | 	incoming handle. | ||||||
|  | 	(symlink_info::check): Drop unused 'opt' parameter.  Add | ||||||
|  | 	path_conv_handle argument.  Always try to open file with GENERIC_READ | ||||||
|  | 	rights first to allow reading file content w/o having to re-open the | ||||||
|  | 	file.  Drop back to READ_CONTROL | FILE_READ_ATTRIBUTES otherwise. | ||||||
|  | 	Call symlink test functions (except for check_reparse_point) only if | ||||||
|  | 	file could be opened with GENERIC_READ.  Keep file handle open if | ||||||
|  | 	PC_KEEP_HANDLE is set in pflags. | ||||||
|  | 	* path.h (enum pathconv_arg): Add PC_KEEP_HANDLE flag. | ||||||
|  | 	(class path_conv_handle): New class. | ||||||
|  | 	(class path_conv): Add conv_handle member. | ||||||
|  | 	(path_conv::operator =): Duplicate conv_handle. | ||||||
|  | 	(path_conv::handle): New method. | ||||||
|  | 	(path_conv::access): New method. | ||||||
|  | 	(path_conv::reset_conv_handle): New method. | ||||||
|  | 	(path_conv::close_conv_handle): New method. | ||||||
|  |  | ||||||
| 2010-06-15  Corinna Vinschen  <corinna@vinschen.de> | 2010-06-15  Corinna Vinschen  <corinna@vinschen.de> | ||||||
|  |  | ||||||
| 	* fhandler_disk_file.cc (fhandler_disk_file::fstatvfs): Fix indentation. | 	* fhandler_disk_file.cc (fhandler_disk_file::fstatvfs): Fix indentation. | ||||||
|   | |||||||
| @@ -582,6 +582,7 @@ dtable::dup_worker (fhandler_base *oldfh, int flags) | |||||||
|     { |     { | ||||||
|       *newfh = *oldfh; |       *newfh = *oldfh; | ||||||
|       newfh->set_io_handle (NULL); |       newfh->set_io_handle (NULL); | ||||||
|  |       newfh->pc.reset_conv_handle (); | ||||||
|       if (oldfh->dup (newfh)) |       if (oldfh->dup (newfh)) | ||||||
| 	{ | 	{ | ||||||
| 	  delete newfh; | 	  delete newfh; | ||||||
|   | |||||||
| @@ -1124,7 +1124,7 @@ fhandler_base::fstatvfs (struct statvfs *sfs) | |||||||
| { | { | ||||||
|   /* If we hit this base implementation, it's some device in /dev. |   /* If we hit this base implementation, it's some device in /dev. | ||||||
|      Just call statvfs on /dev for simplicity. */ |      Just call statvfs on /dev for simplicity. */ | ||||||
|   path_conv pc ("/dev"); |   path_conv pc ("/dev", PC_KEEP_HANDLE); | ||||||
|   fhandler_disk_file fh (pc); |   fhandler_disk_file fh (pc); | ||||||
|   return fh.fstatvfs (sfs); |   return fh.fstatvfs (sfs); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -181,6 +181,7 @@ class fhandler_base | |||||||
|  |  | ||||||
|   int get_access () const { return access; } |   int get_access () const { return access; } | ||||||
|   void set_access (int x) { access = x; } |   void set_access (int x) { access = x; } | ||||||
|  |   int get_stat_access () const { return pc.handle () ? pc.access () : access; } | ||||||
|  |  | ||||||
|   int get_flags () { return openflags; } |   int get_flags () { return openflags; } | ||||||
|   void set_flags (int x, int supplied_bin = 0); |   void set_flags (int x, int supplied_bin = 0); | ||||||
| @@ -355,6 +356,7 @@ class fhandler_base | |||||||
|   virtual HANDLE& get_handle () { return io_handle; } |   virtual HANDLE& get_handle () { return io_handle; } | ||||||
|   virtual HANDLE& get_io_handle () { return io_handle; } |   virtual HANDLE& get_io_handle () { return io_handle; } | ||||||
|   virtual HANDLE& get_output_handle () { return io_handle; } |   virtual HANDLE& get_output_handle () { return io_handle; } | ||||||
|  |   virtual HANDLE get_stat_handle () { return pc.handle () ?: io_handle; } | ||||||
|   virtual bool hit_eof () {return false;} |   virtual bool hit_eof () {return false;} | ||||||
|   virtual select_record *select_read (select_stuff *); |   virtual select_record *select_read (select_stuff *); | ||||||
|   virtual select_record *select_write (select_stuff *); |   virtual select_record *select_write (select_stuff *); | ||||||
|   | |||||||
| @@ -333,6 +333,7 @@ fhandler_base::fstat_by_handle (struct __stat64 *buf) | |||||||
| { | { | ||||||
|   NTSTATUS status; |   NTSTATUS status; | ||||||
|   IO_STATUS_BLOCK io; |   IO_STATUS_BLOCK io; | ||||||
|  |   HANDLE h = get_stat_handle (); | ||||||
|  |  | ||||||
|   if (pc.fs_is_nfs ()) |   if (pc.fs_is_nfs ()) | ||||||
|     return fstat_by_nfs_ea (buf); |     return fstat_by_nfs_ea (buf); | ||||||
| @@ -351,22 +352,22 @@ fhandler_base::fstat_by_handle (struct __stat64 *buf) | |||||||
|  |  | ||||||
|   if (pc.has_buggy_basic_info ()) |   if (pc.has_buggy_basic_info ()) | ||||||
|     { |     { | ||||||
|       status = NtQueryInformationFile (get_handle (), &io, &fi, sizeof fi, |       status = NtQueryInformationFile (h, &io, &fi, sizeof fi, | ||||||
| 				       FileNetworkOpenInformation); | 				       FileNetworkOpenInformation); | ||||||
|       /* The timestamps are in the same relative memory location, only |       /* The timestamps are in the same relative memory location, only | ||||||
| 	 the DOS attributes have to be moved. */ | 	 the DOS attributes have to be moved. */ | ||||||
|       fi.fbi.FileAttributes = fi.fnoi.FileAttributes; |       fi.fbi.FileAttributes = fi.fnoi.FileAttributes; | ||||||
|     } |     } | ||||||
|   else |   else | ||||||
|     status = NtQueryInformationFile (get_handle (), &io, &fi.fbi, sizeof fi.fbi, |     status = NtQueryInformationFile (h, &io, &fi.fbi, | ||||||
| 				     FileBasicInformation); | 				     sizeof fi.fbi, FileBasicInformation); | ||||||
|   if (!NT_SUCCESS (status)) |   if (!NT_SUCCESS (status)) | ||||||
|     { |     { | ||||||
|       debug_printf ("%p = NtQueryInformationFile(%S, FileBasicInformation)", |       debug_printf ("%p = NtQueryInformationFile(%S, FileBasicInformation)", | ||||||
| 		    status, pc.get_nt_native_path ()); | 		    status, pc.get_nt_native_path ()); | ||||||
|       return -1; |       return -1; | ||||||
|     } |     } | ||||||
|   status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi, |   status = NtQueryInformationFile (h, &io, &fsi, sizeof fsi, | ||||||
| 				   FileStandardInformation); | 				   FileStandardInformation); | ||||||
|   if (!NT_SUCCESS (status)) |   if (!NT_SUCCESS (status)) | ||||||
|     { |     { | ||||||
| @@ -374,13 +375,17 @@ fhandler_base::fstat_by_handle (struct __stat64 *buf) | |||||||
| 		    status, pc.get_nt_native_path ()); | 		    status, pc.get_nt_native_path ()); | ||||||
|       return -1; |       return -1; | ||||||
|     } |     } | ||||||
|   status = NtQueryInformationFile (get_handle (), &io, &fii, sizeof fii, |   if (!ino && pc.hasgood_inode ()) | ||||||
| 				   FileInternalInformation); |  | ||||||
|   if (!NT_SUCCESS (status)) |  | ||||||
|     { |     { | ||||||
|       debug_printf ("%p = NtQueryInformationFile(%S, FileInternalInformation)", |       status = NtQueryInformationFile (h, &io, &fii, sizeof fii, | ||||||
| 		    status, pc.get_nt_native_path ()); | 				       FileInternalInformation); | ||||||
|       return -1; |       if (!NT_SUCCESS (status)) | ||||||
|  | 	{ | ||||||
|  | 	  debug_printf ("%p = NtQueryInformationFile(%S, FileInternalInformation)", | ||||||
|  | 			status, pc.get_nt_native_path ()); | ||||||
|  | 	  return -1; | ||||||
|  | 	} | ||||||
|  |       ino = fii.FileId.QuadPart; | ||||||
|     } |     } | ||||||
|   /* If the change time is 0, it's a file system which doesn't |   /* If the change time is 0, it's a file system which doesn't | ||||||
|      support a change timestamp.  In that case use the LastWriteTime |      support a change timestamp.  In that case use the LastWriteTime | ||||||
| @@ -397,7 +402,7 @@ fhandler_base::fstat_by_handle (struct __stat64 *buf) | |||||||
| 		       get_dev (), | 		       get_dev (), | ||||||
| 		       fsi.EndOfFile.QuadPart, | 		       fsi.EndOfFile.QuadPart, | ||||||
| 		       fsi.AllocationSize.QuadPart, | 		       fsi.AllocationSize.QuadPart, | ||||||
| 		       fii.FileId.QuadPart, | 		       ino, | ||||||
| 		       fsi.NumberOfLinks, | 		       fsi.NumberOfLinks, | ||||||
| 		       fi.fbi.FileAttributes); | 		       fi.fbi.FileAttributes); | ||||||
| } | } | ||||||
| @@ -492,7 +497,7 @@ fhandler_base::fstat_fs (struct __stat64 *buf) | |||||||
|   int oret; |   int oret; | ||||||
|   int open_flags = O_RDONLY | O_BINARY; |   int open_flags = O_RDONLY | O_BINARY; | ||||||
|  |  | ||||||
|   if (get_handle ()) |   if (get_stat_handle ()) | ||||||
|     { |     { | ||||||
|       if (!nohandle () && !is_fs_special ()) |       if (!nohandle () && !is_fs_special ()) | ||||||
| 	res = fstat_by_handle (buf); | 	res = fstat_by_handle (buf); | ||||||
| @@ -500,8 +505,16 @@ fhandler_base::fstat_fs (struct __stat64 *buf) | |||||||
| 	res = fstat_by_name (buf); | 	res = fstat_by_name (buf); | ||||||
|       return res; |       return res; | ||||||
|     } |     } | ||||||
|   query_open (query_read_attributes); |   /* First try to open with generic read access.  This allows to read the file | ||||||
|  |      in fstat_helper (when checking for executability) without having to | ||||||
|  |      re-open it.  Opening a file can take a lot of time on network drives | ||||||
|  |      so we try to avoid that. */ | ||||||
|   oret = open_fs (open_flags, 0); |   oret = open_fs (open_flags, 0); | ||||||
|  |   if (!oret) | ||||||
|  |     { | ||||||
|  |       query_open (query_read_attributes); | ||||||
|  |       oret = open_fs (open_flags, 0); | ||||||
|  |     } | ||||||
|   if (oret) |   if (oret) | ||||||
|     { |     { | ||||||
|       /* We now have a valid handle, regardless of the "nohandle" state. |       /* We now have a valid handle, regardless of the "nohandle" state. | ||||||
| @@ -546,6 +559,7 @@ fhandler_base::fstat_helper (struct __stat64 *buf, | |||||||
| { | { | ||||||
|   IO_STATUS_BLOCK st; |   IO_STATUS_BLOCK st; | ||||||
|   FILE_COMPRESSION_INFORMATION fci; |   FILE_COMPRESSION_INFORMATION fci; | ||||||
|  |   HANDLE h = get_stat_handle (); | ||||||
|  |  | ||||||
|   to_timestruc_t ((PFILETIME) LastAccessTime, &buf->st_atim); |   to_timestruc_t ((PFILETIME) LastAccessTime, &buf->st_atim); | ||||||
|   to_timestruc_t ((PFILETIME) LastWriteTime, &buf->st_mtim); |   to_timestruc_t ((PFILETIME) LastWriteTime, &buf->st_mtim); | ||||||
| @@ -563,7 +577,7 @@ fhandler_base::fstat_helper (struct __stat64 *buf, | |||||||
| #endif | #endif | ||||||
|  |  | ||||||
|   /* Enforce namehash as inode number on untrusted file systems. */ |   /* Enforce namehash as inode number on untrusted file systems. */ | ||||||
|   if (pc.isgood_inode (nFileIndex)) |   if (nFileIndex && pc.isgood_inode (nFileIndex)) | ||||||
|     buf->st_ino = (__ino64_t) nFileIndex; |     buf->st_ino = (__ino64_t) nFileIndex; | ||||||
|   else |   else | ||||||
|     buf->st_ino = get_ino (); |     buf->st_ino = get_ino (); | ||||||
| @@ -576,9 +590,9 @@ fhandler_base::fstat_helper (struct __stat64 *buf, | |||||||
|     buf->st_blocks = (nAllocSize + S_BLKSIZE - 1) / S_BLKSIZE; |     buf->st_blocks = (nAllocSize + S_BLKSIZE - 1) / S_BLKSIZE; | ||||||
|   else if (::has_attribute (dwFileAttributes, FILE_ATTRIBUTE_COMPRESSED |   else if (::has_attribute (dwFileAttributes, FILE_ATTRIBUTE_COMPRESSED | ||||||
| 					      | FILE_ATTRIBUTE_SPARSE_FILE) | 					      | FILE_ATTRIBUTE_SPARSE_FILE) | ||||||
| 	   && get_handle () && !is_fs_special () | 	   && h && !is_fs_special () | ||||||
| 	   && !NtQueryInformationFile (get_handle (), &st, (PVOID) &fci, | 	   && !NtQueryInformationFile (h, &st, (PVOID) &fci, sizeof fci, | ||||||
| 				      sizeof fci, FileCompressionInformation)) | 				       FileCompressionInformation)) | ||||||
|     /* Otherwise we request the actual amount of bytes allocated for |     /* Otherwise we request the actual amount of bytes allocated for | ||||||
|        compressed and sparsed files. */ |        compressed and sparsed files. */ | ||||||
|     buf->st_blocks = (fci.CompressedFileSize.QuadPart + S_BLKSIZE - 1) |     buf->st_blocks = (fci.CompressedFileSize.QuadPart + S_BLKSIZE - 1) | ||||||
| @@ -597,15 +611,14 @@ fhandler_base::fstat_helper (struct __stat64 *buf, | |||||||
|       buf->st_size = pc.get_symlink_length (); |       buf->st_size = pc.get_symlink_length (); | ||||||
|       /* symlinks are everything for everyone! */ |       /* symlinks are everything for everyone! */ | ||||||
|       buf->st_mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO; |       buf->st_mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO; | ||||||
|       get_file_attribute (get_handle (), pc, NULL, |       get_file_attribute (h, pc, NULL, | ||||||
| 			  &buf->st_uid, &buf->st_gid); | 			  &buf->st_uid, &buf->st_gid); | ||||||
|       goto done; |       goto done; | ||||||
|     } |     } | ||||||
|   else if (pc.issocket ()) |   else if (pc.issocket ()) | ||||||
|     buf->st_mode = S_IFSOCK; |     buf->st_mode = S_IFSOCK; | ||||||
|  |  | ||||||
|   if (!get_file_attribute (is_fs_special () && !pc.issocket () |   if (!get_file_attribute (is_fs_special () && !pc.issocket () ? NULL : h, pc, | ||||||
| 			   ? NULL : get_handle (), pc, |  | ||||||
| 			   &buf->st_mode, &buf->st_uid, &buf->st_gid)) | 			   &buf->st_mode, &buf->st_uid, &buf->st_gid)) | ||||||
|     { |     { | ||||||
|       /* If read-only attribute is set, modify ntsec return value */ |       /* If read-only attribute is set, modify ntsec return value */ | ||||||
| @@ -659,50 +672,26 @@ fhandler_base::fstat_helper (struct __stat64 *buf, | |||||||
| 	     shebang scripts. */ | 	     shebang scripts. */ | ||||||
| 	  if (pc.exec_state () == dont_know_if_executable) | 	  if (pc.exec_state () == dont_know_if_executable) | ||||||
| 	    { | 	    { | ||||||
| 	      OBJECT_ATTRIBUTES attr; | 	      LARGE_INTEGER off = { QuadPart:0LL }; | ||||||
|  | 	      char magic[3]; | ||||||
| 	      NTSTATUS status; | 	      NTSTATUS status; | ||||||
| 	      HANDLE h; |  | ||||||
| 	      IO_STATUS_BLOCK io; | 	      IO_STATUS_BLOCK io; | ||||||
|  |  | ||||||
| 	      /* The NWFS implementation is frighteningly incomplete.  When | 	      if (get_stat_access () & (GENERIC_READ | FILE_READ_DATA)) | ||||||
| 	         re-opening a file by handle, the subsequent NtReadFile |  | ||||||
| 		 returns with the weird status STATUS_FILE_IS_A_DIRECTORY. |  | ||||||
| 		 We're still using the re-open by handle method for all |  | ||||||
| 		 other filesystems since it's 8-10% faster than opening |  | ||||||
| 		 by name. */ |  | ||||||
| 	      if (pc.fs_is_nwfs ()) |  | ||||||
| 		InitializeObjectAttributes (&attr, pc.get_nt_native_path (), |  | ||||||
| 					    OBJ_CASE_INSENSITIVE, NULL, NULL) |  | ||||||
| 	      else |  | ||||||
| 		InitializeObjectAttributes (&attr, &ro_u_empty, 0, |  | ||||||
| 					    get_handle (), NULL); |  | ||||||
| 	      status = NtOpenFile (&h, SYNCHRONIZE | FILE_READ_DATA, |  | ||||||
| 				   &attr, &io, FILE_SHARE_VALID_FLAGS, |  | ||||||
| 				   FILE_SYNCHRONOUS_IO_NONALERT); |  | ||||||
| 	      if (NT_SUCCESS (status)) |  | ||||||
| 		{ | 		{ | ||||||
| 		  LARGE_INTEGER off = { QuadPart:0LL }; | 		  status = NtReadFile (h, NULL, NULL, NULL, | ||||||
| 		  char magic[3]; | 				       &io, magic, 3, &off, NULL); | ||||||
|  | 		  status = wait_pending (status, h, io); | ||||||
| 		  status = NtReadFile (h, NULL, NULL, NULL, &io, magic, | 		  if (!NT_SUCCESS (status)) | ||||||
| 				       3, &off, NULL); |  | ||||||
| 		  if (NT_SUCCESS (status)) |  | ||||||
| 		    { |  | ||||||
| 		      if (has_exec_chars (magic, io.Information)) |  | ||||||
| 			{ |  | ||||||
| 			  /* Heureka, it's an executable */ |  | ||||||
| 			  pc.set_exec (); |  | ||||||
| 			  buf->st_mode |= STD_XBITS; |  | ||||||
| 			} |  | ||||||
| 		    } |  | ||||||
| 		  else |  | ||||||
| 		    debug_printf ("%p = NtReadFile(%S)", status, | 		    debug_printf ("%p = NtReadFile(%S)", status, | ||||||
| 				  pc.get_nt_native_path ()); | 				  pc.get_nt_native_path ()); | ||||||
| 		  NtClose (h); | 		  else if (has_exec_chars (magic, io.Information)) | ||||||
|  | 		    { | ||||||
|  | 		      /* Heureka, it's an executable */ | ||||||
|  | 		      pc.set_exec (); | ||||||
|  | 		      buf->st_mode |= STD_XBITS; | ||||||
|  | 		    } | ||||||
| 		} | 		} | ||||||
| 	      else |  | ||||||
| 		debug_printf ("%p = NtOpenFile(%S)", status, |  | ||||||
| 			      pc.get_nt_native_path ()); |  | ||||||
| 	    } | 	    } | ||||||
| 	} | 	} | ||||||
|       if (pc.exec_state () == is_executable) |       if (pc.exec_state () == is_executable) | ||||||
| @@ -740,7 +729,7 @@ fhandler_disk_file::fstatvfs (struct statvfs *sfs) | |||||||
|   IO_STATUS_BLOCK io; |   IO_STATUS_BLOCK io; | ||||||
|   FILE_FS_FULL_SIZE_INFORMATION full_fsi; |   FILE_FS_FULL_SIZE_INFORMATION full_fsi; | ||||||
|   FILE_FS_SIZE_INFORMATION fsi; |   FILE_FS_SIZE_INFORMATION fsi; | ||||||
|   HANDLE fh = get_handle (); |   HANDLE fh = get_stat_handle (); | ||||||
|  |  | ||||||
|   if (!fh) |   if (!fh) | ||||||
|     { |     { | ||||||
| @@ -1050,7 +1039,8 @@ cant_access_acl: | |||||||
|     } |     } | ||||||
|   else |   else | ||||||
|     { |     { | ||||||
|       if (!get_handle ()) |       if ((cmd == SETACL && !get_handle ()) | ||||||
|  | 	  || (cmd != SETACL && !get_stat_handle ())) | ||||||
| 	{ | 	{ | ||||||
| 	  query_open (cmd == SETACL ? query_write_control : query_read_control); | 	  query_open (cmd == SETACL ? query_write_control : query_read_control); | ||||||
| 	  if (!(oret = open (O_BINARY, 0))) | 	  if (!(oret = open (O_BINARY, 0))) | ||||||
| @@ -1087,10 +1077,10 @@ cant_access_acl: | |||||||
| 	    if (!aclbufp) | 	    if (!aclbufp) | ||||||
| 	      set_errno(EFAULT); | 	      set_errno(EFAULT); | ||||||
| 	    else | 	    else | ||||||
| 	      res = getacl (get_handle (), pc, nentries, aclbufp); | 	      res = getacl (get_stat_handle (), pc, nentries, aclbufp); | ||||||
| 	    break; | 	    break; | ||||||
| 	  case GETACLCNT: | 	  case GETACLCNT: | ||||||
| 	    res = getacl (get_handle (), pc, 0, NULL); | 	    res = getacl (get_stat_handle (), pc, 0, NULL); | ||||||
| 	    break; | 	    break; | ||||||
| 	  default: | 	  default: | ||||||
| 	    set_errno (EINVAL); | 	    set_errno (EINVAL); | ||||||
| @@ -1277,17 +1267,13 @@ fhandler_disk_file::link (const char *newpath) | |||||||
| 	} | 	} | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   HANDLE fh; |   /* We only need READ_CONTROL access so the handle returned in pc is | ||||||
|   NTSTATUS status; |      sufficient.  And if the file couldn't be opened with READ_CONTROL | ||||||
|   OBJECT_ATTRIBUTES attr; |      access in path_conv, we won't be able to do it here anyway. */ | ||||||
|   IO_STATUS_BLOCK io; |   HANDLE fh = get_stat_handle (); | ||||||
|   status = NtOpenFile (&fh, READ_CONTROL, |   if (!fh) | ||||||
| 		       pc.get_object_attr (attr, sec_none_nih), &io, |  | ||||||
| 		       FILE_SHARE_VALID_FLAGS, |  | ||||||
| 		       FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT); |  | ||||||
|   if (!NT_SUCCESS (status)) |  | ||||||
|     { |     { | ||||||
|       __seterrno_from_nt_status (status); |       set_errno (EACCES); | ||||||
|       return -1; |       return -1; | ||||||
|     } |     } | ||||||
|   PUNICODE_STRING tgt = newpc.get_nt_native_path (); |   PUNICODE_STRING tgt = newpc.get_nt_native_path (); | ||||||
| @@ -1296,8 +1282,10 @@ fhandler_disk_file::link (const char *newpath) | |||||||
|   pfli->ReplaceIfExists = FALSE; |   pfli->ReplaceIfExists = FALSE; | ||||||
|   pfli->RootDirectory = NULL; |   pfli->RootDirectory = NULL; | ||||||
|   memcpy (pfli->FileName, tgt->Buffer, pfli->FileNameLength = tgt->Length); |   memcpy (pfli->FileName, tgt->Buffer, pfli->FileNameLength = tgt->Length); | ||||||
|  |  | ||||||
|  |   NTSTATUS status; | ||||||
|  |   IO_STATUS_BLOCK io; | ||||||
|   status = NtSetInformationFile (fh, &io, pfli, size, FileLinkInformation); |   status = NtSetInformationFile (fh, &io, pfli, size, FileLinkInformation); | ||||||
|   NtClose (fh); |  | ||||||
|   if (!NT_SUCCESS (status)) |   if (!NT_SUCCESS (status)) | ||||||
|     { |     { | ||||||
|       if (status == STATUS_INVALID_DEVICE_REQUEST) |       if (status == STATUS_INVALID_DEVICE_REQUEST) | ||||||
| @@ -1764,8 +1752,6 @@ readdir_get_ino (const char *path, bool dot_dot) | |||||||
|   char *fname; |   char *fname; | ||||||
|   struct __stat64 st; |   struct __stat64 st; | ||||||
|   HANDLE hdl; |   HANDLE hdl; | ||||||
|   OBJECT_ATTRIBUTES attr; |  | ||||||
|   IO_STATUS_BLOCK io; |  | ||||||
|   __ino64_t ino = 0; |   __ino64_t ino = 0; | ||||||
|  |  | ||||||
|   if (dot_dot) |   if (dot_dot) | ||||||
| @@ -1777,7 +1763,7 @@ readdir_get_ino (const char *path, bool dot_dot) | |||||||
|       strcpy (c, ".."); |       strcpy (c, ".."); | ||||||
|       path = fname; |       path = fname; | ||||||
|     } |     } | ||||||
|   path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX | PC_NOWARN); |   path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX | PC_NOWARN | PC_KEEP_HANDLE); | ||||||
|   if (pc.isspecial ()) |   if (pc.isspecial ()) | ||||||
|     { |     { | ||||||
|       if (!stat_worker (pc, &st)) |       if (!stat_worker (pc, &st)) | ||||||
| @@ -1785,17 +1771,11 @@ readdir_get_ino (const char *path, bool dot_dot) | |||||||
|     } |     } | ||||||
|   else if (!pc.hasgood_inode ()) |   else if (!pc.hasgood_inode ()) | ||||||
|     ino = hash_path_name (0, pc.get_nt_native_path ()); |     ino = hash_path_name (0, pc.get_nt_native_path ()); | ||||||
|   else if (NT_SUCCESS (NtOpenFile (&hdl, READ_CONTROL, |   else if ((hdl = pc.handle ()) != NULL) | ||||||
| 				   pc.get_object_attr (attr, sec_none_nih), |  | ||||||
| 				   &io, FILE_SHARE_VALID_FLAGS, |  | ||||||
| 				   FILE_OPEN_FOR_BACKUP_INTENT |  | ||||||
| 				   | (pc.is_rep_symlink () |  | ||||||
| 				      ? FILE_OPEN_REPARSE_POINT : 0)))) |  | ||||||
|     { |     { | ||||||
|       ino = pc.get_ino_by_handle (hdl); |       ino = pc.get_ino_by_handle (hdl); | ||||||
|       if (!ino) |       if (!ino) | ||||||
| 	ino = hash_path_name (0, pc.get_nt_native_path ()); | 	ino = hash_path_name (0, pc.get_nt_native_path ()); | ||||||
|       NtClose (hdl); |  | ||||||
|     } |     } | ||||||
|   return ino; |   return ino; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -868,6 +868,16 @@ typedef enum _EVENT_INFORMATION_CLASS | |||||||
| #define NtCurrentProcess() ((HANDLE) 0xffffffff) | #define NtCurrentProcess() ((HANDLE) 0xffffffff) | ||||||
| #define NtCurrentThread()  ((HANDLE) 0xfffffffe) | #define NtCurrentThread()  ((HANDLE) 0xfffffffe) | ||||||
|  |  | ||||||
|  | /* Helper macro for sync I/O with async handle. */ | ||||||
|  | inline NTSTATUS | ||||||
|  | wait_pending (NTSTATUS status, HANDLE h, IO_STATUS_BLOCK &io) | ||||||
|  | { | ||||||
|  |   if (status != STATUS_PENDING) | ||||||
|  |     return status; | ||||||
|  |   WaitForSingleObject (h, INFINITE); | ||||||
|  |   return io.Status; | ||||||
|  | } | ||||||
|  |  | ||||||
| extern "C" | extern "C" | ||||||
| { | { | ||||||
|   NTSTATUS NTAPI NtAdjustPrivilegesToken (HANDLE, BOOLEAN, PTOKEN_PRIVILEGES, |   NTSTATUS NTAPI NtAdjustPrivilegesToken (HANDLE, BOOLEAN, PTOKEN_PRIVILEGES, | ||||||
|   | |||||||
| @@ -96,8 +96,8 @@ struct symlink_info | |||||||
|   _major_t major; |   _major_t major; | ||||||
|   _minor_t minor; |   _minor_t minor; | ||||||
|   _mode_t mode; |   _mode_t mode; | ||||||
|   int check (char *path, const suffix_info *suffixes, unsigned opt, |   int check (char *path, const suffix_info *suffixes, fs_info &fs, | ||||||
| 	     fs_info &fs); | 	     path_conv_handle &conv_hdl); | ||||||
|   int set (char *path); |   int set (char *path); | ||||||
|   bool parse_device (const char *); |   bool parse_device (const char *); | ||||||
|   int check_sysfile (HANDLE h); |   int check_sysfile (HANDLE h); | ||||||
| @@ -639,6 +639,7 @@ path_conv::check (const char *src, unsigned opt, | |||||||
|       cfree (modifiable_path ()); |       cfree (modifiable_path ()); | ||||||
|       path = NULL; |       path = NULL; | ||||||
|     } |     } | ||||||
|  |   close_conv_handle (); | ||||||
|   memset (&dev, 0, sizeof (dev)); |   memset (&dev, 0, sizeof (dev)); | ||||||
|   fs.clear (); |   fs.clear (); | ||||||
|   if (normalized_path) |   if (normalized_path) | ||||||
| @@ -695,7 +696,9 @@ path_conv::check (const char *src, unsigned opt, | |||||||
|  |  | ||||||
|       int symlen = 0; |       int symlen = 0; | ||||||
|  |  | ||||||
|       for (unsigned pflags_or = opt & PC_NO_ACCESS_CHECK; ; pflags_or = 0) |       for (unsigned pflags_or = opt & (PC_NO_ACCESS_CHECK | PC_KEEP_HANDLE); | ||||||
|  | 	   ; | ||||||
|  | 	   pflags_or = 0) | ||||||
| 	{ | 	{ | ||||||
| 	  const suffix_info *suff; | 	  const suffix_info *suff; | ||||||
| 	  char *full_path; | 	  char *full_path; | ||||||
| @@ -823,7 +826,7 @@ path_conv::check (const char *src, unsigned opt, | |||||||
| 	  if (is_msdos) | 	  if (is_msdos) | ||||||
| 	    sym.pflags |= PATH_NOPOSIX | PATH_NOACL; | 	    sym.pflags |= PATH_NOPOSIX | PATH_NOACL; | ||||||
|  |  | ||||||
| 	  symlen = sym.check (full_path, suff, opt, fs); | 	  symlen = sym.check (full_path, suff, fs, conv_handle); | ||||||
|  |  | ||||||
| is_virtual_symlink: | is_virtual_symlink: | ||||||
|  |  | ||||||
| @@ -1124,6 +1127,7 @@ path_conv::~path_conv () | |||||||
|       cfree (wide_path); |       cfree (wide_path); | ||||||
|       wide_path = NULL; |       wide_path = NULL; | ||||||
|     } |     } | ||||||
|  |   close_conv_handle (); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool | bool | ||||||
| @@ -1683,55 +1687,50 @@ cmp_shortcut_header (win_shortcut_hdr *file_header) | |||||||
| } | } | ||||||
|  |  | ||||||
| int | int | ||||||
| symlink_info::check_shortcut (HANDLE in_h) | symlink_info::check_shortcut (HANDLE h) | ||||||
| { | { | ||||||
|   tmp_pathbuf tp; |   tmp_pathbuf tp; | ||||||
|   win_shortcut_hdr *file_header; |   win_shortcut_hdr *file_header; | ||||||
|   char *buf, *cp; |   char *buf, *cp; | ||||||
|   unsigned short len; |   unsigned short len; | ||||||
|   int res = 0; |   int res = 0; | ||||||
|   OBJECT_ATTRIBUTES attr; |  | ||||||
|   NTSTATUS status; |   NTSTATUS status; | ||||||
|   HANDLE h; |  | ||||||
|   IO_STATUS_BLOCK io; |   IO_STATUS_BLOCK io; | ||||||
|   FILE_STANDARD_INFORMATION fsi; |   FILE_STANDARD_INFORMATION fsi; | ||||||
|  |   LARGE_INTEGER off = { QuadPart:0LL }; | ||||||
|  |  | ||||||
|   InitializeObjectAttributes (&attr, &ro_u_empty, 0, in_h, NULL); |  | ||||||
|   status = NtOpenFile (&h, FILE_READ_DATA | SYNCHRONIZE, |  | ||||||
| 		       &attr, &io, FILE_SHARE_VALID_FLAGS, |  | ||||||
| 		       FILE_OPEN_FOR_BACKUP_INTENT |  | ||||||
| 		       | FILE_SYNCHRONOUS_IO_NONALERT); |  | ||||||
|   if (!NT_SUCCESS (status)) |  | ||||||
|     return 0; |  | ||||||
|   status = NtQueryInformationFile (h, &io, &fsi, sizeof fsi, |   status = NtQueryInformationFile (h, &io, &fsi, sizeof fsi, | ||||||
| 				   FileStandardInformation); | 				   FileStandardInformation); | ||||||
|   if (!NT_SUCCESS (status)) |   if (!NT_SUCCESS (status)) | ||||||
|     { |     { | ||||||
|       set_error (EIO); |       set_error (EIO); | ||||||
|       goto out; |       return 0; | ||||||
|     } |     } | ||||||
|   if (fsi.EndOfFile.QuadPart <= sizeof (win_shortcut_hdr) |   if (fsi.EndOfFile.QuadPart <= sizeof (win_shortcut_hdr) | ||||||
|       || fsi.EndOfFile.QuadPart > 4 * 65536) |       || fsi.EndOfFile.QuadPart > 4 * 65536) | ||||||
|     goto out; |     return 0; | ||||||
|   if (fsi.EndOfFile.LowPart < NT_MAX_PATH * sizeof (WCHAR)) |   if (fsi.EndOfFile.LowPart < NT_MAX_PATH * sizeof (WCHAR)) | ||||||
|     buf = (char *) tp.w_get (); |     buf = (char *) tp.w_get (); | ||||||
|   else |   else | ||||||
|     buf = (char *) alloca (fsi.EndOfFile.LowPart + 1); |     buf = (char *) alloca (fsi.EndOfFile.LowPart + 1); | ||||||
|   if (!NT_SUCCESS (NtReadFile (h, NULL, NULL, NULL, |   status = NtReadFile (h, NULL, NULL, NULL, &io, buf, fsi.EndOfFile.LowPart, | ||||||
| 			       &io, buf, fsi.EndOfFile.LowPart, NULL, NULL))) | 		       &off, NULL); | ||||||
|  |   status = wait_pending (status, h, io); | ||||||
|  |   if (!NT_SUCCESS (status)) | ||||||
|     { |     { | ||||||
|       set_error (EIO); |       if (status != STATUS_END_OF_FILE) | ||||||
|       goto out; | 	set_error (EIO); | ||||||
|  |       return 0; | ||||||
|     } |     } | ||||||
|   file_header = (win_shortcut_hdr *) buf; |   file_header = (win_shortcut_hdr *) buf; | ||||||
|   if (io.Information != fsi.EndOfFile.LowPart |   if (io.Information != fsi.EndOfFile.LowPart | ||||||
|       || !cmp_shortcut_header (file_header)) |       || !cmp_shortcut_header (file_header)) | ||||||
|     goto out; |     return 0; | ||||||
|   cp = buf + sizeof (win_shortcut_hdr); |   cp = buf + sizeof (win_shortcut_hdr); | ||||||
|   if (file_header->flags & WSH_FLAG_IDLIST) /* Skip ITEMIDLIST */ |   if (file_header->flags & WSH_FLAG_IDLIST) /* Skip ITEMIDLIST */ | ||||||
|     cp += *(unsigned short *) cp + 2; |     cp += *(unsigned short *) cp + 2; | ||||||
|   if (!(len = *(unsigned short *) cp)) |   if (!(len = *(unsigned short *) cp)) | ||||||
|     goto out; |     return 0; | ||||||
|   cp += 2; |   cp += 2; | ||||||
|   /* Check if this is a device file - these start with the sequence :\\ */ |   /* Check if this is a device file - these start with the sequence :\\ */ | ||||||
|   if (strncmp (cp, ":\\", 2) == 0) |   if (strncmp (cp, ":\\", 2) == 0) | ||||||
| @@ -1751,11 +1750,11 @@ symlink_info::check_shortcut (HANDLE in_h) | |||||||
| 	  char *tmpbuf = tp.c_get (); | 	  char *tmpbuf = tp.c_get (); | ||||||
| 	  if (sys_wcstombs (tmpbuf, NT_MAX_PATH, (PWCHAR) (cp + 2)) | 	  if (sys_wcstombs (tmpbuf, NT_MAX_PATH, (PWCHAR) (cp + 2)) | ||||||
| 	      > SYMLINK_MAX + 1) | 	      > SYMLINK_MAX + 1) | ||||||
| 	    goto out; | 	    return 0; | ||||||
| 	  res = posixify (tmpbuf); | 	  res = posixify (tmpbuf); | ||||||
| 	} | 	} | ||||||
|       else if (len > SYMLINK_MAX) |       else if (len > SYMLINK_MAX) | ||||||
| 	goto out; | 	return 0; | ||||||
|       else |       else | ||||||
| 	{ | 	{ | ||||||
| 	  cp[len] = '\0'; | 	  cp[len] = '\0'; | ||||||
| @@ -1764,41 +1763,33 @@ symlink_info::check_shortcut (HANDLE in_h) | |||||||
|     } |     } | ||||||
|   if (res) /* It's a symlink.  */ |   if (res) /* It's a symlink.  */ | ||||||
|     pflags |= PATH_SYMLINK | PATH_LNK; |     pflags |= PATH_SYMLINK | PATH_LNK; | ||||||
|  |  | ||||||
| out: |  | ||||||
|   NtClose (h); |  | ||||||
|   return res; |   return res; | ||||||
| } | } | ||||||
|  |  | ||||||
| int | int | ||||||
| symlink_info::check_sysfile (HANDLE in_h) | symlink_info::check_sysfile (HANDLE h) | ||||||
| { | { | ||||||
|   tmp_pathbuf tp; |   tmp_pathbuf tp; | ||||||
|   char cookie_buf[sizeof (SYMLINK_COOKIE) - 1]; |   char cookie_buf[sizeof (SYMLINK_COOKIE) - 1]; | ||||||
|   char *srcbuf = tp.c_get (); |   char *srcbuf = tp.c_get (); | ||||||
|   int res = 0; |   int res = 0; | ||||||
|   OBJECT_ATTRIBUTES attr; |  | ||||||
|   NTSTATUS status; |   NTSTATUS status; | ||||||
|   HANDLE h; |  | ||||||
|   IO_STATUS_BLOCK io; |   IO_STATUS_BLOCK io; | ||||||
|   bool interix_symlink = false; |   bool interix_symlink = false; | ||||||
|  |   LARGE_INTEGER off = { QuadPart:0LL }; | ||||||
|  |  | ||||||
|   InitializeObjectAttributes (&attr, &ro_u_empty, 0, in_h, NULL); |   status = NtReadFile (h, NULL, NULL, NULL, &io, cookie_buf, | ||||||
|   status = NtOpenFile (&h, FILE_READ_DATA | SYNCHRONIZE, | 		       sizeof (cookie_buf), &off, NULL); | ||||||
| 		       &attr, &io, FILE_SHARE_VALID_FLAGS, |   status = wait_pending (status, h, io); | ||||||
| 		       FILE_OPEN_FOR_BACKUP_INTENT |  | ||||||
| 		       | FILE_SYNCHRONOUS_IO_NONALERT); |  | ||||||
|   if (!NT_SUCCESS (status)) |   if (!NT_SUCCESS (status)) | ||||||
|     return 0; |  | ||||||
|   else if (!NT_SUCCESS (status = NtReadFile (h, NULL, NULL, NULL, &io, |  | ||||||
| 					     cookie_buf, sizeof (cookie_buf), |  | ||||||
| 					     NULL, NULL))) |  | ||||||
|     { |     { | ||||||
|       debug_printf ("ReadFile1 failed %p", status); |       debug_printf ("ReadFile1 failed %p", status); | ||||||
|       if (status != STATUS_END_OF_FILE) |       if (status != STATUS_END_OF_FILE) | ||||||
| 	set_error (EIO); | 	set_error (EIO); | ||||||
|  |       return 0; | ||||||
|     } |     } | ||||||
|   else if (io.Information == sizeof (cookie_buf) |   off.QuadPart = io.Information; | ||||||
|  |   if (io.Information == sizeof (cookie_buf) | ||||||
| 	   && memcmp (cookie_buf, SYMLINK_COOKIE, sizeof (cookie_buf)) == 0) | 	   && memcmp (cookie_buf, SYMLINK_COOKIE, sizeof (cookie_buf)) == 0) | ||||||
|     { |     { | ||||||
|       /* It's a symlink.  */ |       /* It's a symlink.  */ | ||||||
| @@ -1817,14 +1808,13 @@ symlink_info::check_sysfile (HANDLE in_h) | |||||||
|       /* Interix symlink cookies are shorter than Cygwin symlink cookies, so |       /* Interix symlink cookies are shorter than Cygwin symlink cookies, so | ||||||
|          in case of an Interix symlink cooky we have read too far into the |          in case of an Interix symlink cooky we have read too far into the | ||||||
| 	 file.  Set file pointer back to the position right after the cookie. */ | 	 file.  Set file pointer back to the position right after the cookie. */ | ||||||
|       FILE_POSITION_INFORMATION fpi; |       off.QuadPart = sizeof (INTERIX_SYMLINK_COOKIE) - 1; | ||||||
|       fpi.CurrentByteOffset.QuadPart = sizeof (INTERIX_SYMLINK_COOKIE) - 1; |  | ||||||
|       NtSetInformationFile (h, &io, &fpi, sizeof fpi, FilePositionInformation); |  | ||||||
|     } |     } | ||||||
|   if (pflags & PATH_SYMLINK) |   if (pflags & PATH_SYMLINK) | ||||||
|     { |     { | ||||||
|       status = NtReadFile (h, NULL, NULL, NULL, &io, srcbuf, |       status = NtReadFile (h, NULL, NULL, NULL, &io, srcbuf, | ||||||
| 			   NT_MAX_PATH, NULL, NULL); | 			   NT_MAX_PATH, &off, NULL); | ||||||
|  |       status = wait_pending (status, h, io); | ||||||
|       if (!NT_SUCCESS (status)) |       if (!NT_SUCCESS (status)) | ||||||
| 	{ | 	{ | ||||||
| 	  debug_printf ("ReadFile2 failed"); | 	  debug_printf ("ReadFile2 failed"); | ||||||
| @@ -1852,7 +1842,6 @@ symlink_info::check_sysfile (HANDLE in_h) | |||||||
|       else |       else | ||||||
| 	res = posixify (srcbuf); | 	res = posixify (srcbuf); | ||||||
|     } |     } | ||||||
|   NtClose (h); |  | ||||||
|   return res; |   return res; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1916,7 +1905,6 @@ symlink_info::check_nfs_symlink (HANDLE h) | |||||||
| { | { | ||||||
|   tmp_pathbuf tp; |   tmp_pathbuf tp; | ||||||
|   NTSTATUS status; |   NTSTATUS status; | ||||||
|   OBJECT_ATTRIBUTES attr; |  | ||||||
|   IO_STATUS_BLOCK io; |   IO_STATUS_BLOCK io; | ||||||
|   struct { |   struct { | ||||||
|     FILE_GET_EA_INFORMATION fgei; |     FILE_GET_EA_INFORMATION fgei; | ||||||
| @@ -1925,11 +1913,6 @@ symlink_info::check_nfs_symlink (HANDLE h) | |||||||
|   PFILE_FULL_EA_INFORMATION pffei; |   PFILE_FULL_EA_INFORMATION pffei; | ||||||
|   int res = 0; |   int res = 0; | ||||||
|  |  | ||||||
|   InitializeObjectAttributes (&attr, &ro_u_empty, 0, h, NULL); |  | ||||||
|   status = NtOpenFile (&h, FILE_READ_EA, &attr, &io, FILE_SHARE_VALID_FLAGS, |  | ||||||
| 		       FILE_OPEN_REPARSE_POINT | FILE_OPEN_FOR_BACKUP_INTENT); |  | ||||||
|   if (!NT_SUCCESS (status)) |  | ||||||
|     return 0; |  | ||||||
|   /* To find out if the file is a symlink and to get the symlink target, |   /* To find out if the file is a symlink and to get the symlink target, | ||||||
|      try to fetch the NfsSymlinkTargetName EA. */ |      try to fetch the NfsSymlinkTargetName EA. */ | ||||||
|   fgei_buf.fgei.NextEntryOffset = 0; |   fgei_buf.fgei.NextEntryOffset = 0; | ||||||
| @@ -1938,13 +1921,12 @@ symlink_info::check_nfs_symlink (HANDLE h) | |||||||
|   pffei = (PFILE_FULL_EA_INFORMATION) tp.w_get (); |   pffei = (PFILE_FULL_EA_INFORMATION) tp.w_get (); | ||||||
|   status = NtQueryEaFile (h, &io, pffei, NT_MAX_PATH * sizeof (WCHAR), TRUE, |   status = NtQueryEaFile (h, &io, pffei, NT_MAX_PATH * sizeof (WCHAR), TRUE, | ||||||
| 			  &fgei_buf.fgei, sizeof fgei_buf, NULL, TRUE); | 			  &fgei_buf.fgei, sizeof fgei_buf, NULL, TRUE); | ||||||
|   NtClose (h); |  | ||||||
|   if (NT_SUCCESS (status) && pffei->EaValueLength > 0) |   if (NT_SUCCESS (status) && pffei->EaValueLength > 0) | ||||||
|     { |     { | ||||||
|       PWCHAR spath = (PWCHAR) |       PWCHAR spath = (PWCHAR) | ||||||
| 		     (pffei->EaName + pffei->EaNameLength + 1); | 		     (pffei->EaName + pffei->EaNameLength + 1); | ||||||
|       res = sys_wcstombs (contents, SYMLINK_MAX + 1, |       res = sys_wcstombs (contents, SYMLINK_MAX + 1, | ||||||
| 		      spath, pffei->EaValueLength); | 			  spath, pffei->EaValueLength) - 1; | ||||||
|       pflags |= PATH_SYMLINK; |       pflags |= PATH_SYMLINK; | ||||||
|     } |     } | ||||||
|   return res; |   return res; | ||||||
| @@ -2197,8 +2179,8 @@ symlink_info::parse_device (const char *contents) | |||||||
|    stored into BUF if PATH is a symlink.  */ |    stored into BUF if PATH is a symlink.  */ | ||||||
|  |  | ||||||
| int | int | ||||||
| symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt, | symlink_info::check (char *path, const suffix_info *suffixes, fs_info &fs, | ||||||
| 		     fs_info &fs) | 		     path_conv_handle &conv_hdl) | ||||||
| { | { | ||||||
|   int res; |   int res; | ||||||
|   HANDLE h; |   HANDLE h; | ||||||
| @@ -2238,6 +2220,10 @@ restart: | |||||||
|   PVOID eabuf = &nfs_aol_ffei; |   PVOID eabuf = &nfs_aol_ffei; | ||||||
|   ULONG easize = sizeof nfs_aol_ffei; |   ULONG easize = sizeof nfs_aol_ffei; | ||||||
|  |  | ||||||
|  | # define MIN_STAT_ACCESS	(READ_CONTROL | FILE_READ_ATTRIBUTES) | ||||||
|  | # define FULL_STAT_ACCESS	(SYNCHRONIZE | GENERIC_READ) | ||||||
|  |   ACCESS_MASK access = 0; | ||||||
|  |  | ||||||
|   bool had_ext = !!*ext_here; |   bool had_ext = !!*ext_here; | ||||||
|   while (suffix.next ()) |   while (suffix.next ()) | ||||||
|     { |     { | ||||||
| @@ -2255,14 +2241,22 @@ restart: | |||||||
| 	 symlink (which would spoil the task of this method quite a bit). | 	 symlink (which would spoil the task of this method quite a bit). | ||||||
| 	 Fortunately it's ignored on most other file systems so we don't have | 	 Fortunately it's ignored on most other file systems so we don't have | ||||||
| 	 to special case NFS too much. */ | 	 to special case NFS too much. */ | ||||||
|       status = NtCreateFile (&h, |       status = NtCreateFile (&h, access = FULL_STAT_ACCESS, &attr, &io, NULL, | ||||||
| 			     READ_CONTROL | FILE_READ_ATTRIBUTES, | 			     0, FILE_SHARE_VALID_FLAGS, FILE_OPEN, | ||||||
| 			     &attr, &io, NULL, 0, FILE_SHARE_VALID_FLAGS, |  | ||||||
| 			     FILE_OPEN, |  | ||||||
| 			     FILE_OPEN_REPARSE_POINT | 			     FILE_OPEN_REPARSE_POINT | ||||||
| 			     | FILE_OPEN_FOR_BACKUP_INTENT, | 			     | FILE_OPEN_FOR_BACKUP_INTENT, | ||||||
| 			     eabuf, easize); | 			     eabuf, easize); | ||||||
|       debug_printf ("%p = NtCreateFile (%S)", status, &upath); |       if (status == STATUS_ACCESS_DENIED) | ||||||
|  | 	{ | ||||||
|  | 	  status = NtCreateFile (&h, access = MIN_STAT_ACCESS, &attr, &io, | ||||||
|  | 				 NULL, 0, FILE_SHARE_VALID_FLAGS, FILE_OPEN, | ||||||
|  | 				 FILE_OPEN_REPARSE_POINT | ||||||
|  | 				 | FILE_OPEN_FOR_BACKUP_INTENT, | ||||||
|  | 				 eabuf, easize); | ||||||
|  | 	  debug_printf ("%p = NtCreateFile (2:%S)", status, &upath); | ||||||
|  | 	} | ||||||
|  |       else | ||||||
|  | 	debug_printf ("%p = NtCreateFile (1:%S)", status, &upath); | ||||||
|       /* No right to access EAs or EAs not supported? */ |       /* No right to access EAs or EAs not supported? */ | ||||||
|       if (!NT_SUCCESS (status) |       if (!NT_SUCCESS (status) | ||||||
| 	  && (status == STATUS_ACCESS_DENIED | 	  && (status == STATUS_ACCESS_DENIED | ||||||
| @@ -2282,11 +2276,20 @@ restart: | |||||||
| 	      eabuf = NULL; | 	      eabuf = NULL; | ||||||
| 	      easize = 0; | 	      easize = 0; | ||||||
| 	    } | 	    } | ||||||
| 	  status = NtOpenFile (&h, READ_CONTROL | FILE_READ_ATTRIBUTES, | 	  status = NtOpenFile (&h, access = FULL_STAT_ACCESS, &attr, &io, | ||||||
| 			       &attr, &io, FILE_SHARE_VALID_FLAGS, | 			       FILE_SHARE_VALID_FLAGS, | ||||||
| 			       FILE_OPEN_REPARSE_POINT | 			       FILE_OPEN_REPARSE_POINT | ||||||
| 			       | FILE_OPEN_FOR_BACKUP_INTENT); | 			       | FILE_OPEN_FOR_BACKUP_INTENT); | ||||||
| 	  debug_printf ("%p = NtOpenFile (no-EA, %S)", status, &upath); | 	  if (status == STATUS_ACCESS_DENIED) | ||||||
|  | 	    { | ||||||
|  | 	      status = NtOpenFile (&h, access = MIN_STAT_ACCESS, &attr, &io, | ||||||
|  | 				   FILE_SHARE_VALID_FLAGS, | ||||||
|  | 				   FILE_OPEN_REPARSE_POINT | ||||||
|  | 				   | FILE_OPEN_FOR_BACKUP_INTENT); | ||||||
|  | 	      debug_printf ("%p = NtOpenFile (no-EAs 2:%S)", status, &upath); | ||||||
|  | 	    } | ||||||
|  | 	  else | ||||||
|  | 	    debug_printf ("%p = NtOpenFile (no-EA 1:%S)", status, &upath); | ||||||
| 	} | 	} | ||||||
|       if (status == STATUS_OBJECT_NAME_NOT_FOUND) |       if (status == STATUS_OBJECT_NAME_NOT_FOUND) | ||||||
| 	{ | 	{ | ||||||
| @@ -2478,7 +2481,10 @@ restart: | |||||||
|       if ((fileattr & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_DIRECTORY)) |       if ((fileattr & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_DIRECTORY)) | ||||||
| 	  == FILE_ATTRIBUTE_READONLY && suffix.lnk_match ()) | 	  == FILE_ATTRIBUTE_READONLY && suffix.lnk_match ()) | ||||||
| 	{ | 	{ | ||||||
| 	  res = check_shortcut (h); | 	  if (!(access & GENERIC_READ)) | ||||||
|  | 	    res = 0; | ||||||
|  | 	  else | ||||||
|  | 	    res = check_shortcut (h); | ||||||
| 	  if (!res) | 	  if (!res) | ||||||
| 	    { | 	    { | ||||||
| 	      /* If searching for `foo' and then finding a `foo.lnk' which is | 	      /* If searching for `foo' and then finding a `foo.lnk' which is | ||||||
| @@ -2538,17 +2544,23 @@ restart: | |||||||
|       else if ((fileattr & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY)) |       else if ((fileattr & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY)) | ||||||
| 	       == FILE_ATTRIBUTE_SYSTEM) | 	       == FILE_ATTRIBUTE_SYSTEM) | ||||||
| 	{ | 	{ | ||||||
| 	  res = check_sysfile (h); | 	  if (!(access & GENERIC_READ)) | ||||||
|  | 	    res = 0; | ||||||
|  | 	  else | ||||||
|  | 	    res = check_sysfile (h); | ||||||
| 	  if (res) | 	  if (res) | ||||||
| 	    break; | 	    break; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|       /* If the file could be opened with FILE_READ_EA, and if it's on a |       /* If the file is on an NFS share and could be opened with extended | ||||||
| 	 NFS share, check if it's a symlink.  Only files can be symlinks | 	 attributes, check if it's a symlink.  Only files can be symlinks | ||||||
| 	 (which can be symlinks to directories). */ | 	 (which can be symlinks to directories). */ | ||||||
|       else if (fs.is_nfs () && !no_ea && !(fileattr & FILE_ATTRIBUTE_DIRECTORY)) |       else if (fs.is_nfs () && !no_ea && !(fileattr & FILE_ATTRIBUTE_DIRECTORY)) | ||||||
| 	{ | 	{ | ||||||
| 	  res = check_nfs_symlink (h); | 	  if (!(access & GENERIC_READ)) | ||||||
|  | 	    res = 0; | ||||||
|  | 	  else | ||||||
|  | 	    res = check_nfs_symlink (h); | ||||||
| 	  if (res) | 	  if (res) | ||||||
| 	    break; | 	    break; | ||||||
| 	} | 	} | ||||||
| @@ -2562,7 +2574,12 @@ restart: | |||||||
|     } |     } | ||||||
|  |  | ||||||
|   if (h) |   if (h) | ||||||
|     NtClose (h); |     { | ||||||
|  |       if (pflags & PC_KEEP_HANDLE) | ||||||
|  | 	conv_hdl.set (h, access); | ||||||
|  |       else | ||||||
|  | 	NtClose (h); | ||||||
|  |     } | ||||||
|  |  | ||||||
|   syscall_printf ("%d = symlink.check (%s, %p) (%p)", |   syscall_printf ("%d = symlink.check (%s, %p) (%p)", | ||||||
| 		  res, suffix.path, contents, pflags); | 		  res, suffix.path, contents, pflags); | ||||||
|   | |||||||
| @@ -57,6 +57,7 @@ enum pathconv_arg | |||||||
|   PC_CHECK_EA		= 0x0040, |   PC_CHECK_EA		= 0x0040, | ||||||
|   PC_POSIX		= 0x0080, |   PC_POSIX		= 0x0080, | ||||||
|   PC_NOWARN		= 0x0100, |   PC_NOWARN		= 0x0100, | ||||||
|  |   PC_KEEP_HANDLE	= 0x00400000, | ||||||
|   PC_NO_ACCESS_CHECK	= 0x00800000 |   PC_NO_ACCESS_CHECK	= 0x00800000 | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -86,6 +87,33 @@ enum path_types | |||||||
|   PATH_SOCKET		= 0x40000000 |   PATH_SOCKET		= 0x40000000 | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | class path_conv_handle | ||||||
|  | { | ||||||
|  |   HANDLE      hdl; | ||||||
|  |   ACCESS_MASK acc; | ||||||
|  | public: | ||||||
|  |   path_conv_handle () : hdl (NULL), acc (0) {} | ||||||
|  |   inline void set (HANDLE h, ACCESS_MASK a) { hdl = h; acc = a; } | ||||||
|  |   inline void close () | ||||||
|  |   { | ||||||
|  |     if (hdl) | ||||||
|  |       CloseHandle (hdl); | ||||||
|  |     set (NULL, 0); | ||||||
|  |   } | ||||||
|  |   inline void dup (path_conv_handle &pch) | ||||||
|  |   { | ||||||
|  |     if (!DuplicateHandle (GetCurrentProcess (), pch.handle (), | ||||||
|  | 			  GetCurrentProcess (), &hdl, | ||||||
|  | 			  0, TRUE, DUPLICATE_SAME_ACCESS)) | ||||||
|  |       { | ||||||
|  | 	hdl = NULL; | ||||||
|  | 	acc = 0; | ||||||
|  |       } | ||||||
|  |   } | ||||||
|  |   inline HANDLE handle () const { return hdl; } | ||||||
|  |   inline ACCESS_MASK access () const { return acc; } | ||||||
|  | }; | ||||||
|  |  | ||||||
| class symlink_info; | class symlink_info; | ||||||
|  |  | ||||||
| class path_conv | class path_conv | ||||||
| @@ -98,6 +126,7 @@ class path_conv | |||||||
|   void add_ext_from_sym (symlink_info&); |   void add_ext_from_sym (symlink_info&); | ||||||
|   DWORD symlink_length; |   DWORD symlink_length; | ||||||
|   const char *path; |   const char *path; | ||||||
|  |   path_conv_handle conv_handle; | ||||||
|  public: |  public: | ||||||
|   unsigned path_flags; |   unsigned path_flags; | ||||||
|   const char *known_suffix; |   const char *known_suffix; | ||||||
| @@ -220,6 +249,7 @@ class path_conv | |||||||
|   { |   { | ||||||
|     memcpy (this, &pc, sizeof pc); |     memcpy (this, &pc, sizeof pc); | ||||||
|     path = cstrdup (pc.path); |     path = cstrdup (pc.path); | ||||||
|  |     conv_handle.dup (pc.conv_handle); | ||||||
|     normalized_path = cstrdup(pc.normalized_path); |     normalized_path = cstrdup(pc.normalized_path); | ||||||
|     wide_path = NULL; |     wide_path = NULL; | ||||||
|     return *this; |     return *this; | ||||||
| @@ -250,6 +280,11 @@ class path_conv | |||||||
|   } |   } | ||||||
|   bool is_binary (); |   bool is_binary (); | ||||||
|  |  | ||||||
|  |   HANDLE handle () const { return conv_handle.handle (); } | ||||||
|  |   ACCESS_MASK access () const { return conv_handle.access (); } | ||||||
|  |   void reset_conv_handle () { conv_handle.set (NULL, 0); } | ||||||
|  |   void close_conv_handle () { conv_handle.close (); } | ||||||
|  |  | ||||||
|   __ino64_t get_ino_by_handle (HANDLE h); |   __ino64_t get_ino_by_handle (HANDLE h); | ||||||
| #if 0 /* obsolete, method still exists in fhandler_disk_file.cc */ | #if 0 /* obsolete, method still exists in fhandler_disk_file.cc */ | ||||||
|   unsigned __stdcall ndisk_links (DWORD); |   unsigned __stdcall ndisk_links (DWORD); | ||||||
|   | |||||||
| @@ -419,7 +419,8 @@ acl_worker (const char *path, int cmd, int nentries, __aclent32_t *aclbufp, | |||||||
| 	    unsigned fmode) | 	    unsigned fmode) | ||||||
| { | { | ||||||
|   int res = -1; |   int res = -1; | ||||||
|   fhandler_base *fh = build_fh_name (path, fmode, stat_suffixes); |   fhandler_base *fh = build_fh_name (path, fmode | PC_KEEP_HANDLE, | ||||||
|  | 				     stat_suffixes); | ||||||
|   if (fh->error ()) |   if (fh->error ()) | ||||||
|     { |     { | ||||||
|       debug_printf ("got %d error from build_fh_name", fh->error ()); |       debug_printf ("got %d error from build_fh_name", fh->error ()); | ||||||
|   | |||||||
| @@ -1169,7 +1169,8 @@ link (const char *oldpath, const char *newpath) | |||||||
|   int res = -1; |   int res = -1; | ||||||
|   fhandler_base *fh; |   fhandler_base *fh; | ||||||
|  |  | ||||||
|   if (!(fh = build_fh_name (oldpath, PC_SYM_NOFOLLOW, stat_suffixes))) |   if (!(fh = build_fh_name (oldpath, PC_SYM_NOFOLLOW | PC_KEEP_HANDLE, | ||||||
|  | 			    stat_suffixes))) | ||||||
|     goto error; |     goto error; | ||||||
|  |  | ||||||
|   if (fh->error ()) |   if (fh->error ()) | ||||||
| @@ -1542,7 +1543,7 @@ extern "C" int | |||||||
| stat64 (const char *name, struct __stat64 *buf) | stat64 (const char *name, struct __stat64 *buf) | ||||||
| { | { | ||||||
|   syscall_printf ("entering"); |   syscall_printf ("entering"); | ||||||
|   path_conv pc (name, PC_SYM_FOLLOW | PC_POSIX, stat_suffixes); |   path_conv pc (name, PC_SYM_FOLLOW | PC_POSIX | PC_KEEP_HANDLE, stat_suffixes); | ||||||
|   return stat_worker (pc, buf); |   return stat_worker (pc, buf); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1581,7 +1582,8 @@ extern "C" int | |||||||
| lstat64 (const char *name, struct __stat64 *buf) | lstat64 (const char *name, struct __stat64 *buf) | ||||||
| { | { | ||||||
|   syscall_printf ("entering"); |   syscall_printf ("entering"); | ||||||
|   path_conv pc (name, PC_SYM_NOFOLLOW | PC_POSIX, stat_suffixes); |   path_conv pc (name, PC_SYM_NOFOLLOW | PC_POSIX | PC_KEEP_HANDLE, | ||||||
|  | 		stat_suffixes); | ||||||
|   return stat_worker (pc, buf); |   return stat_worker (pc, buf); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -2561,7 +2563,8 @@ statvfs (const char *name, struct statvfs *sfs) | |||||||
|   if (efault.faulted (EFAULT)) |   if (efault.faulted (EFAULT)) | ||||||
|     goto error; |     goto error; | ||||||
|  |  | ||||||
|   if (!(fh = build_fh_name (name, PC_SYM_FOLLOW, stat_suffixes))) |   if (!(fh = build_fh_name (name, PC_SYM_FOLLOW | PC_KEEP_HANDLE, | ||||||
|  | 			    stat_suffixes))) | ||||||
|     goto error; |     goto error; | ||||||
|  |  | ||||||
|   if (fh->error ()) |   if (fh->error ()) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user