OSDN Git Service

* Makefile.in (DLL_OFILES): Add ntea.o.
authorcorinna <corinna>
Sun, 10 Feb 2008 15:43:03 +0000 (15:43 +0000)
committercorinna <corinna>
Sun, 10 Feb 2008 15:43:03 +0000 (15:43 +0000)
* cygwin.din (getxattr, listxattr, removexattr, setxattr, lgetxattr,
llistxattr, lremovexattr, lsetxattr, fgetxattr, flistxattr,
fremovexattr, fsetxattr): Export Linux extended attribute functions.
Sort.
* errno.cc (errmap): Add mappings for ERROR_EAS_DIDNT_FIT,
ERROR_EAS_NOT_SUPPORTED, ERROR_EA_LIST_INCONSISTENT,
ERROR_EA_TABLE_FULL, ERROR_FILE_CORRUPT, ERROR_INVALID_EA_NAME.
* fhandler.h (class fhandler_base): Declare new fgetxattr and
fsetxattr methods.
(class fhandler_disk_file): Ditto.
* fhandler.cc (fhandler_base::fgetxattr): New method.
(fhandler_base::fsetxattr): New method.
* fhandler_disk_file.cc (fhandler_disk_file::fgetxattr): New method.
(fhandler_disk_file::fsetxattr): New method.
* ntdll.h (STATUS_EA_TOO_LARGE): Define.
(STATUS_NONEXISTENT_EA_ENTRY): Define.
(STATUS_NO_EAS_ON_FILE): Define.
* ntea.cc (read_ea): Rewrite for long pathnames and for using with
Linux extended attribute functions.
(write_ea): Ditto.
(getxattr_worker): New static function.
(getxattr): New function.
(lgetxattr): New function.
(fgetxattr): New function.
(listxattr): New function.
(llistxattr): New function.
(flistxattr): New function.
(setxattr_worker): New static function.
(setxattr): New function.
(lsetxattr): New function.
(fsetxattr): New function.
(removexattr): New function.
(lsetxattr): New function.
(fsetxattr): New function.
* security.h (read_ea): Change declaration according to above changes.
(write_ea): Ditto.
* include/cygwin/version.h: Bump API minor version.

winsup/cygwin/ChangeLog
winsup/cygwin/Makefile.in
winsup/cygwin/cygwin.din
winsup/cygwin/errno.cc
winsup/cygwin/fhandler.cc
winsup/cygwin/fhandler.h
winsup/cygwin/fhandler_disk_file.cc
winsup/cygwin/include/cygwin/version.h
winsup/cygwin/ntdll.h
winsup/cygwin/ntea.cc
winsup/cygwin/security.h

index e1d6a10..5570c60 100644 (file)
@@ -1,5 +1,46 @@
 2008-02-10  Corinna Vinschen  <corinna@vinschen.de>
 
+       * Makefile.in (DLL_OFILES): Add ntea.o.
+       * cygwin.din (getxattr, listxattr, removexattr, setxattr, lgetxattr,
+       llistxattr, lremovexattr, lsetxattr, fgetxattr, flistxattr,
+       fremovexattr, fsetxattr): Export Linux extended attribute functions.
+       Sort.
+       * errno.cc (errmap): Add mappings for ERROR_EAS_DIDNT_FIT,
+       ERROR_EAS_NOT_SUPPORTED, ERROR_EA_LIST_INCONSISTENT,
+       ERROR_EA_TABLE_FULL, ERROR_FILE_CORRUPT, ERROR_INVALID_EA_NAME.
+       * fhandler.h (class fhandler_base): Declare new fgetxattr and
+       fsetxattr methods.
+       (class fhandler_disk_file): Ditto.
+       * fhandler.cc (fhandler_base::fgetxattr): New method.
+       (fhandler_base::fsetxattr): New method.
+       * fhandler_disk_file.cc (fhandler_disk_file::fgetxattr): New method.
+       (fhandler_disk_file::fsetxattr): New method.
+       * ntdll.h (STATUS_EA_TOO_LARGE): Define.
+       (STATUS_NONEXISTENT_EA_ENTRY): Define.
+       (STATUS_NO_EAS_ON_FILE): Define.
+       * ntea.cc (read_ea): Rewrite for long pathnames and for using with
+       Linux extended attribute functions.
+       (write_ea): Ditto.
+       (getxattr_worker): New static function.
+       (getxattr): New function.
+       (lgetxattr): New function.
+       (fgetxattr): New function.
+       (listxattr): New function.
+       (llistxattr): New function.
+       (flistxattr): New function.
+       (setxattr_worker): New static function.
+       (setxattr): New function.
+       (lsetxattr): New function.
+       (fsetxattr): New function.
+       (removexattr): New function.
+       (lsetxattr): New function.
+       (fsetxattr): New function.
+       * security.h (read_ea): Change declaration according to above changes.
+       (write_ea): Ditto.
+       * include/cygwin/version.h: Bump API minor version.
+
+2008-02-10  Corinna Vinschen  <corinna@vinschen.de>
+
        * libc/ftw.cc (ftw): Allow nfds < 0 for glibc compatibility.
        * libc/nftw.c (nftw): Ditto.
 
