diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index e1d6a109c..5570c60f8 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,44 @@ +2008-02-10 Corinna Vinschen + + * 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 * libc/ftw.cc (ftw): Allow nfds < 0 for glibc compatibility. diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in index a92b316d5..9586bfdd2 100644 --- a/winsup/cygwin/Makefile.in +++ b/winsup/cygwin/Makefile.in @@ -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 \ diff --git a/winsup/cygwin/cygwin.din b/winsup/cygwin/cygwin.din index eb767c55c..da6b986dd 100644 --- a/winsup/cygwin/cygwin.din +++ b/winsup/cygwin/cygwin.din @@ -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 diff --git a/winsup/cygwin/errno.cc b/winsup/cygwin/errno.cc index 50819a952..eebb46305 100644 --- a/winsup/cygwin/errno.cc +++ b/winsup/cygwin/errno.cc @@ -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), diff --git a/winsup/cygwin/fhandler.cc b/winsup/cygwin/fhandler.cc index a479d1b28..9a961172f 100644 --- a/winsup/cygwin/fhandler.cc +++ b/winsup/cygwin/fhandler.cc @@ -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) { diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index 28f34df45..cedf7f987 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -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))); diff --git a/winsup/cygwin/fhandler_disk_file.cc b/winsup/cygwin/fhandler_disk_file.cc index f660bb06c..a3c6566f5 100644 --- a/winsup/cygwin/fhandler_disk_file.cc +++ b/winsup/cygwin/fhandler_disk_file.cc @@ -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) { diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h index 52e6a98d1..6690ebcb5 100644 --- a/winsup/cygwin/include/cygwin/version.h +++ b/winsup/cygwin/include/cygwin/version.h @@ -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 diff --git a/winsup/cygwin/ntdll.h b/winsup/cygwin/ntdll.h index 91ddf28dd..0036e54ce 100644 --- a/winsup/cygwin/ntdll.h +++ b/winsup/cygwin/ntdll.h @@ -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) diff --git a/winsup/cygwin/ntea.cc b/winsup/cygwin/ntea.cc index becb8ab2e..a5f9c7832 100644 --- a/winsup/cygwin/ntea.cc +++ b/winsup/cygwin/ntea.cc @@ -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 -#include +#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 #include "ntdll.h" +#include +#include -/* Default to not using NTEA information */ -bool allow_ntea; +#define MAX_EA_NAME_LEN 256 +#define MAX_EA_VALUE_LEN 65536 -/* - * 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. - */ +/* At least one maximum sized entry fits. */ +#define EA_BUFSIZ (sizeof (FILE_FULL_EA_INFORMATION) + MAX_EA_NAME_LEN \ + + MAX_EA_VALUE_LEN) -int __stdcall -read_ea (HANDLE hdl, const char *file, const char *attrname, char *attrbuf, - int 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); - 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) -{ - IO_STATUS_BLOCK io; - - /* 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); - 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; - 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) - { - debug_printf ("Opening %s for setting EA %s failed, %E", - file, attrname); - goto out; - } - status = NtSetEaFile (h, &io, fea, flen); - if (NT_SUCCESS (status) || !hdl) - 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)); - else - ret = true; - -out: - debug_printf ("%d = write_ea (%x, %s, %s, %x, %d)", ret, hdl, file, attrname, - attrbuf, len); + debug_printf ("%d = read_ea (%S, %s, %p, %lu)", + ret, attr.ObjectName, name, value, size); return ret; } + +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; + + 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 = nlen; + fea->EaValueLength = size; + strcpy (fea->EaName, name); + if (value) + memcpy (fea->EaName + fea->EaNameLength + 1, value, size); + + while (true) + { + if (h) + { + status = NtSetEaFile (h, &io, fea, flen); + if (status != STATUS_ACCESS_DENIED || !hdl) + break; + } + status = NtOpenFile (&h, READ_CONTROL | FILE_WRITE_EA, &attr, &io, + FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT); + if (!NT_SUCCESS (status)) + break; + hdl = NULL; + } + if (!NT_SUCCESS (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 = 0; + +out: + 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; +} diff --git a/winsup/cygwin/security.h b/winsup/cygwin/security.h index c69785563..ba0a06a94 100644 --- a/winsup/cygwin/security.h +++ b/winsup/cygwin/security.h @@ -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 ()). */