Support for reporting owner and group of each file on MS-Windows:
authorEli Zaretskii <eliz@gnu.org>
Fri, 9 May 2008 19:03:52 +0000 (19:03 +0000)
committerEli Zaretskii <eliz@gnu.org>
Fri, 9 May 2008 19:03:52 +0000 (19:03 +0000)
* dired.c (stat_uname, stat_gname): New functions, with special
implementation for w32.
(Ffile_attributes): Use them instead of getpwuid and getgrgid.

* w32.c:
(g_b_init_get_file_security, g_b_init_get_security_descriptor_owner)
(g_b_init_get_security_descriptor_group, g_b_init_is_valid_sid):
New initialization states.
(globals_of_w32): Initialize them to zero.  Initialize the default
group name to "None".
(GetFileSecurity_Name): New global var, the name of the function
to call for GetFileSecurity.
(GetFileSecurity_Proc, GetSecurityDescriptorOwner_Proc)
(GetSecurityDescriptorGroup_Proc, IsValidSid_Proc): New typedefs.
(get_file_security, get_security_descriptor_owner)
(get_security_descriptor_group, is_valid_sid)
(get_file_security_desc, get_rid, get_name_and_id)
(get_file_owner_and_group): New functions.
(stat): Use get_file_security_desc and get_file_owner_and_group to
report the owner and primary group of each file.  Don't ignore the
high 32 bits of file's size, now that st_size is 64-bit wide.  Fix
test when to get true file attributes.
(init_user_info): Use get_rid instead of equivalent inline code.
(fstat): Don't ignore the high 32 bits of file's size.

src/ChangeLog
src/dired.c
src/w32.c

index 888b0f8..94c2dfc 100644 (file)
@@ -1,10 +1,34 @@
 2008-05-09  Eli Zaretskii  <eliz@gnu.org>
 
+       Support for reporting owner and group of each file on MS-Windows:
+       * dired.c (stat_uname, stat_gname): New functions, with special
+       implementation for w32.
+       (Ffile_attributes): Use them instead of getpwuid and getgrgid.
+
        * w32.c: Rename the_passwd_* to dflt_passwd_*.
        (dflt_group_name): New static variable.
        (dflt_group): Renamed from the_group.
        (init_user_info): Init dflt_group fields.  Get user's group name
        from LookupAccountSid.
+       (g_b_init_get_file_security, g_b_init_get_security_descriptor_owner)
+       (g_b_init_get_security_descriptor_group, g_b_init_is_valid_sid):
+       New initialization states.
+       (globals_of_w32): Initialize them to zero.  Initialize the default
+       group name to "None".
+       (GetFileSecurity_Name): New global var, the name of the function
+       to call for GetFileSecurity.
+       (GetFileSecurity_Proc, GetSecurityDescriptorOwner_Proc)
+       (GetSecurityDescriptorGroup_Proc, IsValidSid_Proc): New typedefs.
+       (get_file_security, get_security_descriptor_owner)
+       (get_security_descriptor_group, is_valid_sid)
+       (get_file_security_desc, get_rid, get_name_and_id)
+       (get_file_owner_and_group): New functions.
+       (stat): Use get_file_security_desc and get_file_owner_and_group to
+       report the owner and primary group of each file.  Don't ignore the
+       high 32 bits of file's size, now that st_size is 64-bit wide.  Fix
+       test when to get true file attributes.
+       (init_user_info): Use get_rid instead of equivalent inline code.
+       (fstat): Don't ignore the high 32 bits of file's size.
 
 2008-05-09  Chong Yidong  <cyd@stupidchicken.com>
 
index d3a6e7b..7b4c916 100644 (file)
@@ -899,6 +899,36 @@ make_time (time)
                Fcons (make_number (time & 0177777), Qnil));
 }
 