index a92b316..9586bfd 100644 (file)
@@ -136,7 +136,7 @@ DLL_OFILES:=assert.o autoload.o bsdlib.o ctype.o cxx.o cygheap.o cygthread.o \
        fhandler_zero.o flock.o fnmatch.o fork.o fts.o ftw.o getopt.o glob.o \
        grp.o heap.o hookapi.o inet_addr.o inet_network.o init.o ioctl.o ipc.o \
        localtime.o lsearch.o malloc_wrapper.o minires-os-if.o \
-       minires.o miscfuncs.o mktemp.o mmap.o msg.o net.o netdb.o nftw.o \
+       minires.o miscfuncs.o mktemp.o mmap.o msg.o net.o netdb.o nftw.o ntea.o \
        passwd.o path.o pinfo.o pipe.o poll.o posix_ipc.o pthread.o random.o \
        regcomp.o regerror.o regexec.o regfree.o registry.o resource.o rexec.o \
        rcmd.o scandir.o sched.o sec_acl.o sec_auth.o sec_helper.o security.o \
index eb767c5..da6b986 100644 (file)
@@ -28,6 +28,7 @@ sys_nerr = _sys_nerr DATA
 _sys_nerr DATA
 _timezone DATA
 _tzname DATA
+_Exit SIGFE
 a64l NOSIGFE
 abort NOSIGFE
 _abort = abort SIGFE
@@ -348,7 +349,6 @@ execvp SIGFE
 _execvp = execvp SIGFE
 exit = cygwin_exit SIGFE
 _exit SIGFE
-_Exit SIGFE
 exp NOSIGFE
 _exp = exp NOSIGFE
 exp10 NOSIGFE
@@ -432,12 +432,12 @@ _fcvtbuf = fcvtbuf SIGFE
 fcvtf SIGFE
 _fcvtf = fcvtf SIGFE
 fdatasync SIGFE
-fdopendir SIGFE
 fdim NOSIGFE
 fdimf NOSIGFE
 fdopen SIGFE
 _fdopen = fdopen SIGFE
 _fdopen64 = fdopen64 SIGFE
+fdopendir SIGFE
 feof SIGFE
 _feof = feof SIGFE
 ferror SIGFE
@@ -453,6 +453,7 @@ _fgetpos = fgetpos SIGFE
 _fgetpos64 = fgetpos64 SIGFE
 fgets SIGFE
 _fgets = fgets SIGFE
+fgetxattr SIGFE
 fileno SIGFE
 _fileno = fileno SIGFE
 finite NOSIGFE
@@ -461,6 +462,7 @@ finitef NOSIGFE
 _finitef = finitef NOSIGFE
 fiprintf SIGFE
 _fiprintf = fiprintf SIGFE
+flistxattr SIGFE
 flock SIGFE
 flockfile SIGFE
 floor NOSIGFE
@@ -501,6 +503,7 @@ free SIGFE
 _free = free SIGFE
 freeaddrinfo = cygwin_freeaddrinfo SIGFE
 freeifaddrs SIGFE
+fremovexattr SIGFE
 freopen SIGFE
 _freopen = freopen SIGFE
 _freopen64 = freopen64 SIGFE
@@ -520,6 +523,7 @@ _fseeko64 = fseeko64 SIGFE
 fsetpos SIGFE
 _fsetpos = fsetpos SIGFE
 _fsetpos64 = fsetpos64 SIGFE
+fsetxattr SIGFE
 fstat SIGFE
 _fstat = fstat SIGFE
 _fstat64 = fstat64 SIGFE
@@ -695,6 +699,7 @@ getw SIGFE
 _getw = getw SIGFE
 getwd SIGFE
 _getwd = getwd SIGFE
+getxattr SIGFE
 glob SIGFE
 _glob = glob SIGFE
 globfree SIGFE
@@ -849,11 +854,14 @@ lgammaf NOSIGFE
 _lgammaf = lgammaf NOSIGFE
 lgammaf_r NOSIGFE
 _lgammaf_r = lgammaf_r NOSIGFE
+lgetxattr SIGFE
 link SIGFE
 _link = link SIGFE
 listen = cygwin_listen SIGFE
+listxattr SIGFE
 llabs NOSIGFE
 lldiv NOSIGFE
+llistxattr SIGFE
 llrint = _f_llrint NOSIGFE
 llrintf = _f_llrintf NOSIGFE
 llrintl = _f_llrintl NOSIGFE
@@ -887,6 +895,7 @@ longjmp NOSIGFE
 _longjmp = longjmp NOSIGFE
 lrand48 NOSIGFE
 _lrand48 = lrand48 NOSIGFE
+lremovexattr SIGFE
 lrint = _f_lrint NOSIGFE
 lrintf = _f_lrintf NOSIGFE
 lrintl = _f_lrintl NOSIGFE
@@ -896,6 +905,7 @@ lsearch NOSIGFE
 lseek SIGFE
 _lseek = lseek SIGFE
 _lseek64 = lseek64 SIGFE
+lsetxattr SIGFE
 lstat SIGFE
 _lstat = lstat SIGFE
 _lstat64 = lstat64 SIGFE
