[newlib-cygwin] Cygwin: Add FS_IOC_GETFLAGS and FS_IOC_SETFLAGS ioctls

Corinna Vinschen corinna@sourceware.org
Wed Dec 26 09:42:00 GMT 2018


https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=af4a65a26d703cf150cd6d75336c19543aa014d8

commit af4a65a26d703cf150cd6d75336c19543aa014d8
Author: Corinna Vinschen <corinna@vinschen.de>
Date:   Tue Dec 25 23:38:52 2018 +0100

    Cygwin: Add FS_IOC_GETFLAGS and FS_IOC_SETFLAGS ioctls
    
    Signed-off-by: Corinna Vinschen <corinna@vinschen.de>

Diff:
---
 winsup/cygwin/autoload.cc           |   2 +
 winsup/cygwin/fhandler.h            |   3 +
 winsup/cygwin/fhandler_disk_file.cc | 247 ++++++++++++++++++++++++++++++++++++
 winsup/cygwin/include/cygwin/fs.h   |  27 ++++
 4 files changed, 279 insertions(+)

diff --git a/winsup/cygwin/autoload.cc b/winsup/cygwin/autoload.cc
index 5a0e400..7978287 100644
--- a/winsup/cygwin/autoload.cc
+++ b/winsup/cygwin/autoload.cc
@@ -534,6 +534,8 @@ LoadDLLprime (ws2_32, _wsock_init, 0)
 LoadDLLfunc (CheckTokenMembership, 12, advapi32)
 LoadDLLfunc (CreateProcessAsUserW, 44, advapi32)
 LoadDLLfunc (DeregisterEventSource, 4, advapi32)
+LoadDLLfunc (DecryptFileW, 8, advapi32)
+LoadDLLfunc (EncryptFileW, 4, advapi32)
 LoadDLLfunc (LogonUserW, 24, advapi32)
 LoadDLLfunc (LookupAccountNameW, 28, advapi32)
 LoadDLLfunc (LookupAccountSidW, 28, advapi32)
diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index 9e63867..07e8a18 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -1437,6 +1437,8 @@ class fhandler_disk_file: public fhandler_base
   int __reg3 readdir_helper (DIR *, dirent *, DWORD, DWORD, PUNICODE_STRING fname);
 
   int prw_open (bool, void *);
+  uint64_t fs_ioc_getflags ();
+  int fs_ioc_setflags (uint64_t);
 
  public:
   fhandler_disk_file ();
@@ -1462,6 +1464,7 @@ class fhandler_disk_file: public fhandler_base
   int __reg2 link (const char *);
   int __reg2 utimens (const struct timespec *);
   int __reg2 fstatvfs (struct statvfs *buf);
+  int ioctl (unsigned int cmd, void *buf);
 
   HANDLE mmap (caddr_t *addr, size_t len, int prot, int flags, off_t off);
   int munmap (HANDLE h, caddr_t addr, size_t len);
diff --git a/winsup/cygwin/fhandler_disk_file.cc b/winsup/cygwin/fhandler_disk_file.cc
index a8b7af4..e0a196f 100644
--- a/winsup/cygwin/fhandler_disk_file.cc
+++ b/winsup/cygwin/fhandler_disk_file.cc
@@ -25,6 +25,7 @@ details. */
 #include "devices.h"
 #include "ldap.h"
 #include <aio.h>
+#include <cygwin/fs.h>
 
 #define _COMPILING_NEWLIB
 #include <dirent.h>
@@ -2437,6 +2438,252 @@ fhandler_disk_file::closedir (DIR *dir)
   return res;
 }
 