+static char *
+stat_uname (struct stat *st)
+{
+#ifdef WINDOWSNT
+  return st->st_uname;
+#else
+  struct passwd *pw = (struct passwd *) getpwuid (st->st_uid);
+
+  if (pw)
+    return pw->pw_name;
+  else
+    return NULL;
+#endif
+}
+
+static char *
+stat_gname (struct stat *st)
+{
+#ifdef WINDOWSNT
+  return st->st_gname;
+#else
+  struct group *gr = (struct group *) getgrgid (st->st_gid);
+
+  if (gr)
+    return gr->gr_name;
+  else
+    return NULL;
+#endif
+}
+
 DEFUN ("file-attributes", Ffile_attributes, Sfile_attributes, 1, 2, 0,
        doc: /* Return a list of attributes of file FILENAME.
 Value is nil if specified file cannot be opened.
@@ -935,8 +965,6 @@ Elements of the attribute list are:
   Lisp_Object values[12];
   Lisp_Object encoded;
   struct stat s;
-  struct passwd *pw;
-  struct group *gr;
 #if defined (BSD4_2) || defined (BSD4_3)
   Lisp_Object dirname;
   struct stat sdir;
@@ -945,6 +973,7 @@ Elements of the attribute list are:
   Lisp_Object handler;
   struct gcpro gcpro1;
   EMACS_INT ino;
+  char *uname, *gname;
 
   filename = Fexpand_file_name (filename, Qnil);
 
@@ -987,12 +1016,12 @@ Elements of the attribute list are:
   else
     {
       BLOCK_INPUT;
-      pw = (struct passwd *) getpwuid (s.st_uid);
-      values[2] = (pw ? build_string (pw->pw_name)
-                  : make_fixnum_or_float (s.st_uid));
-      gr = (struct group *) getgrgid (s.st_gid);
-      values[3] = (gr ? build_string (gr->gr_name)
-                  : make_fixnum_or_float (s.st_gid));
+      uname = stat_uname (&s);
+      values[2] = (uname ? build_string (uname)
+                  : make_fixnum_or_float (s.st_uid));
+      gname = stat_gname (&s);
+      values[3] = (gname ? build_string (gname)
+                  : make_fixnum_or_float (s.st_gid));
       UNBLOCK_INPUT;
     }
   values[4] = make_time (s.st_atime);
index 9b4d18a..8dc6162 100644 (file)
--- a/src/w32.c
+++ b/src/w32.c
@@ -106,6 +106,7 @@ typedef HRESULT (WINAPI * ShGetFolderPath_fn)
   (IN HWND, IN int, IN HANDLE, IN DWORD, OUT char *);
 
 void globals_of_w32 ();
+static DWORD get_rid (PSID);
 
 extern Lisp_Object Vw32_downcase_file_names;
 extern Lisp_Object Vw32_generate_fake_inodes;
@@ -132,6 +133,10 @@ static BOOL g_b_init_lookup_account_sid;
 static BOOL g_b_init_get_sid_identifier_authority;
 static BOOL g_b_init_get_sid_sub_authority;
 static BOOL g_b_init_get_sid_sub_authority_count;
+static BOOL g_b_init_get_file_security;
+static BOOL g_b_init_get_security_descriptor_owner;
+static BOOL g_b_init_get_security_descriptor_group;
+static BOOL g_b_init_is_valid_sid;
 
 /*
   BEGIN: Wrapper functions around OpenProcessToken
@@ -160,8 +165,10 @@ GetProcessTimes_Proc get_process_times_fn = NULL;
 
 #ifdef _UNICODE
 const char * const LookupAccountSid_Name = "LookupAccountSidW";
+const char * const GetFileSecurity_Name =  "GetFileSecurityW";
 #else
 const char * const LookupAccountSid_Name = "LookupAccountSidA";
+const char * const GetFileSecurity_Name =  "GetFileSecurityA";
 #endif
 typedef BOOL (WINAPI * LookupAccountSid_Proc) (
     LPCTSTR lpSystemName,
@@ -178,7 +185,22 @@ typedef PDWORD (WINAPI * GetSidSubAuthority_Proc) (
     DWORD n);
 typedef PUCHAR (WINAPI * GetSidSubAuthorityCount_Proc) (
     PSID pSid);
-
+typedef BOOL (WINAPI * GetFileSecurity_Proc) (
+    LPCTSTR lpFileName,
+    SECURITY_INFORMATION RequestedInformation,
+    PSECURITY_DESCRIPTOR pSecurityDescriptor,
+    DWORD nLength,
+    LPDWORD lpnLengthNeeded);
+typedef BOOL (WINAPI * GetSecurityDescriptorOwner_Proc) (
+    PSECURITY_DESCRIPTOR pSecurityDescriptor,
+    PSID *pOwner,
+    LPBOOL lpbOwnerDefaulted);
+typedef BOOL (WINAPI * GetSecurityDescriptorGroup_Proc) (
+    PSECURITY_DESCRIPTOR pSecurityDescriptor,
+    PSID *pGroup,
+    LPBOOL lpbGroupDefaulted);
+typedef BOOL (WINAPI * IsValidSid_Proc) (
+    PSID sid);
 
   /* ** A utility function ** */
 static BOOL
@@ -418,6 +440,114 @@ PUCHAR WINAPI get_sid_sub_authority_count (
   return (s_pfn_Get_Sid_Sub_Authority_Count (pSid));
 }
 
+BOOL WINAPI get_file_security (
+    LPCTSTR lpFileName,
+    SECURITY_INFORMATION RequestedInformation,
+    PSECURITY_DESCRIPTOR pSecurityDescriptor,
+    DWORD nLength,
+    LPDWORD lpnLengthNeeded)
+{
+  static GetFileSecurity_Proc s_pfn_Get_File_Security = NULL;
+  HMODULE hm_advapi32 = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      return FALSE;
+    }
+  if (g_b_init_get_file_security == 0)
+    {
+      g_b_init_get_file_security = 1;
+      hm_advapi32 = LoadLibrary ("Advapi32.dll");
+      s_pfn_Get_File_Security =
+        (GetFileSecurity_Proc) GetProcAddress (
+            hm_advapi32, GetFileSecurity_Name);
+    }
+  if (s_pfn_Get_File_Security == NULL)
+    {
+      return FALSE;
+    }
+  return (s_pfn_Get_File_Security (lpFileName, RequestedInformation,
+                                  pSecurityDescriptor, nLength,
+                                  lpnLengthNeeded));
+}
+
+BOOL WINAPI get_security_descriptor_owner (
+    PSECURITY_DESCRIPTOR pSecurityDescriptor,
+    PSID *pOwner,
+    LPBOOL lpbOwnerDefaulted)
+{
+  static GetSecurityDescriptorOwner_Proc s_pfn_Get_Security_Descriptor_Owner = NULL;
+  HMODULE hm_advapi32 = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      return FALSE;
+    }
+  if (g_b_init_get_security_descriptor_owner == 0)
+    {
+      g_b_init_get_security_descriptor_owner = 1;
+      hm_advapi32 = LoadLibrary ("Advapi32.dll");
+      s_pfn_Get_Security_Descriptor_Owner =
+        (GetSecurityDescriptorOwner_Proc) GetProcAddress (
+            hm_advapi32, "GetSecurityDescriptorOwner");
+    }
+  if (s_pfn_Get_Security_Descriptor_Owner == NULL)
+    {
+      return FALSE;
+    }
+  return (s_pfn_Get_Security_Descriptor_Owner (pSecurityDescriptor, pOwner,
+                                              lpbOwnerDefaulted));
+}
+
+BOOL WINAPI get_security_descriptor_group (
+    PSECURITY_DESCRIPTOR pSecurityDescriptor,
+    PSID *pGroup,
+    LPBOOL lpbGroupDefaulted)
+{
+  static GetSecurityDescriptorGroup_Proc s_pfn_Get_Security_Descriptor_Group = NULL;
+  HMODULE hm_advapi32 = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      return FALSE;
+    }
+  if (g_b_init_get_security_descriptor_group == 0)
+    {
+      g_b_init_get_security_descriptor_group = 1;
+      hm_advapi32 = LoadLibrary ("Advapi32.dll");
+      s_pfn_Get_Security_Descriptor_Group =
+        (GetSecurityDescriptorGroup_Proc) GetProcAddress (
+            hm_advapi32, "GetSecurityDescriptorGroup");
+    }
+  if (s_pfn_Get_Security_Descriptor_Group == NULL)
+    {
+      return FALSE;
+    }
+  return (s_pfn_Get_Security_Descriptor_Group (pSecurityDescriptor, pGroup,
+                                              lpbGroupDefaulted));
+}
+
+BOOL WINAPI is_valid_sid (
+    PSID sid)
+{
+  static IsValidSid_Proc s_pfn_Is_Valid_Sid = NULL;
+  HMODULE hm_advapi32 = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      return FALSE;
+    }
+  if (g_b_init_is_valid_sid == 0)
+    {
+      g_b_init_is_valid_sid = 1;
+      hm_advapi32 = LoadLibrary ("Advapi32.dll");
+      s_pfn_Is_Valid_Sid =
+        (IsValidSid_Proc) GetProcAddress (
+            hm_advapi32, "IsValidSid");
+    }
+  if (s_pfn_Is_Valid_Sid == NULL)
+    {
+      return FALSE;
+    }
+  return (s_pfn_Is_Valid_Sid (sid));
+}
+
 /*
   END: Wrapper functions around OpenProcessToken
   and other functions in advapi32.dll that are only
@@ -616,8 +746,6 @@ init_user_info ()
   TOKEN_USER   user_token;
   TOKEN_PRIMARY_GROUP group_token;
 
-  /* "None" is the default group name on standalone workstations.  */
-  strcpy (dflt_group_name, "None");
   if (open_process_token (GetCurrentProcess (), TOKEN_QUERY, &token)
       && get_token_information (token, TokenUser,
                                (PVOID)buf, sizeof (buf), &trash)
@@ -636,34 +764,14 @@ init_user_info ()
        {
          /* Use the last sub-authority value of the RID, the relative
             portion of the SID, as user/group ID. */
-         DWORD n_subauthorities =
-           *get_sid_sub_authority_count (user_token.User.Sid);
-
-         if (n_subauthorities < 1)
-           dflt_passwd.pw_uid = 0;     /* the "World" RID */
-         else
-           {
-             dflt_passwd.pw_uid =
-               *get_sid_sub_authority (user_token.User.Sid,
-                                       n_subauthorities - 1);
-           }
+         dflt_passwd.pw_uid = get_rid (user_token.User.Sid);
 
-         /* Get group id */
+         /* Get group id and name.  */
          if (get_token_information (token, TokenPrimaryGroup,
                                     (PVOID)buf, sizeof (buf), &trash))
            {
              memcpy (&group_token, buf, sizeof (group_token));
-             n_subauthorities =
-               *get_sid_sub_authority_count (group_token.PrimaryGroup);
-
-             if (n_subauthorities < 1)
-               dflt_passwd.pw_gid = 0; /* the "World" RID */
-             else
-               {
-                 dflt_passwd.pw_gid =
-                   *get_sid_sub_authority (group_token.PrimaryGroup,
-                                           n_subauthorities - 1);
-               }
+             dflt_passwd.pw_gid = get_rid (group_token.PrimaryGroup);
              dlength = sizeof (domain);
              if (lookup_account_sid (NULL, group_token.PrimaryGroup,
                                      gname, &glength, NULL, &dlength,
@@ -2548,6 +2656,136 @@ generate_inode_val (const char * name)
 
 #endif
 
+static PSECURITY_DESCRIPTOR
+get_file_security_desc (const char *fname)
+{
+  PSECURITY_DESCRIPTOR psd = NULL;
+  DWORD sd_len, err;
+  SECURITY_INFORMATION si = OWNER_SECURITY_INFORMATION
+    | GROUP_SECURITY_INFORMATION  /* | DACL_SECURITY_INFORMATION */ ;
+
+  if (!get_file_security (fname, si, psd, 0, &sd_len))
+    {
+      err = GetLastError ();
+      if (err != ERROR_INSUFFICIENT_BUFFER)
+       return NULL;
+    }
+
+  psd = xmalloc (sd_len);
+  if (!get_file_security (fname, si, psd, sd_len, &sd_len))
+    {
+      xfree (psd);
+      return NULL;
+    }
+
+  return psd;
+}
+
+static DWORD
+get_rid (PSID sid)
+{
+  unsigned n_subauthorities;
+
+  /* Use the last sub-authority value of the RID, the relative
+     portion of the SID, as user/group ID. */
+  n_subauthorities = *get_sid_sub_authority_count (sid);
+  if (n_subauthorities < 1)
+    return 0;  /* the "World" RID */
+  return *get_sid_sub_authority (sid, n_subauthorities - 1);
+}
+
+#define UID 1
+#define GID 2
+
+static int
+get_name_and_id (PSECURITY_DESCRIPTOR psd, const char *fname,
+                int *id, char *nm, int what)
+{
+  PSID sid = NULL;
+  char machine[MAX_COMPUTERNAME_LENGTH+1];
+  BOOL dflt;
+  SID_NAME_USE ignore;
+  char name[UNLEN+1];
+  DWORD name_len = sizeof (name);
+  char domain[1024];
+  DWORD domain_len = sizeof(domain);
+  char *mp = NULL;
+  int use_dflt = 0;
+  int result;
+
+  if (what == UID)
+    result = get_security_descriptor_owner (psd, &sid, &dflt);
+  else if (what == GID)
+    result = get_security_descriptor_group (psd, &sid, &dflt);
+  else
+    result = 0;
+
+  if (!result || !is_valid_sid (sid))
+    use_dflt = 1;
+  else
+    {
+      /* If FNAME is a UNC, we need to lookup account on the
+        specified machine.  */
+      if (IS_DIRECTORY_SEP (fname[0]) && IS_DIRECTORY_SEP (fname[1])
+         && fname[2] != '\0')
+       {
+         const char *s;
+         char *p;
+
+         for (s = fname + 2, p = machine;
+              *s && !IS_DIRECTORY_SEP (*s); s++, p++)
+           *p = *s;
+         *p = '\0';
+         mp = machine;
+       }
+
+      if (!lookup_account_sid (mp, sid, name, &name_len,
+                              domain, &domain_len, &ignore)
+         || name_len > UNLEN+1)
+       use_dflt = 1;
+      else
+       {
+         *id = get_rid (sid);
+         strcpy (nm, name);
+       }
+    }
+  return use_dflt;
+}
+
+static void
+get_file_owner_and_group (
+    PSECURITY_DESCRIPTOR psd,
+    const char *fname,
+    struct stat *st)
+{
+  int dflt_usr = 0, dflt_grp = 0;
+
+  if (!psd)
+    {
+      dflt_usr = 1;
+      dflt_grp = 1;
+    }
+  else
+    {
+      if (get_name_and_id (psd, fname, &st->st_uid, st->st_uname, UID))
+       dflt_usr = 1;
+      if (get_name_and_id (psd, fname, &st->st_gid, st->st_gname, GID))
+       dflt_grp = 1;
+    }
+  /* Consider files to belong to current user/group, if we cannot get
+     more accurate information.  */
+  if (dflt_usr)
+    {
+      st->st_uid = dflt_passwd.pw_uid;
+      strcpy (st->st_uname, dflt_passwd.pw_name);
+    }
+  if (dflt_grp)
+    {
+      st->st_gid = dflt_passwd.pw_gid;
+      strcpy (st->st_gname, dflt_group.gr_name);
+    }
+}
+
 /* MSVC stat function can't cope with UNC names and has other bugs, so
    replace it with our own.  This also allows us to calculate consistent
    inode values without hacks in the main Emacs code. */
@@ -2561,6 +2799,7 @@ stat (const char * path, struct stat * buf)
   int permission;
   int len;
   int rootdir = FALSE;
+  PSECURITY_DESCRIPTOR psd = NULL;
 
   if (path == NULL || buf == NULL)
     {
@@ -2658,9 +2897,9 @@ stat (const char * path, struct stat * buf)
        }
     }
 
-  if (!NILP (Vw32_get_true_file_attributes)
-      && !(EQ (Vw32_get_true_file_attributes, Qlocal) && 
-          GetDriveType (name) == DRIVE_FIXED)
+  if (!(NILP (Vw32_get_true_file_attributes)
+       || (EQ (Vw32_get_true_file_attributes, Qlocal) &&
+           GetDriveType (name) != DRIVE_FIXED)))
       /* No access rights required to get info.  */
       && (fh = CreateFile (name, 0, 0, NULL, OPEN_EXISTING,
                           FILE_FLAG_BACKUP_SEMANTICS, NULL))
@@ -2710,6 +2949,8 @@ stat (const char * path, struct stat * buf)
            }
        }
       CloseHandle (fh);
+      psd = get_file_security_desc (name);
+      get_file_owner_and_group (psd, name, buf);
     }
   else
     {
@@ -2718,7 +2959,11 @@ stat (const char * path, struct stat * buf)
        S_IFDIR : S_IFREG;
       buf->st_nlink = 1;
       fake_inode = 0;
+
+      get_file_owner_and_group (NULL, name, buf);
     }
+  if (psd)
+    xfree (psd);
 
 #if 0
   /* Not sure if there is any point in this.  */
@@ -2738,16 +2983,14 @@ stat (const char * path, struct stat * buf)
   else
     buf->st_ino = fake_inode;
 
-  /* consider files to belong to current user */
-  buf->st_uid = dflt_passwd.pw_uid;
-  buf->st_gid = dflt_passwd.pw_gid;
-
   /* volume_info is set indirectly by map_w32_filename */
   buf->st_dev = volume_info.serialnum;
   buf->st_rdev = volume_info.serialnum;
 
 
-  buf->st_size = wfd.nFileSizeLow;
+  buf->st_size = wfd.nFileSizeHigh;
+  buf->st_size <<= 32;
+  buf->st_size += wfd.nFileSizeLow;
 
   /* Convert timestamps to Unix format. */
   buf->st_mtime = convert_time (wfd.ftLastWriteTime);
@@ -2826,14 +3069,20 @@ fstat (int desc, struct stat * buf)
   else
     buf->st_ino = fake_inode;
 
-  /* consider files to belong to current user */
+  /* Consider files to belong to current user.
+     FIXME: this should use GetSecurityInfo API, but it is only
+     available for _WIN32_WINNT >= 0x501.  */
   buf->st_uid = dflt_passwd.pw_uid;
   buf->st_gid = dflt_passwd.pw_gid;
+  strcpy (buf->st_uname, dflt_passwd.pw_name);
+  strcpy (buf->st_gname, dflt_group.gr_name);
 
   buf->st_dev = info.dwVolumeSerialNumber;
   buf->st_rdev = info.dwVolumeSerialNumber;
 
-  buf->st_size = info.nFileSizeLow;
+  buf->st_size = info.nFileSizeHigh;
+  buf->st_size <<= 32;
+  buf->st_size += info.nFileSizeLow;
 
   /* Convert timestamps to Unix format. */
   buf->st_mtime = convert_time (info.ftLastWriteTime);
@@ -4350,11 +4599,18 @@ globals_of_w32 ()
   g_b_init_get_sid_identifier_authority = 0;
   g_b_init_get_sid_sub_authority = 0;
   g_b_init_get_sid_sub_authority_count = 0;
+  g_b_init_get_file_security = 0;
+  g_b_init_get_security_descriptor_owner = 0;
+  g_b_init_get_security_descriptor_group = 0;
+  g_b_init_is_valid_sid = 0;
   /* The following sets a handler for shutdown notifications for
      console apps. This actually applies to Emacs in both console and
      GUI modes, since we had to fool windows into thinking emacs is a
      console application to get console mode to work.  */
   SetConsoleCtrlHandler(shutdown_handler, TRUE);
+
+  /* "None" is the default group name on standalone workstations.  */
+  strcpy (dflt_group_name, "None");
 }
 
 /* end of w32.c */