@@ -1170,6 +1180,7 @@ remainderf NOSIGFE
 _remainderf = remainderf NOSIGFE
 remove SIGFE
 _remove = remove SIGFE
+removexattr SIGFE
 remque NOSIGFE
 remquo NOSIGFE
 remquof NOSIGFE
@@ -1341,6 +1352,7 @@ _setutent = setutent SIGFE
 setutxent SIGFE
 setvbuf SIGFE
 _setvbuf = setvbuf SIGFE
+setxattr SIGFE
 sexecl = sexecve_is_bad SIGFE
 sexecle = sexecve_is_bad SIGFE
 sexeclp = sexecve_is_bad SIGFE
@@ -1349,12 +1361,12 @@ sexecp = sexecve_is_bad SIGFE
 sexecv = sexecve_is_bad SIGFE
 sexecve = sexecve_is_bad SIGFE
 sexecvpe = sexecve_is_bad SIGFE
+shm_open SIGFE
+shm_unlink SIGFE
 shmat SIGFE
 shmctl SIGFE
 shmdt SIGFE
 shmget SIGFE
-shm_open SIGFE
-shm_unlink SIGFE
 shutdown = cygwin_shutdown SIGFE
 sigaction SIGFE
 sigaddset SIGFE
index 50819a9..eebb463 100644 (file)
@@ -62,10 +62,15 @@ static NO_COPY struct
   X (DISK_CORRUPT,             EIO),
   X (DISK_FULL,                        ENOSPC),
   X (DUP_NAME,                 ENOTUNIQ),
+  X (EAS_DIDNT_FIT,            ENOSPC),
+  X (EAS_NOT_SUPPORTED,                ENOTSUP),
+  X (EA_LIST_INCONSISTENT,     EINVAL),
+  X (EA_TABLE_FULL,            ENOSPC),
   X (END_OF_MEDIA,             ENOSPC),
   X (EOM_OVERFLOW,             EIO),
   X (FILEMARK_DETECTED,                EIO),
   X (FILENAME_EXCED_RANGE,     ENAMETOOLONG),
+  X (FILE_CORRUPT,             EEXIST),
   X (FILE_EXISTS,              EEXIST),
   X (FILE_INVALID,             ENXIO),
   X (FILE_NOT_FOUND,           ENOENT),
@@ -76,6 +81,7 @@ static NO_COPY struct
   X (INVALID_BLOCK_LENGTH,     EIO),
   X (INVALID_DATA,             EINVAL),
   X (INVALID_DRIVE,            ENODEV),
+  X (INVALID_EA_NAME,          EINVAL),
   X (INVALID_FUNCTION,         EBADRQC),
   X (INVALID_HANDLE,           EBADF),
   X (INVALID_NAME,             ENOENT),
index a479d1b..9a96117 100644 (file)
@@ -1476,6 +1476,21 @@ fhandler_base::facl (int cmd, int nentries, __aclent32_t *aclbufp)
   return res;
 }
 
+ssize_t
+fhandler_base::fgetxattr (const char *name, void *value, size_t size)
+{
+  set_errno (ENOTSUP);
+  return -1;
+}
+
+int
+fhandler_base::fsetxattr (const char *name, const void *value, size_t size,
+                         int flags)
+{
+  set_errno (ENOTSUP);
+  return -1;
+}
+
 int
 fhandler_base::fadvise (_off64_t offset, _off64_t length, int advice)
 {
index 28f34df..cedf7f9 100644 (file)
@@ -288,6 +288,8 @@ class fhandler_base
   virtual int __stdcall fchmod (mode_t mode) __attribute__ ((regparm (1)));
   virtual int __stdcall fchown (__uid32_t uid, __gid32_t gid) __attribute__ ((regparm (2)));
   virtual int __stdcall facl (int, int, __acl32 *) __attribute__ ((regparm (3)));
+  virtual ssize_t __stdcall fgetxattr (const char *, void *, size_t) __attribute__ ((regparm (3)));
+  virtual int __stdcall fsetxattr (const char *, const void *, size_t, int) __attribute__ ((regparm (3)));
   virtual int __stdcall fadvise (_off64_t, _off64_t, int) __attribute__ ((regparm (3)));
   virtual int __stdcall ftruncate (_off64_t, bool) __attribute__ ((regparm (3)));
   virtual int __stdcall link (const char *) __attribute__ ((regparm (2)));
@@ -686,6 +688,8 @@ class fhandler_disk_file: public fhandler_base
   int __stdcall fchmod (mode_t mode) __attribute__ ((regparm (1)));
   int __stdcall fchown (__uid32_t uid, __gid32_t gid) __attribute__ ((regparm (2)));
   int __stdcall facl (int, int, __acl32 *) __attribute__ ((regparm (3)));
+  ssize_t __stdcall fgetxattr (const char *, void *, size_t) __attribute__ ((regparm (3)));
+  int __stdcall fsetxattr (const char *, const void *, size_t, int) __attribute__ ((regparm (3)));
   int __stdcall fadvise (_off64_t, _off64_t, int) __attribute__ ((regparm (3)));
   int __stdcall ftruncate (_off64_t, bool) __attribute__ ((regparm (3)));
   int __stdcall link (const char *) __attribute__ ((regparm (2)));
index f660bb0..a3c6566 100644 (file)
@@ -946,6 +946,33 @@ cant_access_acl:
   return res;
 }
 
