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
19 #include <attr/xattr.h>
20 #include <cygwin/limits.h>
22 /* On storage the `user.` prefix is not included but the terminating null byte
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_
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))
44 read_ea (HANDLE hdl
, path_conv
&pc
, const char *name
, char *value
, size_t size
)
46 OBJECT_ATTRIBUTES attr
;
52 PFILE_GET_EA_INFORMATION gea
= NULL
;
53 PFILE_FULL_EA_INFORMATION fea
;
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_
];
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
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
);
84 fea
= (PFILE_FULL_EA_INFORMATION
) tp
.w_get ();
90 /* For compatibility with Linux, we only allow user xattrs and
91 return ENOTSUP otherwise. */
92 if (ascii_strncasematch (name
, "user.", 5))
100 if ((nlen
= strlen (name
)) >= _XATTR_NAME_MAX_ONDISK_
)
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
);
117 status
= NtQueryEaFile (h
, &io
, fea
, EA_BUFSIZ
, TRUE
, gea
, glen
,
119 if (status
!= STATUS_ACCESS_DENIED
|| !hdl
)
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
))
130 if (!NT_SUCCESS (status
))
134 case STATUS_NO_EAS_ON_FILE
:
137 case STATUS_INVALID_DEVICE_REQUEST
:
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
);
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,
157 __seterrno_from_nt_status (status
);
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
169 if (!fea
->EaValueLength
)
176 if (size
< fea
->EaValueLength
)
181 memcpy (value
, fea
->EaName
+ fea
->EaNameLength
+ 1,
184 ret
= fea
->EaValueLength
;
191 fea
->EaNameLength
+= 5; /* "user." */
194 if ((size_t) ret
+ fea
->EaNameLength
+ 1 > size
)
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 ())
212 tp
= stpcpy (value
, tmpbuf
) + 1;
217 ret
+= fea
->EaNameLength
+ 1;
218 strcpy (lastname
, fea
->EaName
);
219 status
= NtQueryEaFile (h
, &io
, fea
, EA_BUFSIZ
, TRUE
, NULL
, 0,
222 while (NT_SUCCESS (status
) && strcmp (lastname
, fea
->EaName
) != 0);
229 debug_printf ("%d = read_ea(%S, %s, %p, %lu)",
230 ret
, attr
.ObjectName
, name
, value
, size
);
235 write_ea (HANDLE hdl
, path_conv
&pc
, const char *name
, const char *value
,
236 size_t size
, int flags
)
238 OBJECT_ATTRIBUTES attr
;
243 PFILE_FULL_EA_INFORMATION fea
;
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
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
);
269 /* For compatibility with Linux, we only allow user xattrs and
270 return ENOTSUP otherwise. */
271 if (!ascii_strncasematch (name
, "user.", 5))
277 /* removexattr is supposed to fail with ENOATTR if the requested EA is
278 not available. This is equivalent to XATTR_REPLACE for setxattr. */
280 flags
= XATTR_REPLACE
;
284 if (flags
!= XATTR_CREATE
&& flags
!= XATTR_REPLACE
)
289 ssize_t rret
= read_ea (hdl
, pc
, name
, NULL
, 0);
290 if (flags
== XATTR_CREATE
&& rret
> 0)
295 if (flags
== XATTR_REPLACE
&& rret
< 0)
299 /* Skip "user." prefix. */
302 if ((nlen
= strlen (name
)) >= _XATTR_NAME_MAX_ONDISK_
)
307 flen
= sizeof (FILE_FULL_EA_INFORMATION
) + nlen
+ 1 + size
;
308 fea
= (PFILE_FULL_EA_INFORMATION
) alloca (flen
);
309 fea
->NextEntryOffset
= 0;
311 fea
->EaNameLength
= nlen
;
312 fea
->EaValueLength
= size
;
313 strcpy (fea
->EaName
, name
);
315 memcpy (fea
->EaName
+ fea
->EaNameLength
+ 1, value
, size
);
321 status
= NtSetEaFile (h
, &io
, fea
, flen
);
322 if (status
!= STATUS_ACCESS_DENIED
|| !hdl
)
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
))
333 if (!NT_SUCCESS (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. */
349 case STATUS_INVALID_DEVICE_REQUEST
:
353 __seterrno_from_nt_status (status
);
364 debug_printf ("%d = write_ea(%S, %s, %p, %lu, %d)",
365 ret
, attr
.ObjectName
, name
, value
, size
, flags
);
370 getxattr_worker (path_conv
&pc
, const char *name
, void *value
, size_t size
)
376 debug_printf ("got %d error from path_conv", pc
.error
);
377 set_errno (pc
.error
);
379 else if (pc
.exists ())
383 if (!(fh
= build_fh_pc (pc
)))
386 res
= fh
->fgetxattr (name
, value
, size
);
395 getxattr (const char *path
, const char *name
, void *value
, size_t size
)
402 path_conv
pc (path
, PC_SYM_FOLLOW
| PC_POSIX
, stat_suffixes
);
403 return getxattr_worker (pc
, name
, value
, size
);
407 lgetxattr (const char *path
, const char *name
, void *value
, size_t size
)
414 path_conv
pc (path
, PC_SYM_NOFOLLOW
| PC_POSIX
, stat_suffixes
);
415 return getxattr_worker (pc
, name
, value
, size
);
419 fgetxattr (int fd
, const char *name
, void *value
, size_t size
)
428 cygheap_fdget
cfd (fd
);
431 else if (cfd
->get_flags () & O_PATH
)
437 res
= cfd
->fgetxattr (name
, value
, size
);
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
);
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
);
456 flistxattr (int fd
, char *list
, size_t size
)
460 cygheap_fdget
cfd (fd
);
463 else if (cfd
->get_flags () & O_PATH
)
469 res
= cfd
->fgetxattr (NULL
, list
, size
);
474 setxattr_worker (path_conv
&pc
, const char *name
, const void *value
,
475 size_t size
, int flags
)
481 debug_printf ("got %d error from path_conv", pc
.error
);
482 set_errno (pc
.error
);
484 else if (pc
.exists ())
488 if (!(fh
= build_fh_pc (pc
)))
491 res
= fh
->fsetxattr (name
, value
, size
, flags
);
500 setxattr (const char *path
, const char *name
, const void *value
, size_t size
,
508 path_conv
pc (path
, PC_SYM_NOFOLLOW
| PC_POSIX
, stat_suffixes
);
509 return setxattr_worker (pc
, name
, value
, size
, flags
);
513 lsetxattr (const char *path
, const char *name
, const void *value
, size_t size
,
521 path_conv
pc (path
, PC_SYM_NOFOLLOW
| PC_POSIX
, stat_suffixes
);
522 return setxattr_worker (pc
, name
, value
, size
, flags
);
526 fsetxattr (int fd
, const char *name
, const void *value
, size_t size
, int flags
)
535 cygheap_fdget
cfd (fd
);
538 else if (cfd
->get_flags () & O_PATH
)
544 res
= cfd
->fsetxattr (name
, value
, size
, flags
);
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);
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);
563 fremovexattr (int fd
, const char *name
)
567 cygheap_fdget
cfd (fd
);
570 else if (cfd
->get_flags () & O_PATH
)
576 res
= cfd
->fsetxattr (name
, NULL
, 0, 0);