Cygwin: (mostly) drop NT4 and Samba < 3.0 support
[newlib-cygwin.git] / winsup / cygwin / ntea.cc
blob70815649c3749ccb83c74283e8fdd837660caf9f
1 /* ntea.cc: code for manipulating Extended Attributes
3 This file is part of Cygwin.
5 This software is a copyrighted work licensed under the terms of the
6 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
7 details. */
9 #include "winsup.h"
10 #include "cygtls.h"
11 #include "security.h"
12 #include "path.h"
13 #include "fhandler.h"
14 #include "dtable.h"
15 #include "cygheap.h"
16 #include "ntdll.h"
17 #include "tls_pbuf.h"
18 #include <stdlib.h>
19 #include <attr/xattr.h>
20 #include <cygwin/limits.h>
22 /* On storage the `user.` prefix is not included but the terminating null byte
23 is needed.*/
24 #define _XATTR_NAME_MAX_ONDISK_ (XATTR_NAME_MAX - strlen("user.") + 1)
26 /* At least one maximum sized entry fits.
27 CV 2014-04-04: NtQueryEaFile function chokes on buffers bigger than 64K
28 with STATUS_INVALID_PARAMETER if the handle points to a file
29 on a remote share, at least on Windows 7 and later.
30 In theory the buffer should have a size of
32 sizeof (FILE_FULL_EA_INFORMATION) + _XATTR_NAME_MAX_ONDISK_
33 + XATTR_SIZE_MAX
35 (65804 bytes), but we're opting for simplicity here, and
36 a 64K buffer has the advantage that we can use a tmp_pathbuf
37 buffer, rather than having to alloca 64K from stack. */
38 #define EA_BUFSIZ XATTR_SIZE_MAX
40 #define NEXT_FEA(p) ((PFILE_FULL_EA_INFORMATION) (p->NextEntryOffset \
41 ? (char *) p + p->NextEntryOffset : NULL))
43 ssize_t
44 read_ea (HANDLE hdl, path_conv &pc, const char *name, char *value, size_t size)
46 OBJECT_ATTRIBUTES attr;
47 NTSTATUS status;
48 IO_STATUS_BLOCK io;
49 ssize_t ret = -1;
50 HANDLE h = hdl;
51 ULONG glen = 0;
52 PFILE_GET_EA_INFORMATION gea = NULL;
53 PFILE_FULL_EA_INFORMATION fea;
54 tmp_pathbuf tp;
55 /* We have to store the latest EaName to compare with the next one, since
56 NtQueryEaFile has a bug when accessing files on a remote share. It
57 returns the last EA entry of the file infinitely. Even utilizing the
58 optional EaIndex only helps marginally. If you use that, the last
59 EA in the file is returned twice. */
60 char lastname[_XATTR_NAME_MAX_ONDISK_];
62 __try
64 pc.get_object_attr (attr, sec_none_nih);
66 debug_printf ("read_ea (%S, %s, %p, %lu)",
67 attr.ObjectName, name, value, size);
69 /* Early open if handle is NULL. This allows to return error codes like
70 ENOENT before we actually check for the correctness of the EA name and
71 stuff like that. */
72 if (!hdl)
74 status = NtOpenFile (&h, READ_CONTROL | FILE_READ_EA, &attr, &io,
75 FILE_SHARE_VALID_FLAGS,
76 FILE_OPEN_FOR_BACKUP_INTENT);
77 if (!NT_SUCCESS (status))
79 __seterrno_from_nt_status (status);
80 __leave;
84 fea = (PFILE_FULL_EA_INFORMATION) tp.w_get ();
86 if (name)
88 size_t nlen;
90 /* For compatibility with Linux, we only allow user xattrs and
91 return ENOTSUP otherwise. */
92 if (ascii_strncasematch (name, "user.", 5))
93 name += 5;
94 else
96 set_errno (ENOTSUP);
97 __leave;
100 if ((nlen = strlen (name)) >= _XATTR_NAME_MAX_ONDISK_)
102 set_errno (EINVAL);
103 __leave;
105 glen = sizeof (FILE_GET_EA_INFORMATION) + nlen;
106 gea = (PFILE_GET_EA_INFORMATION) alloca (glen);
108 gea->NextEntryOffset = 0;
109 gea->EaNameLength = nlen;
110 strcpy (gea->EaName, name);
113 while (true)
115 if (h)
117 status = NtQueryEaFile (h, &io, fea, EA_BUFSIZ, TRUE, gea, glen,
118 NULL, TRUE);
119 if (status != STATUS_ACCESS_DENIED || !hdl)
120 break;
121 pc.init_reopen_attr (attr, h);
123 status = NtOpenFile (&h, READ_CONTROL | FILE_READ_EA, &attr, &io,
124 FILE_SHARE_VALID_FLAGS,
125 FILE_OPEN_FOR_BACKUP_INTENT);
126 if (!NT_SUCCESS (status))
127 break;
128 hdl = NULL;
130 if (!NT_SUCCESS (status))
132 switch (status)
134 case STATUS_NO_EAS_ON_FILE:
135 ret = 0;
136 break;
137 case STATUS_INVALID_DEVICE_REQUEST:
138 set_errno (ENOTSUP);
139 break;
140 case STATUS_NOT_FOUND:
141 /* STATUS_NOT_FOUND is returned when calling NtQueryEaFile on NFS.
142 In theory this should mean that the file just has no EAs, but
143 in fact NFS doesn't support EAs, other than the EAs which are
144 used for NFS requests. We're playing safe and convert
145 STATUS_NOT_FOUND to ENOATTR, unless we're on NFS, where we
146 convert it to ENOTSUP. */
147 set_errno (pc.fs_is_nfs () ? ENOTSUP : ENOATTR);
148 break;
149 case STATUS_NONEXISTENT_EA_ENTRY:
150 /* Actually STATUS_NONEXISTENT_EA_ENTRY is either never generated,
151 or it was only generated in some old and long forgotton NT
152 version. See below. For safty reasons, we handle it here,
153 nevertheless. */
154 set_errno (ENOATTR);
155 break;
156 default:
157 __seterrno_from_nt_status (status);
158 break;
160 __leave;
162 if (name)
164 /* Another weird behaviour of NtQueryEaFile. If you ask for a
165 specific EA which is not present in the file's EA list, you don't
166 get a useful error code like STATUS_NONEXISTENT_EA_ENTRY. Rather
167 NtQueryEaFile returns success with the entry's EaValueLength
168 set to 0. */
169 if (!fea->EaValueLength)
171 set_errno (ENOATTR);
172 __leave;
174 if (size > 0)
176 if (size < fea->EaValueLength)
178 set_errno (ERANGE);
179 __leave;
181 memcpy (value, fea->EaName + fea->EaNameLength + 1,
182 fea->EaValueLength);
184 ret = fea->EaValueLength;
186 else
188 ret = 0;
191 fea->EaNameLength += 5; /* "user." */
192 if (size > 0)
194 if ((size_t) ret + fea->EaNameLength + 1 > size)
196 set_errno (ERANGE);
197 __leave;
199 /* For compatibility with Linux, we always prepend "user." to
200 the attribute name, so effectively we only support user
201 attributes from a application point of view. */
202 char tmpbuf[_XATTR_NAME_MAX_ONDISK_ * 2];
203 char *tp = stpcpy (tmpbuf, "user.");
204 stpcpy (tp, fea->EaName);
205 /* NTFS stores all EA names in uppercase unfortunately. To
206 keep compatibility with ext/xfs EA namespaces and
207 accompanying tools, which expect the namespaces to be
208 lower case, we return EA names in lowercase if the file
209 is on a native NTFS. */
210 if (pc.fs_is_ntfs ())
211 strlwr (tp);
212 tp = stpcpy (value, tmpbuf) + 1;
213 ret += tp - value;
214 value = tp;
216 else
217 ret += fea->EaNameLength + 1;
218 strcpy (lastname, fea->EaName);
219 status = NtQueryEaFile (h, &io, fea, EA_BUFSIZ, TRUE, NULL, 0,
220 NULL, FALSE);
222 while (NT_SUCCESS (status) && strcmp (lastname, fea->EaName) != 0);
225 __except (EFAULT) {}
226 __endtry
227 if (!hdl && h)
228 NtClose (h);
229 debug_printf ("%d = read_ea(%S, %s, %p, %lu)",
230 ret, attr.ObjectName, name, value, size);
231 return ret;
235 write_ea (HANDLE hdl, path_conv &pc, const char *name, const char *value,
236 size_t size, int flags)
238 OBJECT_ATTRIBUTES attr;
239 NTSTATUS status;
240 IO_STATUS_BLOCK io;
241 int ret = -1;
242 HANDLE h = hdl;
243 PFILE_FULL_EA_INFORMATION fea;
244 ULONG flen;
245 size_t nlen;
247 __try
249 pc.get_object_attr (attr, sec_none_nih);
251 debug_printf ("write_ea (%S, %s, %p, %lu, %d)",
252 attr.ObjectName, name, value, size, flags);
254 /* Early open if handle is NULL. This allows to return error codes like
255 ENOENT before we actually check for the correctness of the EA name and
256 stuff like that. */
257 if (!hdl)
259 status = NtOpenFile (&h, READ_CONTROL | FILE_WRITE_EA, &attr, &io,
260 FILE_SHARE_VALID_FLAGS,
261 FILE_OPEN_FOR_BACKUP_INTENT);
262 if (!NT_SUCCESS (status))
264 __seterrno_from_nt_status (status);
265 __leave;
269 /* For compatibility with Linux, we only allow user xattrs and
270 return ENOTSUP otherwise. */
271 if (!ascii_strncasematch (name, "user.", 5))
273 set_errno (ENOTSUP);
274 __leave;
277 /* removexattr is supposed to fail with ENOATTR if the requested EA is
278 not available. This is equivalent to XATTR_REPLACE for setxattr. */
279 if (!value)
280 flags = XATTR_REPLACE;
282 if (flags)
284 if (flags != XATTR_CREATE && flags != XATTR_REPLACE)
286 set_errno (EINVAL);
287 __leave;
289 ssize_t rret = read_ea (hdl, pc, name, NULL, 0);
290 if (flags == XATTR_CREATE && rret > 0)
292 set_errno (EEXIST);
293 __leave;
295 if (flags == XATTR_REPLACE && rret < 0)
296 __leave;
299 /* Skip "user." prefix. */
300 name += 5;
302 if ((nlen = strlen (name)) >= _XATTR_NAME_MAX_ONDISK_)
304 set_errno (EINVAL);
305 __leave;
307 flen = sizeof (FILE_FULL_EA_INFORMATION) + nlen + 1 + size;
308 fea = (PFILE_FULL_EA_INFORMATION) alloca (flen);
309 fea->NextEntryOffset = 0;
310 fea->Flags = 0;
311 fea->EaNameLength = nlen;
312 fea->EaValueLength = size;
313 strcpy (fea->EaName, name);
314 if (value)
315 memcpy (fea->EaName + fea->EaNameLength + 1, value, size);
317 while (true)
319 if (h)
321 status = NtSetEaFile (h, &io, fea, flen);
322 if (status != STATUS_ACCESS_DENIED || !hdl)
323 break;
324 pc.init_reopen_attr (attr, h);
326 status = NtOpenFile (&h, READ_CONTROL | FILE_WRITE_EA, &attr, &io,
327 FILE_SHARE_VALID_FLAGS,
328 FILE_OPEN_FOR_BACKUP_INTENT);
329 if (!NT_SUCCESS (status))
330 break;
331 hdl = NULL;
333 if (!NT_SUCCESS (status))
335 switch (status)
337 case STATUS_EA_TOO_LARGE:
338 /* STATUS_EA_TOO_LARGE has a matching Win32 error code
339 ERROR_EA_TABLE_FULL. For some reason RtlNtStatusToDosError
340 does not translate STATUS_EA_TOO_LARGE to ERROR_EA_TABLE_FULL,
341 but instead to ERROR_EA_LIST_INCONSISTENT. This error code is
342 also returned for STATUS_EA_LIST_INCONSISTENT, which means the
343 incoming EA list is... inconsistent. For obvious reasons we
344 translate ERROR_EA_LIST_INCONSISTENT to EINVAL, so we have to
345 handle STATUS_EA_TOO_LARGE explicitely here, to get the correct
346 mapping to ENOSPC. */
347 set_errno (ENOSPC);
348 break;
349 case STATUS_INVALID_DEVICE_REQUEST:
350 set_errno (ENOTSUP);
351 break;
352 default:
353 __seterrno_from_nt_status (status);
354 break;
357 else
358 ret = 0;
360 __except (EFAULT) {}
361 __endtry
362 if (!hdl && h)
363 NtClose (h);
364 debug_printf ("%d = write_ea(%S, %s, %p, %lu, %d)",
365 ret, attr.ObjectName, name, value, size, flags);
366 return ret;
369 static ssize_t
370 getxattr_worker (path_conv &pc, const char *name, void *value, size_t size)
372 int res = -1;
374 if (pc.error)
376 debug_printf ("got %d error from path_conv", pc.error);
377 set_errno (pc.error);
379 else if (pc.exists ())
381 fhandler_base *fh;
383 if (!(fh = build_fh_pc (pc)))
384 return -1;
386 res = fh->fgetxattr (name, value, size);
387 delete fh;
389 else
390 set_errno (ENOENT);
391 return res;
394 extern "C" ssize_t
395 getxattr (const char *path, const char *name, void *value, size_t size)
397 if (!name)
399 set_errno (EINVAL);
400 return -1;
402 path_conv pc (path, PC_SYM_FOLLOW | PC_POSIX, stat_suffixes);
403 return getxattr_worker (pc, name, value, size);
406 extern "C" ssize_t
407 lgetxattr (const char *path, const char *name, void *value, size_t size)
409 if (!name)
411 set_errno (EINVAL);
412 return -1;
414 path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX, stat_suffixes);
415 return getxattr_worker (pc, name, value, size);
418 extern "C" ssize_t
419 fgetxattr (int fd, const char *name, void *value, size_t size)
421 int res;
423 if (!name)
425 set_errno (EINVAL);
426 return -1;
428 cygheap_fdget cfd (fd);
429 if (cfd < 0)
430 res = -1;
431 else if (cfd->get_flags () & O_PATH)
433 set_errno (EBADF);
434 res = -1;
436 else
437 res = cfd->fgetxattr (name, value, size);
438 return res;
441 extern "C" ssize_t
442 listxattr (const char *path, char *list, size_t size)
444 path_conv pc (path, PC_SYM_FOLLOW | PC_POSIX, stat_suffixes);
445 return getxattr_worker (pc, NULL, list, size);
448 extern "C" ssize_t
449 llistxattr (const char *path, char *list, size_t size)
451 path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX, stat_suffixes);
452 return getxattr_worker (pc, NULL, list, size);
455 extern "C" ssize_t
456 flistxattr (int fd, char *list, size_t size)
458 int res;
460 cygheap_fdget cfd (fd);
461 if (cfd < 0)
462 res = -1;
463 else if (cfd->get_flags () & O_PATH)
465 set_errno (EBADF);
466 res = -1;
468 else
469 res = cfd->fgetxattr (NULL, list, size);
470 return res;
473 static int
474 setxattr_worker (path_conv &pc, const char *name, const void *value,
475 size_t size, int flags)
477 int res = -1;
479 if (pc.error)
481 debug_printf ("got %d error from path_conv", pc.error);
482 set_errno (pc.error);
484 else if (pc.exists ())
486 fhandler_base *fh;
488 if (!(fh = build_fh_pc (pc)))
489 return -1;
491 res = fh->fsetxattr (name, value, size, flags);
492 delete fh;
494 else
495 set_errno (ENOENT);
496 return res;
499 extern "C" int
500 setxattr (const char *path, const char *name, const void *value, size_t size,
501 int flags)
503 if (!size)
505 set_errno (EINVAL);
506 return -1;
508 path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX, stat_suffixes);
509 return setxattr_worker (pc, name, value, size, flags);
512 extern "C" int
513 lsetxattr (const char *path, const char *name, const void *value, size_t size,
514 int flags)
516 if (!size)
518 set_errno (EINVAL);
519 return -1;
521 path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX, stat_suffixes);
522 return setxattr_worker (pc, name, value, size, flags);
525 extern "C" int
526 fsetxattr (int fd, const char *name, const void *value, size_t size, int flags)
528 int res;
530 if (!size)
532 set_errno (EINVAL);
533 return -1;
535 cygheap_fdget cfd (fd);
536 if (cfd < 0)
537 res = -1;
538 else if (cfd->get_flags () & O_PATH)
540 set_errno (EBADF);
541 res = -1;
543 else
544 res = cfd->fsetxattr (name, value, size, flags);
545 return res;
548 extern "C" int
549 removexattr (const char *path, const char *name)
551 path_conv pc (path, PC_SYM_FOLLOW | PC_POSIX, stat_suffixes);
552 return setxattr_worker (pc, name, NULL, 0, 0);
555 extern "C" int
556 lremovexattr (const char *path, const char *name)
558 path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX, stat_suffixes);
559 return setxattr_worker (pc, name, NULL, 0, 0);
562 extern "C" int
563 fremovexattr (int fd, const char *name)
565 int res;
567 cygheap_fdget cfd (fd);
568 if (cfd < 0)
569 res = -1;
570 else if (cfd->get_flags () & O_PATH)
572 set_errno (EBADF);
573 res = -1;
575 else
576 res = cfd->fsetxattr (name, NULL, 0, 0);
577 return res;