+ssize_t
+fhandler_disk_file::fgetxattr (const char *name, void *value, size_t size)
+{
+  OBJECT_ATTRIBUTES attr;
+
+  if (pc.is_fs_special ())
+    {
+      set_errno (ENOTSUP);
+      return -1;
+    }
+  return read_ea (get_handle (), pc, name, (char *) value, size);
+}
+
+int
+fhandler_disk_file::fsetxattr (const char *name, const void *value, size_t size,
+                              int flags)
+{
+  OBJECT_ATTRIBUTES attr;
+
+  if (pc.is_fs_special ())
+    {
+      set_errno (ENOTSUP);
+      return -1;
+    }
+  return write_ea (get_handle (), pc, name, (const char *) value, size, flags);
+}
+
 int
 fhandler_disk_file::fadvise (_off64_t offset, _off64_t length, int advice)
 {
index 52e6a98..6690ebc 100644 (file)
@@ -322,12 +322,15 @@ details. */
       179: Export _f_llrint, _f_llrintf, _f_llrintl, _f_lrint, _f_lrintf,
           _f_lrintl, _f_rint, _f_rintf, _f_rintl, llrint, llrintf, llrintl,
           rintl, lrintl, and redirect exports of lrint, lrintf, rint, rintf.
+      180: Export getxattr, lgetxattr, fgetxattr, listxattr, llistxattr,
+          flistxattr, setxattr, lsetxattr, fsetxattr, removexattr,
+          lremovexattr, fremovexattr.
      */
 
      /* Note that we forgot to bump the api for ualarm, strtoll, strtoull */
 
 #define CYGWIN_VERSION_API_MAJOR 0
-#define CYGWIN_VERSION_API_MINOR 179
+#define CYGWIN_VERSION_API_MINOR 180
 
      /* There is also a compatibity version number associated with the
        shared memory regions.  It is incremented when incompatible
index 91ddf28..0036e54 100644 (file)
@@ -25,6 +25,9 @@
 #define STATUS_OBJECT_NAME_NOT_FOUND  ((NTSTATUS) 0xc0000034)
 #define STATUS_OBJECT_PATH_NOT_FOUND  ((NTSTATUS) 0xc000003A)
 #define STATUS_SHARING_VIOLATION      ((NTSTATUS) 0xc0000043)
+#define STATUS_EA_TOO_LARGE           ((NTSTATUS) 0xc0000050)
+#define STATUS_NONEXISTENT_EA_ENTRY   ((NTSTATUS) 0xc0000051)
+#define STATUS_NO_EAS_ON_FILE         ((NTSTATUS) 0xc0000052)
 #define STATUS_DELETE_PENDING         ((NTSTATUS) 0xc0000056)
 #define STATUS_DISK_FULL              ((NTSTATUS) 0xc000007f)
 #define STATUS_WORKING_SET_QUOTA      ((NTSTATUS) 0xc00000a1)
index becb8ab..a5f9c78 100644 (file)
@@ -1,8 +1,6 @@
-/* ntea.cc: code for manipulating NTEA information
+/* ntea.cc: code for manipulating Extended Attributes
 
-   Copyright 1997, 1998, 2000, 2001, 2006 Red Hat, Inc.
-
-   Written by Sergey S. Okhapkin (sos@prospect.com.ru)
+   Copyright 1997, 1998, 2000, 2001, 2006, 2008 Red Hat, Inc.
 
 This file is part of Cygwin.
 
@@ -11,162 +9,485 @@ Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 details. */
 
 #include "winsup.h"
-#include <stdlib.h>
-#include <ntdef.h>
+#include "cygtls.h"
+#include "cygerrno.h"
 #include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "pinfo.h"
+#include "cygheap.h"
+#include <ntdef.h>
 #include "ntdll.h"
+#include <stdlib.h>
+#include <attr/xattr.h>
 
-/* Default to not using NTEA information */
-bool allow_ntea;
-
-/*
- * read_ea - read file's Extended Attribute.
- *
- * Parameters:
- *     file    - pointer to filename
- *     attrname- pointer to EA name (case insensitiv)
- *     attrbuf - pointer to buffer to store EA's value.
- *     len     - length of attrbuf.
- * Return value:
- *     0       - if file or attribute "attrname" not found.
- *     N       - number of bytes stored in attrbuf if success.
- *     -1      - attrbuf too small for EA value.
- */
+#define MAX_EA_NAME_LEN    256
+#define MAX_EA_VALUE_LEN 65536
 
-int __stdcall
-read_ea (HANDLE hdl, const char *file, const char *attrname, char *attrbuf,
-        int len)
+/* At least one maximum sized entry fits. */
+#define EA_BUFSIZ (sizeof (FILE_FULL_EA_INFORMATION) + MAX_EA_NAME_LEN \
+                  + MAX_EA_VALUE_LEN)
+
+#define NEXT_FEA(p) ((PFILE_FULL_EA_INFORMATION) (p->NextEntryOffset \
+                    ? (char *) p + p->NextEntryOffset : NULL))
+
+ssize_t __stdcall
+read_ea (HANDLE hdl, path_conv &pc, const char *name, char *value, size_t size)
 {
+  OBJECT_ATTRIBUTES attr;
+  NTSTATUS status;
   IO_STATUS_BLOCK io;
-
-  /* Prepare buffer which receives the result. */
-  ULONG flen = sizeof (FILE_FULL_EA_INFORMATION) + strlen (attrname)
-              + len + 1;
-  PFILE_FULL_EA_INFORMATION fea = (PFILE_FULL_EA_INFORMATION) alloca (flen);
-  /* Prepare buffer specifying the EA to search for. */
-  ULONG glen = sizeof (FILE_GET_EA_INFORMATION) + strlen (attrname);
-  PFILE_GET_EA_INFORMATION gea = (PFILE_GET_EA_INFORMATION) alloca (glen);
-  gea->NextEntryOffset = 0;
-  gea->EaNameLength = strlen (attrname);
-  strcpy (gea->EaName, attrname);
-
-  /* If no incoming hdl is given, the loop only runs once, trying to
-     open the file and to query the EA.  If an incoming hdl is given,
-     the loop runs twice, first trying to query with the given hdl.
-     If this fails it tries to open the file and to query with that
-     handle again. */
+  ssize_t ret = -1;
   HANDLE h = hdl;
-  NTSTATUS status = STATUS_SUCCESS;
-  int ret = 0;
+  ULONG glen = 0;
+  PFILE_GET_EA_INFORMATION gea = NULL;
+  PFILE_FULL_EA_INFORMATION fea;
+  /* We have to store the latest EaName to compare with the next one, since
+     ZwQueryEaFile has a bug when accessing files on a remote share.  It
+     returns the last EA entry of the file infinitely.  Even utilizing the
+     optional EaIndex only helps marginally.  If you use that, the last
+     EA in the file is returned twice. */
+  char lastname[MAX_EA_NAME_LEN];
+
+  myfault efault;
+  if (efault.faulted (EFAULT))
+    goto out;
+
+  pc.get_object_attr (attr, sec_none_nih);
+
+  debug_printf ("read_ea (%S, %s, %p, %lu)",
+               attr.ObjectName, name, value, size);
+
+  fea = (PFILE_FULL_EA_INFORMATION) alloca (EA_BUFSIZ);
+  
+  if (name)
+    {
+      size_t nlen;
+
+      /* Samba hides the user namespace from Windows clients.  If we try to
+        retrieve a user namespace item, we remove the leading namespace from
+        the name, otherwise the search fails. */
+      if (pc.fs_is_samba ())
+       if (ascii_strncasematch (name, "user.", 5))
+         name += 5;
+       else
+         {
+           set_errno (ENOATTR);
+           goto out;
+         }
+
+      if ((nlen = strlen (name)) >= MAX_EA_NAME_LEN)
+        {
+         set_errno (EINVAL);
+         return -1;
+       }
+      glen = sizeof (FILE_GET_EA_INFORMATION) + nlen;
+      gea = (PFILE_GET_EA_INFORMATION) alloca (glen);
+
+      gea->NextEntryOffset = 0;
+      gea->EaNameLength = nlen;
+      strcpy (gea->EaName, name);
+    }
+
   while (true)
     {
-      if (!hdl && (h = CreateFile (file, FILE_READ_EA,
-                                  FILE_SHARE_READ | FILE_SHARE_WRITE,
-                                  &sec_none_nih, OPEN_EXISTING,
-                                  FILE_FLAG_BACKUP_SEMANTICS, NULL))
-                 == INVALID_HANDLE_VALUE)
+      if (h)
        {
-         debug_printf ("Opening %s for querying EA %s failed, %E",
-                       file, attrname);
-         goto out;
+         status = NtQueryEaFile (h, &io, fea, EA_BUFSIZ, TRUE, gea, glen,
+                                 NULL, TRUE);
+         if (status != STATUS_ACCESS_DENIED || !hdl)
+           break;
        }
-      status = NtQueryEaFile (h, &io, fea, flen, FALSE, gea, glen, NULL, TRUE);
-      if (NT_SUCCESS (status) || !hdl)
+      status = NtOpenFile (&h, READ_CONTROL | FILE_READ_EA, &attr, &io,
+                          FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT);
+      if (!NT_SUCCESS (status))
        break;
-      debug_printf ("1. chance, %x = NtQueryEaFile (%s, %s), Win32 error %d",
-                   status, file, attrname, RtlNtStatusToDosError (status));
       hdl = NULL;
     }
-  if (!hdl)
-    CloseHandle (h);
   if (!NT_SUCCESS (status))
     {
-      ret = -1;
-      debug_printf ("%x = NtQueryEaFile (%s, %s), Win32 error %d",
-                   status, file, attrname, RtlNtStatusToDosError (status));
+      if (status == STATUS_NO_EAS_ON_FILE)
+       ret = 0;
+      else if (status == STATUS_NONEXISTENT_EA_ENTRY)
+       /* Actually this error code is either never generated, or it was only
+          generated in some old and long forgotton NT version.  See below. */
+        set_errno (ENOATTR);
+      else
+       __seterrno_from_nt_status (status);
+      goto out;
     }
-  if (!fea->EaValueLength)
-    ret = 0;
-  else
+  if (name)
     {
-      memcpy (attrbuf, fea->EaName + fea->EaNameLength + 1,
-             fea->EaValueLength);
+      if (size > 0)
+       {
+         if (size < fea->EaValueLength)
+           {
+             set_errno (ERANGE);
+             goto out;
+           }
+         /* Another weird behaviour of ZwQueryEaFile.  If you ask for a
+            specific EA which is not present in the file's EA list, you don't
+            get a useful error code like STATUS_NONEXISTENT_EA_ENTRY.  Rather
+            ZwQueryEaFile returns success with the entry's EaValueLength
+            set to 0. */
+         if (!fea->EaValueLength)
+           {
+             set_errno (ENOATTR);
+             goto out;
+           }
+         else
+           memcpy (value, fea->EaName + fea->EaNameLength + 1,
+                   fea->EaValueLength);
+       }
       ret = fea->EaValueLength;
     }
+  else
+    {
+      ret = 0;
+      do
+        {
+         if (pc.fs_is_samba ())        /* See below. */
+           fea->EaNameLength += 5;
+         if (size > 0)
+           {
+             if ((size_t) ret + fea->EaNameLength + 1 > size)
+               {
+                 set_errno (ERANGE);
+                 goto out;
+               }
+             /* Samba hides the user namespace from Windows clients.  We add
+                it in EA listings to keep tools like attr/getfattr/setfattr
+                happy. */
+             char tmpbuf[MAX_EA_NAME_LEN * 2], *tp = tmpbuf;
+             if (pc.fs_is_samba ())
+               tp = stpcpy (tmpbuf, "user.");
+             stpcpy (tp, fea->EaName);
+             /* NTFS stores all EA names in uppercase unfortunately.  To keep
+                compatibility with ext/xfs EA namespaces and accompanying
+                tools, which expect the namespaces to be lower case, we return
+                EA names in lowercase if the file is on a native NTFS. */
+             if (pc.fs_is_ntfs ())
+               strlwr (tp);
+             tp = stpcpy (value, tmpbuf) + 1;
+             ret += tp - value;
+             value = tp;
+           }
+         else
+           ret += fea->EaNameLength + 1;
+         strcpy (lastname, fea->EaName);
+         status = NtQueryEaFile (h, &io, fea, EA_BUFSIZ, TRUE, NULL, 0,
+                                 NULL, FALSE);
+       }
+      while (NT_SUCCESS (status) && strcmp (lastname, fea->EaName) != 0);
+    }
 
 out:
-  debug_printf ("%d = read_ea (%x, %s, %s, %x, %d)", ret, hdl, file, attrname,
-               attrbuf, len);
+  if (!hdl)
+    CloseHandle (h);
+  debug_printf ("%d = read_ea (%S, %s, %p, %lu)",
+               ret, attr.ObjectName, name, value, size);
   return ret;
 }
 
