* mount.cc (GETVOLINFO_VALID_MASK): Drop FILE_SEQUENTIAL_WRITE_ONCE
from mask. Expand the comment a bit. (WIN_FAT_FLAGS): New define. (FS_IS_WINDOWS_FAT): New macro. (fs_info::update): Handle remote FS faking to be FAT. Subsume under CIFS. Check for NWFS and has_buggy_basic_info only for remote filesystems. Add check for has_dos_filenames_only. * mount.h (class fs_info): Add has_dos_filenames_only status flag. Implement accessors. * path.cc (symlink_info::check): Rearrange variable definitions to clear them up. Add a restart label to allow a clean restart within the method. Add a check for broken filesystems only allowing DOS pathnames in case we encounter a STATUS_OBJECT_NAME_NOT_FOUND status. If all checks point to one of that, restart method with tweaked incoming path. Add lengthy comments to explain what we do.
This commit is contained in:
		| @@ -1,3 +1,21 @@ | ||||
| 2010-04-22  Corinna Vinschen  <corinna@vinschen.de> | ||||
|  | ||||
| 	* mount.cc (GETVOLINFO_VALID_MASK): Drop FILE_SEQUENTIAL_WRITE_ONCE | ||||
| 	from mask.  Expand the comment a bit. | ||||
| 	(WIN_FAT_FLAGS): New define. | ||||
| 	(FS_IS_WINDOWS_FAT): New macro. | ||||
| 	(fs_info::update): Handle remote FS faking to be FAT.  Subsume under | ||||
| 	CIFS.  Check for NWFS and has_buggy_basic_info only for remote | ||||
| 	filesystems.  Add check for has_dos_filenames_only. | ||||
| 	* mount.h (class fs_info): Add has_dos_filenames_only status flag. | ||||
| 	Implement accessors. | ||||
| 	* path.cc (symlink_info::check): Rearrange variable definitions to | ||||
| 	clear them up.  Add a restart label to allow a clean restart within | ||||
| 	the method.  Add a check for broken filesystems only allowing DOS | ||||
| 	pathnames in case we encounter a STATUS_OBJECT_NAME_NOT_FOUND status. | ||||
| 	If all checks point to one of that, restart method with tweaked | ||||
| 	incoming path.  Add lengthy comments to explain what we do. | ||||
|  | ||||
| 2010-04-22  Corinna Vinschen  <corinna@vinschen.de> | ||||
|  | ||||
| 	* path.cc (symlink_info::check): Fix a comment. | ||||
|   | ||||
| @@ -208,9 +208,12 @@ fs_info::update (PUNICODE_STRING upath, HANDLE in_vol) | ||||
| /* Should be reevaluated for each new OS.  Right now this mask is valid up | ||||
|    to Vista.  The important point here is to test only flags indicating | ||||
|    capabilities and to ignore flags indicating a specific state of this | ||||
|    volume.  At present these flags to ignore are FILE_VOLUME_IS_COMPRESSED | ||||
|    and FILE_READ_ONLY_VOLUME. */ | ||||
| #define GETVOLINFO_VALID_MASK (0x003701ffUL) | ||||
|    volume.  At present these flags to ignore are FILE_VOLUME_IS_COMPRESSED, | ||||
|    FILE_READ_ONLY_VOLUME, and FILE_SEQUENTIAL_WRITE_ONCE.  The additional | ||||
|    filesystem flags supported since Windows 7 are also ignored for now. | ||||
|    They add information, but only on W7 and later, and only for filesystems | ||||
|    also supporting these flags, right now only NTFS. */ | ||||
| #define GETVOLINFO_VALID_MASK (0x002701ffUL) | ||||
| #define TEST_GVI(f,m) (((f) & GETVOLINFO_VALID_MASK) == (m)) | ||||
|  | ||||
| /* FIXME: This flag twist is getting awkward.  There should really be some | ||||
| @@ -244,6 +247,11 @@ fs_info::update (PUNICODE_STRING upath, HANDLE in_vol) | ||||
| 				| FILE_FILE_COMPRESSION) | ||||
| #define FS_IS_WINDOWS_NTFS TEST_GVI(flags () & MINIMAL_WIN_NTFS_FLAGS, \ | ||||
| 				    MINIMAL_WIN_NTFS_FLAGS) | ||||
| /* These are the exact flags of a real Windows FAT/FAT32 filesystem. | ||||
|    Anything else is a filesystem faking to be FAT. */ | ||||
| #define WIN_FAT_FLAGS (FILE_CASE_PRESERVED_NAMES | FILE_UNICODE_ON_DISK) | ||||
| #define FS_IS_WINDOWS_FAT  TEST_GVI(flags (), WIN_FAT_FLAGS) | ||||
|  | ||||
|       /* This always fails on NT4. */ | ||||
|       status = NtQueryVolumeInformationFile (vol, &io, &ffoi, sizeof ffoi, | ||||
| 					     FileFsObjectIdInformation); | ||||
| @@ -266,6 +274,11 @@ fs_info::update (PUNICODE_STRING upath, HANDLE in_vol) | ||||
| 	  && !is_netapp (FS_IS_NETAPP_DATAONTAP)) | ||||
| 	/* Any other remote FS faking to be NTFS. */ | ||||
| 	is_cifs (!FS_IS_WINDOWS_NTFS); | ||||
|       /* Then check the remote filesystems faking to be FAT.  Right now all | ||||
| 	 of them are subsumed under the "CIFS" filesystem type. */ | ||||
|       if (!got_fs () | ||||
| 	  && is_fat (RtlEqualUnicodePathPrefix (&fsname, &ro_u_fat, TRUE))) | ||||
| 	is_cifs (!FS_IS_WINDOWS_FAT); | ||||
|       /* Then check remote filesystems honest about their name. */ | ||||
|       if (!got_fs () | ||||
| 	  /* Microsoft NFS needs distinct access methods for metadata. */ | ||||
| @@ -274,6 +287,8 @@ fs_info::update (PUNICODE_STRING upath, HANDLE in_vol) | ||||
| 	     drawbacks, like not supporting DOS attributes other than R/O | ||||
| 	     and stuff like that. */ | ||||
| 	  && !is_mvfs (RtlEqualUnicodePathPrefix (&fsname, &ro_u_mvfs, FALSE)) | ||||
| 	  /* NWFS == Novell Netware FS.  Broken info class, see below. */ | ||||
| 	  && !is_nwfs (RtlEqualUnicodeString (&fsname, &ro_u_nwfs, FALSE)) | ||||
| 	  /* Known remote file system which can't handle calls to | ||||
| 	     NtQueryDirectoryFile(FileIdBothDirectoryInformation) */ | ||||
| 	  && !is_unixfs (RtlEqualUnicodeString (&fsname, &ro_u_unixfs, FALSE))) | ||||
| @@ -283,18 +298,31 @@ fs_info::update (PUNICODE_STRING upath, HANDLE in_vol) | ||||
| 	  is_sunwnfs (RtlEqualUnicodeString (&fsname, &ro_u_sunwnfs, FALSE)); | ||||
| 	  has_buggy_open (is_sunwnfs ()); | ||||
| 	} | ||||
|       /* Not only UNIXFS is known to choke on FileIdBothDirectoryInformation. | ||||
| 	 Some other CIFS servers have problems with this call as well. | ||||
| 	 Know example: EMC NS-702.  We just don't use that info class on | ||||
| 	 any remote CIFS.  */ | ||||
|       if (got_fs ()) | ||||
| 	has_buggy_fileid_dirinfo (is_cifs () || is_unixfs ()); | ||||
| 	{ | ||||
| 	  /* UNIXFS is known to choke on FileIdBothDirectoryInformation. | ||||
| 	     Some other CIFS servers have problems with this call as well. | ||||
| 	     Know example: EMC NS-702.  We just don't use that info class on | ||||
| 	     any remote CIFS.  */ | ||||
| 	  has_buggy_fileid_dirinfo (is_cifs () || is_unixfs ()); | ||||
| 	  /* NWFS is known to have a broken FileBasicInformation info class. | ||||
| 	     It can't be used to fetch information, only to set information.  | ||||
| 	     Therefore, for NWFS we have to fallback to the | ||||
| 	     FileNetworkOpenInformation info class.  Unfortunately we can't | ||||
| 	     use FileNetworkOpenInformation all the time since that fails on | ||||
| 	     other filesystems like NFS. */ | ||||
| 	  has_buggy_basic_info (is_nwfs ()); | ||||
| 	  /* Netapp ans NWFS are too dumb to allow non-DOS filesystems | ||||
| 	     containing trailing dots and spaces when accessed from Windows | ||||
| 	     clients.  We subsume CIFS into this class of filesystems right | ||||
| 	     away since at least some of them are not capable either. */ | ||||
| 	  has_dos_filenames_only (is_netapp () || is_nwfs () || is_cifs ()); | ||||
| 	} | ||||
|     } | ||||
|   if (!got_fs () | ||||
|       && !is_ntfs (RtlEqualUnicodeString (&fsname, &ro_u_ntfs, FALSE)) | ||||
|       && !is_fat (RtlEqualUnicodePathPrefix (&fsname, &ro_u_fat, TRUE)) | ||||
|       && !is_csc_cache (RtlEqualUnicodeString (&fsname, &ro_u_csc, FALSE)) | ||||
|       && !is_nwfs (RtlEqualUnicodeString (&fsname, &ro_u_nwfs, FALSE)) | ||||
|       && is_cdrom (ffdi.DeviceType == FILE_DEVICE_CD_ROM)) | ||||
|     is_udf (RtlEqualUnicodeString (&fsname, &ro_u_udf, FALSE)); | ||||
|   if (!got_fs ()) | ||||
| @@ -308,12 +336,6 @@ fs_info::update (PUNICODE_STRING upath, HANDLE in_vol) | ||||
|   has_acls (flags () & FS_PERSISTENT_ACLS); | ||||
|   /* Netapp inode numbers are fly-by-night. */ | ||||
|   hasgood_inode ((has_acls () && !is_netapp ()) || is_nfs ()); | ||||
|   /* NWFS is known to have a broken FileBasicInformation info class.  It | ||||
|      can't be used to fetch information, only to set information.  Therefore, | ||||
|      for NWFS we have to fallback to the FileNetworkOpenInformation info | ||||
|      class.  Unfortunately we can't use FileNetworkOpenInformation all the | ||||
|      time since that fails on other filesystems like NFS. */ | ||||
|   has_buggy_basic_info (is_nwfs ()); | ||||
|   /* Case sensitivity is supported if FILE_CASE_SENSITIVE_SEARCH is set, | ||||
|      except on Samba which handles Windows clients case insensitive. | ||||
|  | ||||
|   | ||||
| @@ -51,6 +51,7 @@ class fs_info | ||||
|     unsigned has_buggy_open		: 1; | ||||
|     unsigned has_buggy_fileid_dirinfo	: 1; | ||||
|     unsigned has_buggy_basic_info	: 1; | ||||
|     unsigned has_dos_filenames_only	: 1; | ||||
|   } status; | ||||
|   ULONG sernum;			/* Volume Serial Number */ | ||||
|   char fsn[80];			/* Windows filesystem name */ | ||||
| @@ -75,6 +76,7 @@ class fs_info | ||||
|   IMPLEMENT_STATUS_FLAG (bool, has_buggy_open) | ||||
|   IMPLEMENT_STATUS_FLAG (bool, has_buggy_fileid_dirinfo) | ||||
|   IMPLEMENT_STATUS_FLAG (bool, has_buggy_basic_info) | ||||
|   IMPLEMENT_STATUS_FLAG (bool, has_dos_filenames_only) | ||||
|   IMPLEMENT_FS_FLAG (is_fat, fat) | ||||
|   IMPLEMENT_FS_FLAG (is_ntfs, ntfs) | ||||
|   IMPLEMENT_FS_FLAG (is_samba, samba) | ||||
|   | ||||
| @@ -2170,29 +2170,39 @@ int | ||||
| symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt, | ||||
| 		     fs_info &fs) | ||||
| { | ||||
|   HANDLE h = NULL; | ||||
|   int res = 0; | ||||
|   int res; | ||||
|   HANDLE h; | ||||
|   NTSTATUS status; | ||||
|   UNICODE_STRING upath; | ||||
|   OBJECT_ATTRIBUTES attr; | ||||
|   IO_STATUS_BLOCK io; | ||||
|   FILE_BASIC_INFORMATION fbi; | ||||
|   suffix_scan suffix; | ||||
|   contents[0] = '\0'; | ||||
|  | ||||
|   ULONG ci_flag = cygwin_shared->obcaseinsensitive || (pflags & PATH_NOPOSIX) | ||||
| 		  ? OBJ_CASE_INSENSITIVE : 0; | ||||
|   /* TODO: Temporarily do all char->UNICODE conversion here.  This should | ||||
|      already be slightly faster than using Ascii functions. */ | ||||
|   tmp_pathbuf tp; | ||||
|   tp.u_get (&upath); | ||||
|   InitializeObjectAttributes (&attr, &upath, ci_flag, NULL, NULL); | ||||
|  | ||||
|   /* This label is used in case we encounter a FS which only handles | ||||
|      DOS paths.  See below. */ | ||||
| restart: | ||||
|  | ||||
|   h = NULL; | ||||
|   res = 0; | ||||
|   contents[0] = '\0'; | ||||
|   issymlink = true; | ||||
|   isdevice = false; | ||||
|   ext_here = suffix.has (path, suffixes); | ||||
|   extn = ext_here - path; | ||||
|   major = 0; | ||||
|   minor = 0; | ||||
|   mode = 0; | ||||
|   pflags &= ~(PATH_SYMLINK | PATH_LNK | PATH_REP); | ||||
|   ULONG ci_flag = cygwin_shared->obcaseinsensitive || (pflags & PATH_NOPOSIX) | ||||
| 		  ? OBJ_CASE_INSENSITIVE : 0; | ||||
|  | ||||
|   /* TODO: Temporarily do all char->UNICODE conversion here.  This should | ||||
|      already be slightly faster than using Ascii functions. */ | ||||
|   tmp_pathbuf tp; | ||||
|   UNICODE_STRING upath; | ||||
|   OBJECT_ATTRIBUTES attr; | ||||
|   tp.u_get (&upath); | ||||
|   InitializeObjectAttributes (&attr, &upath, ci_flag, NULL, NULL); | ||||
|   ext_here = suffix.has (path, suffixes); | ||||
|   extn = ext_here - path; | ||||
|  | ||||
|   PVOID eabuf = &nfs_aol_ffei; | ||||
|   ULONG easize = sizeof nfs_aol_ffei; | ||||
| @@ -2200,9 +2210,6 @@ symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt, | ||||
|   bool had_ext = !!*ext_here; | ||||
|   while (suffix.next ()) | ||||
|     { | ||||
|       FILE_BASIC_INFORMATION fbi; | ||||
|       NTSTATUS status; | ||||
|       IO_STATUS_BLOCK io; | ||||
|       bool no_ea = false; | ||||
|       bool fs_update_called = false; | ||||
|  | ||||
| @@ -2251,28 +2258,66 @@ symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt, | ||||
| 			       | FILE_OPEN_FOR_BACKUP_INTENT); | ||||
| 	  debug_printf ("%p = NtOpenFile (no-EA, %S)", status, &upath); | ||||
| 	} | ||||
|       if (status == STATUS_OBJECT_NAME_NOT_FOUND && ci_flag == 0 | ||||
| 	  && wincap.has_broken_udf ()) | ||||
|         { | ||||
| 	  /* On NT 5.x UDF is broken (at least) in terms of case sensitivity. | ||||
| 	     When trying to open a file case sensitive, the file appears to be | ||||
| 	     non-existant.  Another bug is described in fs_info::update. */ | ||||
| 	  attr.Attributes = OBJ_CASE_INSENSITIVE; | ||||
| 	  status = NtOpenFile (&h, READ_CONTROL | FILE_READ_ATTRIBUTES, | ||||
| 			       &attr, &io, FILE_SHARE_VALID_FLAGS, | ||||
| 			       FILE_OPEN_REPARSE_POINT | ||||
| 			       | FILE_OPEN_FOR_BACKUP_INTENT); | ||||
| 	  debug_printf ("%p = NtOpenFile (broken-UDF, %S)", status, &upath); | ||||
| 	  attr.Attributes = 0; | ||||
| 	  if (NT_SUCCESS (status)) | ||||
|       if (status == STATUS_OBJECT_NAME_NOT_FOUND) | ||||
| 	{ | ||||
| 	  if (ci_flag == 0 && wincap.has_broken_udf ()) | ||||
| 	    { | ||||
| 	      fs.update (&upath, h); | ||||
| 	      if (fs.is_udf ()) | ||||
| 		fs_update_called = true; | ||||
| 	      else | ||||
| 	      	{ | ||||
| 		  NtClose (h); | ||||
| 		  status = STATUS_OBJECT_NAME_NOT_FOUND; | ||||
| 	      /* On NT 5.x UDF is broken (at least) in terms of case | ||||
| 		 sensitivity.  When trying to open a file case sensitive, | ||||
| 		 the file appears to be non-existant.  Another bug is | ||||
| 		 described in fs_info::update. */ | ||||
| 	      attr.Attributes = OBJ_CASE_INSENSITIVE; | ||||
| 	      status = NtOpenFile (&h, READ_CONTROL | FILE_READ_ATTRIBUTES, | ||||
| 				   &attr, &io, FILE_SHARE_VALID_FLAGS, | ||||
| 				   FILE_OPEN_REPARSE_POINT | ||||
| 				   | FILE_OPEN_FOR_BACKUP_INTENT); | ||||
| 	      debug_printf ("%p = NtOpenFile (broken-UDF, %S)", status, &upath); | ||||
| 	      attr.Attributes = 0; | ||||
| 	      if (NT_SUCCESS (status)) | ||||
| 		{ | ||||
| 		  fs.update (&upath, h); | ||||
| 		  if (fs.is_udf ()) | ||||
| 		    fs_update_called = true; | ||||
| 		  else | ||||
| 		    { | ||||
| 		      NtClose (h); | ||||
| 		      status = STATUS_OBJECT_NAME_NOT_FOUND; | ||||
| 		    } | ||||
| 		} | ||||
| 	    } | ||||
| 	  /* There are filesystems out in the wild (Netapp, NWFS, and others) | ||||
| 	     which are uncapable of generating pathnames outside the Win32 | ||||
| 	     rules.  That means, filenames on these FSes must not have a | ||||
| 	     leading space or trailing dots and spaces.  This code snippet | ||||
| 	     manages them.  I really hope it's streamlined enough not to | ||||
| 	     slow down normal operation.  This extra check only kicks in if | ||||
| 	     we encountered a STATUS_OBJECT_NAME_NOT_FOUND *and* we didn't | ||||
| 	     already attach a suffix *and* the above special case for UDF | ||||
| 	     on XP didn't succeeed. */ | ||||
| 	  if (!*ext_here && !fs_update_called) | ||||
| 	    { | ||||
| 	      /* Check for leading space or trailing dot or space in | ||||
| 	         last component. */ | ||||
| 	      char *pend = ext_here; | ||||
| 	      while (pend[-1] == '.' || pend[-1] == ' ') | ||||
| 		--pend; | ||||
| 	      char *pbeg = pend; | ||||
| 	      while (pbeg[-1] != '\\') | ||||
| 	      	--pbeg; | ||||
| 	      /* If so, call fs.update to check if the filesystem is one of | ||||
| 		 the broken ones. */ | ||||
| 	      if ((*pbeg == ' ' || *pend != '\0') | ||||
| 		  && fs.update (&upath, NULL) | ||||
| 		  && fs.has_dos_filenames_only ()) | ||||
| 		{ | ||||
| 		  /* If so, strip leading spaces and trailing dots and spaces | ||||
| 		     from filename and... */ | ||||
| 		  if (pbeg) | ||||
| 		    while (*pbeg == ' ') | ||||
| 		      memmove (pbeg, pbeg + 1, --pend - pbeg); | ||||
| 		  *pend = '\0'; | ||||
| 		  /* ...try again. */ | ||||
| 		  goto restart; | ||||
| 		} | ||||
| 	    } | ||||
| 	} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user