+uint64_t
+fhandler_disk_file::fs_ioc_getflags ()
+{
+  NTSTATUS status;
+  IO_STATUS_BLOCK io;
+  FILE_BASIC_INFORMATION fbi;
+  FILE_CASE_SENSITIVE_INFORMATION fcsi;
+  uint64_t flags = 0;
+
+  status = NtQueryInformationFile (get_handle (), &io, &fbi, sizeof fbi,
+				   FileBasicInformation);
+  if (NT_SUCCESS (status))
+    {
+      flags = (uint64_t) fbi.FileAttributes & FS_FL_USER_VISIBLE;
+      pc.file_attributes (fbi.FileAttributes);
+    }
+  else
+    flags = (uint64_t) pc.file_attributes () & FS_FL_USER_VISIBLE;
+  if (pc.isdir () && wincap.has_case_sensitive_dirs ()
+      && !pc.isremote () && pc.fs_is_ntfs ())
+    {
+      fcsi.Flags = 0;
+      status = NtQueryInformationFile (get_handle (), &io,
+				       &fcsi, sizeof fcsi,
+				       FileCaseSensitiveInformation);
+      if (NT_SUCCESS (status)
+	  && (fcsi.Flags & FILE_CS_FLAG_CASE_SENSITIVE_DIR))
+	flags |= FS_CASESENS_FL;
+    }
+  return flags;
+}
+
+/* Settable DOS attributes */
+#define FS_FL_SETATTRIBS	(FS_READONLY_FL \
+				 | FS_HIDDEN_FL \
+				 | FS_SYSTEM_FL \
+				 | FS_ARCHIVE_FL \
+				 | FS_TEMP_FL \
+				 | FS_NOTINDEXED_FL)
+
+int
+fhandler_disk_file::fs_ioc_setflags (uint64_t flags)
+{
+  int ret = -1;
+  uint64_t old_flags;
+  HANDLE fh;
+  NTSTATUS status;
+  OBJECT_ATTRIBUTES attr;
+  IO_STATUS_BLOCK io;
+  FILE_BASIC_INFORMATION fbi;
+  FILE_SET_SPARSE_BUFFER fssb;
+  USHORT comp;
+  FILE_CASE_SENSITIVE_INFORMATION fcsi;
+
+  if ((get_access () & (GENERIC_WRITE | FILE_WRITE_ATTRIBUTES)) != 0)
+    fh = get_handle ();
+  else
+    {
+      status = NtOpenFile (&fh, FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
+			   pc.init_reopen_attr (attr, get_handle ()), &io,
+			   FILE_SHARE_VALID_FLAGS,
+			   FILE_OPEN_FOR_BACKUP_INTENT);
+      if (!NT_SUCCESS (status))
+	{
+	  fh = get_handle ();
+	  __seterrno_from_nt_status (status);
+	  goto out;
+	}
+    }
+  old_flags = fs_ioc_getflags ();
+  if ((old_flags & FS_FL_SETATTRIBS) != (flags & FS_FL_SETATTRIBS))
+    {
+      fbi.CreationTime.QuadPart
+      = fbi.LastAccessTime.QuadPart
+      = fbi.LastWriteTime.QuadPart
+      = fbi.ChangeTime.QuadPart = 0LL;
+      fbi.FileAttributes = (ULONG) old_flags;
+      fbi.FileAttributes &= ~FS_FL_SETATTRIBS;
+      fbi.FileAttributes |= (flags & FS_FL_SETATTRIBS);
+      if (fbi.FileAttributes == 0)
+	fbi.FileAttributes = FILE_ATTRIBUTE_NORMAL;
+      status = NtSetInformationFile (fh, &io, &fbi, sizeof fbi,
+				     FileBasicInformation);
+      if (!NT_SUCCESS (status))
+	{
+	  __seterrno_from_nt_status (status);
+	  goto out;
+	}
+    }
+  if (!pc.isdir() && (flags & FS_SPARSE_FL) != (old_flags & FS_SPARSE_FL))
+    {
+      fssb.SetSparse = (flags & FS_SPARSE_FL) ? TRUE : FALSE;
+      status = NtFsControlFile (fh, NULL, NULL, NULL, &io,
+				FSCTL_SET_SPARSE, &fssb, sizeof fssb, NULL, 0);
+      if (!NT_SUCCESS (status))
+	{
+	  __seterrno_from_nt_status (status);
+	  goto out;
+	}
+    }
+  if (pc.isdir () && (flags & FS_CASESENS_FL) != (old_flags & FS_CASESENS_FL))
+    {
+      if (wincap.has_case_sensitive_dirs ()
+	  && !pc.isremote () && pc.fs_is_ntfs ())
+	{
+	  fcsi.Flags = (flags & FS_CASESENS_FL)
+		       ? FILE_CS_FLAG_CASE_SENSITIVE_DIR : 0;
+	  status = NtSetInformationFile (fh, &io, &fcsi, sizeof fcsi,
+					 FileCaseSensitiveInformation);
+	  if (!NT_SUCCESS (status))
+	    {
+	      /* Special case: The directory contains files which only
+		 differ in case.  NtSetInformationFile refuses to change
+		 back to case insensitivity and returns status 0xc00004b3.
+		 There's no STATUS_xyz macro assigned to that value yet,
+		 nor does it map to a useful Win32 error value. */
+	      if (status == (NTSTATUS) 0xc00004b3)
+		set_errno (EINVAL);	/* Does that make sense? */
+	      else
+		__seterrno_from_nt_status (status);
+	      goto out;
+	    }
+	}
+      else
+	{
+	  set_errno (ENOTSUP);
+	  goto out;
+	}
+    }
+  if ((flags & FS_COMPRESSED_FL) != (old_flags & FS_COMPRESSED_FL))
+    {
+      if (fh != get_handle ())
+	NtClose (fh);
+      fh = NULL;
+      if ((get_access () & (GENERIC_WRITE | GENERIC_READ))
+	  != (GENERIC_WRITE | GENERIC_READ))
+	{
+	  status = NtOpenFile (&fh, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
+			       pc.init_reopen_attr (attr, get_handle ()), &io,
+			       FILE_SHARE_VALID_FLAGS,
+			       FILE_SYNCHRONOUS_IO_NONALERT
+			       | FILE_OPEN_FOR_BACKUP_INTENT);
+	  if (!NT_SUCCESS (status))
+	    {
+	      fh = get_handle ();
+	      __seterrno_from_nt_status (status);
+	      goto out;
+	    }
+	}
+      comp = (flags & FS_COMPRESSED_FL)
+	     ? COMPRESSION_FORMAT_DEFAULT : COMPRESSION_FORMAT_NONE;
+      status = NtFsControlFile (fh, NULL, NULL, NULL, &io,
+				FSCTL_SET_COMPRESSION, &comp, sizeof comp,
+				NULL, 0);
+      if (!NT_SUCCESS (status))
+	{
+	  __seterrno_from_nt_status (status);
+	  goto out;
+	}
+    }
+  if (!pc.isdir() && (flags & FS_ENCRYPT_FL) != (old_flags & FS_ENCRYPT_FL))
+    {
+      tmp_pathbuf tp;
+      PWCHAR path = tp.w_get ();
+      BOOL cret;
+
+      /* EncryptFileW/DecryptFileW needs exclusive access. */
+      if (fh != get_handle ())
+	NtClose (fh);
+      NtClose (get_handle ());
+      set_io_handle (NULL);
+
+      pc.get_wide_win32_path (path);
+      cret = (flags & FS_ENCRYPT_FL)
+	     ? EncryptFileW (path) : DecryptFileW (path, 0);
+      status = NtOpenFile (&fh, get_access (),
+			   pc.get_object_attr (attr, sec_none_nih), &io,
+			   FILE_SHARE_VALID_FLAGS,
+			   FILE_SYNCHRONOUS_IO_NONALERT
+			   | FILE_OPEN_FOR_BACKUP_INTENT);
+      if (!NT_SUCCESS (status))
+	{
+	  __seterrno_from_nt_status (status);
+	  return -1;
+	}
+      set_io_handle (fh);
+      if (!cret)
+	{
+	  __seterrno ();
+	  goto out;
+	}
+    }
+  ret = 0;
+out:
+  status = NtQueryInformationFile (fh, &io, &fbi, sizeof fbi,
+				   FileBasicInformation);
+  if (NT_SUCCESS (status))
+    pc.file_attributes (fbi.FileAttributes);
+  if (fh != get_handle ())
+    NtClose (fh);
+  return ret;
+}
+
+int
+fhandler_disk_file::ioctl (unsigned int cmd, void *p)
+{
+  int ret = -1;
+  uint64_t flags = 0;
+
+  switch (cmd)
+    {
+    case FS_IOC_GETFLAGS:
+      __try
+	{
+	  uint64_t *fp = (uint64_t *) p;
+	  *fp = fs_ioc_getflags ();
+	  ret = 0;
+	}
+      __except (EFAULT) {}
+      __endtry
+      break;
+    case FS_IOC_SETFLAGS:
+      __try
+	{
+	  flags = *(__uint64_t *) p;
+	}
+      __except (EFAULT)
+	{
+	  break;
+	}
+      __endtry
+      if (flags & ~FS_FL_USER_MODIFIABLE)
+	{
+	  set_errno (EINVAL);
+	  break;
+	}
+      ret = fs_ioc_setflags (flags);
+      break;
+    default:
+      ret = fhandler_base::ioctl (cmd, p);
+      break;
+    }
+  syscall_printf ("%d = ioctl_file(%x, %p)", ret, cmd, p);
+  return ret;
+}
+
 fhandler_cygdrive::fhandler_cygdrive () :
   fhandler_disk_file ()
 {
diff --git a/winsup/cygwin/include/cygwin/fs.h b/winsup/cygwin/include/cygwin/fs.h
index 48b0cca..9b4baf3 100644
--- a/winsup/cygwin/include/cygwin/fs.h
+++ b/winsup/cygwin/include/cygwin/fs.h
@@ -10,6 +10,8 @@ details. */
 #ifndef _CYGWIN_FS_H_
 #define _CYGWIN_FS_H_
 
+#include <asm/socket.h>
+
 #define BLKRRPART    0x0000125f
 #define BLKGETSIZE   0x00001260
 #define BLKSSZGET    0x00001268
@@ -19,6 +21,31 @@ details. */
 #define BLKPBSZGET   0x0000127b
 #define BLKGETSIZE64 0x00041268
 
+/* Get/Set file attributes */
+#define FS_IOC_GETFLAGS		_IOR('f', 1, __uint64_t)
+#define FS_IOC_SETFLAGS		_IOW('f', 2, __uint64_t)
+
+/* Inode flags (FS_IOC_GETFLAGS / FS_IOC_SETFLAGS)
+
+   This is loosely following the Linux inode flags.  Basically it's just
+   a convenient way to handle certain aspects of files on Windows which
+   are not covered by POSIX calls, mostly connected to DOS attributes. */
+#define FS_READONLY_FL		0x000000001ULL /* DOS R/O */
+#define FS_HIDDEN_FL		0x000000002ULL /* DOS Hidden */
+#define FS_SYSTEM_FL		0x000000004ULL /* DOS System */
+#define FS_ARCHIVE_FL		0x000000020ULL /* DOS Archive */
+#define FS_TEMP_FL		0x000000100ULL /* DOS Temporary */
+#define FS_SPARSE_FL		0x000000200ULL /* Sparse file */
+#define FS_REPARSE_FL		0x000000400ULL /* Reparse point */
+#define FS_COMPRESSED_FL	0x000000800ULL /* Compressed file */
+#define FS_OFFLINE_FL		0x000001000ULL /* DOS Offline */
+#define FS_NOTINDEXED_FL	0x000002000ULL /* DOS Not context indexed */
+#define FS_ENCRYPT_FL		0x000004000ULL /* Encrypted file */
+#define FS_CASESENS_FL		0x100000000ULL /* Case sensitive dir */
+
+#define FS_FL_USER_VISIBLE	0x100007f27ULL /* User visible flags */
+#define FS_FL_USER_MODIFIABLE	0x100006b27ULL /* User modifiable flags */
+
 /* Flags for renameat2, from /usr/include/linux/fs.h.  For now we
    support only RENAME_NOREPLACE. */
 #define RENAME_NOREPLACE (1 << 0)



More information about the Cygwin-cvs mailing list