-/*
- * write_ea - write file's Extended Attribute.
- *
- * Parameters:
- *     file    - pointer to filename
- *     attrname- pointer to EA name (case insensitiv)
- *     attrbuf - pointer to buffer with EA value.
- *     len     - length of attrbuf.
- * Return value:
- *     true if success, false otherwice.
- * Note: if len=0 given EA will be deleted.
- */
-
-BOOL __stdcall
-write_ea (HANDLE hdl, const char *file, const char *attrname,
-         const char *attrbuf, int len)
+int __stdcall
+write_ea (HANDLE hdl, path_conv &pc, const char *name, const char *value,
+         size_t size, int flags)
 {
+  OBJECT_ATTRIBUTES attr;
+  NTSTATUS status;
   IO_STATUS_BLOCK io;
+  int ret = -1;
+  HANDLE h = hdl;
+  PFILE_FULL_EA_INFORMATION fea;
+  ULONG flen;
+  size_t nlen;
 
-  /* Prepare buffer specifying the EA to write back. */
-  ULONG flen = sizeof (FILE_FULL_EA_INFORMATION) + strlen (attrname)
-              + len + 1;
-  PFILE_FULL_EA_INFORMATION fea = (PFILE_FULL_EA_INFORMATION) alloca (flen);
+  myfault efault;
+  if (efault.faulted (EFAULT))
+    goto out;
+
+  pc.get_object_attr (attr, sec_none_nih);
+
+  debug_printf ("write_ea (%S, %s, %p, %lu, %d)",
+               attr.ObjectName, name, value, size, flags);
+
+  /* Samba hides the user namespace from Windows clients.  If we get a
+     user namespace item, we remove the leading namespace from the name.
+     This keeps tools like attr/getfattr/setfattr happy.  Otherwise
+     setting the EA fails as if we don't have the permissions. */
+  if (pc.fs_is_samba () && ascii_strncasematch (name, "user.", 5))
+    name += 5;
+  else
+    {
+      set_errno (EOPNOTSUPP);
+      goto out;
+    }
+
+  /* removexattr is supposed to fail with ENOATTR if the requested EA is not
+     available.  This is equivalent to the XATTR_REPLACE flag for setxattr. */
+  if (!value)
+    flags = XATTR_REPLACE;
+
+  if (flags)
+    {
+      if (flags != XATTR_CREATE && flags != XATTR_REPLACE)
+       {
+         set_errno (EINVAL);
+         goto out;
+       }
+      ssize_t rret = read_ea (hdl, pc, name, NULL, 0);
+      if (flags == XATTR_CREATE && rret > 0)
+        {
+         set_errno (EEXIST);
+         goto out;
+       }
+      if (flags == XATTR_REPLACE && rret < 0)
+       goto out;
+    }
+
+  if ((nlen = strlen (name)) >= MAX_EA_NAME_LEN)
+    {
+      set_errno (EINVAL);
+      goto out;
+    }
+  flen = sizeof (FILE_FULL_EA_INFORMATION) + nlen + 1 + size;
+  fea = (PFILE_FULL_EA_INFORMATION) alloca (flen);
   fea->NextEntryOffset = 0;
   fea->Flags = 0;
-  fea->EaNameLength = strlen (attrname);
-  fea->EaValueLength = len;
-  strcpy (fea->EaName, attrname);
-  memcpy (fea->EaName + fea->EaNameLength + 1, attrbuf, len);
-
-  /* If no incoming hdl is given, the loop only runs once, trying to
-     open the file and to set the EA.  If an incoming hdl is given,
-     the loop runs twice, first trying to set the EA with the given hdl.
-     If this fails it tries to open the file and to set the EA with that
-     handle again. */
-  HANDLE h = hdl;
-  NTSTATUS status = STATUS_SUCCESS;
-  bool ret = false;
+  fea->EaNameLength = nlen;
+  fea->EaValueLength = size;
+  strcpy (fea->EaName, name);
+  if (value)
+    memcpy (fea->EaName + fea->EaNameLength + 1, value, size);
+
   while (true)
     {
-      if (!hdl && (h = CreateFile (file, FILE_READ_EA,
-                                  FILE_SHARE_READ | FILE_SHARE_WRITE,
-                                  &sec_none_nih, OPEN_EXISTING,
-                                  FILE_FLAG_BACKUP_SEMANTICS, NULL))
-                 == INVALID_HANDLE_VALUE)
+      if (h)
        {
-         debug_printf ("Opening %s for setting EA %s failed, %E",
-                       file, attrname);
-         goto out;
+         status = NtSetEaFile (h, &io, fea, flen);
+         if (status != STATUS_ACCESS_DENIED || !hdl)
+           break;
        }
-      status = NtSetEaFile (h, &io, fea, flen);
-      if (NT_SUCCESS (status) || !hdl)
+      status = NtOpenFile (&h, READ_CONTROL | FILE_WRITE_EA, &attr, &io,
+                          FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT);
+      if (!NT_SUCCESS (status))
        break;
-      debug_printf ("1. chance, %x = NtQueryEaFile (%s, %s), Win32 error %d",
-                   status, file, attrname, RtlNtStatusToDosError (status));
       hdl = NULL;
     }
-  if (!hdl)
-    CloseHandle (h);
   if (!NT_SUCCESS (status))
-    debug_printf ("%x = NtQueryEaFile (%s, %s), Win32 error %d",
-                 status, file, attrname, RtlNtStatusToDosError (status));
+    {
+      /* STATUS_EA_TOO_LARGE has a matching Win32 error ERROR_EA_TABLE_FULL.
+        Too bad RtlNtStatusToDosError does not translate STATUS_EA_TOO_LARGE
+        to ERROR_EA_TABLE_FULL, but to ERROR_EA_LIST_INCONSISTENT.  This
+        error code is also returned for STATUS_EA_LIST_INCONSISTENT, which
+        means the incoming EA list is... inconsistent.  For obvious reasons
+        we translate ERROR_EA_LIST_INCONSISTENT to EINVAL, so we have to
+        handle STATUS_EA_TOO_LARGE explicitely here, to get the correct
+        mapping to ENOSPC. */
+      if (status == STATUS_EA_TOO_LARGE)
+       set_errno (ENOSPC);
+      else
+       __seterrno_from_nt_status (status);
+    }
   else
-    ret = true;
+    ret = 0;
 
 out:
-  debug_printf ("%d = write_ea (%x, %s, %s, %x, %d)", ret, hdl, file, attrname,
-               attrbuf, len);
+  if (!hdl)
+    CloseHandle (h);
+  debug_printf ("%d = write_ea (%S, %s, %p, %lu, %d)",
+               ret, attr.ObjectName, name, value, size, flags);
   return ret;
 }
