diff options
author | Corinna Vinschen <corinna@vinschen.de> | 2008-02-10 15:43:04 +0000 |
---|---|---|
committer | Corinna Vinschen <corinna@vinschen.de> | 2008-02-10 15:43:04 +0000 |
commit | 50450dcc5f6f54bf1d8af7174fc950ca1449a9a3 (patch) | |
tree | d74c694e6412f33cbca27d8103b749fa308502ec /winsup/cygwin/ntea.cc | |
parent | bebb25961c1ed29d217b1a40fc69c77ebdc18bcd (diff) | |
download | cygnal-50450dcc5f6f54bf1d8af7174fc950ca1449a9a3.tar.gz cygnal-50450dcc5f6f54bf1d8af7174fc950ca1449a9a3.tar.bz2 cygnal-50450dcc5f6f54bf1d8af7174fc950ca1449a9a3.zip |
* 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.
Diffstat (limited to 'winsup/cygwin/ntea.cc')
-rw-r--r-- | winsup/cygwin/ntea.cc | 561 |
1 files changed, 441 insertions, 120 deletions
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 <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; +} |