+
+static ssize_t __stdcall
+getxattr_worker (path_conv &pc, const char *name, void *value, size_t size)
+{
+  int res = -1;
+
+  if (pc.error)
+    {
+      debug_printf ("got %d error from build_fh_name", pc.error);
+      set_errno (pc.error);
+    }
+  else if (pc.exists ())
+    {
+      fhandler_base *fh;
+
+      if (!(fh = build_fh_pc (pc)))
+        return -1;
+
+      res = fh->fgetxattr (name, value, size);
+      delete fh;
+    }
+  else
+    set_errno (ENOENT);
+  return res;
+}
+
+extern "C" ssize_t
+getxattr (const char *path, const char *name, void *value, size_t size)
+{
+  if (!name)
+    {
+      set_errno (EINVAL);
+      return -1;
+    }
+  path_conv pc (path, PC_SYM_FOLLOW | PC_POSIX, stat_suffixes);
+  return getxattr_worker (pc, name, value, size);
+}
+
+extern "C" ssize_t
+lgetxattr (const char *path, const char *name, void *value, size_t size)
+{
+  if (!name)
+    {
+      set_errno (EINVAL);
+      return -1;
+    }
+  path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX, stat_suffixes);
+  return getxattr_worker (pc, name, value, size);
+}
+
+extern "C" ssize_t
+fgetxattr (int fd, const char *name, void *value, size_t size)
+{
+  int res;
+
+  if (!name)
+    {
+      set_errno (EINVAL);
+      return -1;
+    }
+  cygheap_fdget cfd (fd);
+  if (cfd < 0)
+    res = -1;
+  else
+    res = cfd->fgetxattr (name, value, size);
+  return res;
+}
+
+extern "C" ssize_t
+listxattr (const char *path, char *list, size_t size)
+{
+  path_conv pc (path, PC_SYM_FOLLOW | PC_POSIX, stat_suffixes);
+  return getxattr_worker (pc, NULL, list, size);
+}
+
+extern "C" ssize_t
+llistxattr (const char *path, char *list, size_t size)
+{
+  path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX, stat_suffixes);
+  return getxattr_worker (pc, NULL, list, size);
+}
+
+extern "C" ssize_t
+flistxattr (int fd, char *list, size_t size)
+{
+  int res;
+
+  cygheap_fdget cfd (fd);
+  if (cfd < 0)
+    res = -1;
+  else
+    res = cfd->fgetxattr (NULL, list, size);
+  return res;
+}
+
+static int __stdcall
+setxattr_worker (path_conv &pc, const char *name, const void *value,
+                size_t size, int flags)
+{
+  int res = -1;
+
+  if (pc.error)
+    {
+      debug_printf ("got %d error from build_fh_name", pc.error);
+      set_errno (pc.error);
+    }
+  else if (pc.exists ())
+    {
+      fhandler_base *fh;
+
+      if (!(fh = build_fh_pc (pc)))
+        return -1;
+
+      res = fh->fsetxattr (name, value, size, flags);
+      delete fh;
+    }
+  else
+    set_errno (ENOENT);
+  return res;
+}
+
+extern "C" int
+setxattr (const char *path, const char *name, const void *value, size_t size,
+         int flags)
+{
+  if (!size)
+    {
+      set_errno (EINVAL);
+      return -1;
+    }
+  path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX, stat_suffixes);
+  return setxattr_worker (pc, name, value, size, flags);
+}
+
+extern "C" int
+lsetxattr (const char *path, const char *name, const void *value, size_t size,
+          int flags)
+{
+  if (!size)
+    {
+      set_errno (EINVAL);
+      return -1;
+    }
+  path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX, stat_suffixes);
+  return setxattr_worker (pc, name, value, size, flags);
+}
+
+extern "C" int
+fsetxattr (int fd, const char *name, const void *value, size_t size, int flags)
+{
+  int res;
+
+  if (!size)
+    {
+      set_errno (EINVAL);
+      return -1;
+    }
+  cygheap_fdget cfd (fd);
+  if (cfd < 0)
+    res = -1;
+  else
+    res = cfd->fsetxattr (name, value, size, flags);
+  return res;
+}
+
+extern "C" int
+removexattr (const char *path, const char *name)
+{
+  path_conv pc (path, PC_SYM_FOLLOW | PC_POSIX, stat_suffixes);
+  return setxattr_worker (pc, name, NULL, 0, 0);
+}
+
+extern "C" int
+lremovexattr (const char *path, const char *name)
+{
+  path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX, stat_suffixes);
+  return setxattr_worker (pc, name, NULL, 0, 0);
+}
+
+extern "C" int
+fremovexattr (int fd, const char *name)
+{
+  int res;
+
+  cygheap_fdget cfd (fd);
+  if (cfd < 0)
+    res = -1;
+  else
+    res = cfd->fsetxattr (name, NULL, 0, 0);
+  return res;
+}
index c697855..ba0a06a 100644 (file)
@@ -423,10 +423,10 @@ extern SECURITY_ATTRIBUTES *__stdcall __sec_user (PVOID sa_buf, PSID sid1, PSID
 extern bool sec_acl (PACL acl, bool original, bool admins, PSID sid1 = NO_SID,
                     PSID sid2 = NO_SID, DWORD access2 = 0);
 
-int __stdcall read_ea (HANDLE hdl, const char *file, const char *attrname,
-                      char *buf, int len);
-BOOL __stdcall write_ea (HANDLE hdl, const char *file, const char *attrname,
-                        const char *buf, int len);
+ssize_t __stdcall read_ea (HANDLE hdl, path_conv &pc, const char *name,
+                          char *value, size_t size);
+int __stdcall write_ea (HANDLE hdl, path_conv &pc, const char *name,
+                       const char *value, size_t size, int flags);
 
 /* Note: sid1 is usually (read: currently always) the current user's
    effective sid (cygheap->user.sid ()). */