2 * ea.c - Processing of EA's
4 * This module is part of ntfs-3g library
6 * Copyright (c) 2014-2021 Jean-Pierre Andre
8 * This program/include file is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as published
10 * by the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program/include file is distributed in the hope that it will be
14 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program (in the main directory of the NTFS-3G
20 * distribution in the file COPYING); if not, write to the Free Software
21 * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
47 #include <sys/mkdev.h>
49 #ifdef MAJOR_IN_SYSMACROS
50 #include <sys/sysmacros.h>
64 static const char lxdev
[] = "$LXDEV";
65 static const char lxmod
[] = "$LXMOD";
69 * Create a needed attribute (EA or EA_INFORMATION)
71 * Returns 0 if successful,
72 * -1 otherwise, with errno indicating why it failed.
75 static int ntfs_need_ea(ntfs_inode
*ni
, ATTR_TYPES type
, int size
, int flags
)
81 if (!ntfs_attr_exist(ni
,type
, AT_UNNAMED
,0)) {
82 if (!(flags
& XATTR_REPLACE
)) {
84 * no needed attribute : add one,
85 * apparently, this does not feed the new value in
86 * Note : NTFS version must be >= 3
88 if (ni
->vol
->major_ver
>= 3) {
89 res
= ntfs_attr_add(ni
, type
,
90 AT_UNNAMED
,0,&dummy
,(s64
)size
);
92 NInoFileNameSetDirty(ni
);
108 * Restore the old EA_INFORMATION or delete the current one,
109 * when EA cannot be updated.
111 * As this is used in the context of some other error, the caller
112 * is responsible for returning the proper error, and errno is
114 * Only double errors are logged here.
117 static void restore_ea_info(ntfs_attr
*nai
, const EA_INFORMATION
*old_ea_info
)
124 written
= ntfs_attr_pwrite(nai
, 0, sizeof(EA_INFORMATION
),
126 if ((size_t)written
!= sizeof(EA_INFORMATION
)) {
127 ntfs_log_error("Could not restore the EA_INFORMATION,"
128 " possible inconsistency in inode %lld\n",
129 (long long)nai
->ni
->mft_no
);
132 if (ntfs_attr_rm(nai
)) {
133 ntfs_log_error("Could not delete the EA_INFORMATION,"
134 " possible inconsistency in inode %lld\n",
135 (long long)nai
->ni
->mft_no
);
142 * Update both EA and EA_INFORMATION
145 static int ntfs_update_ea(ntfs_inode
*ni
, const char *value
, size_t size
,
146 const EA_INFORMATION
*ea_info
,
147 const EA_INFORMATION
*old_ea_info
)
154 nai
= ntfs_attr_open(ni
, AT_EA_INFORMATION
, AT_UNNAMED
, 0);
156 na
= ntfs_attr_open(ni
, AT_EA
, AT_UNNAMED
, 0);
159 * Set EA_INFORMATION first, it is easier to
160 * restore the old value, if setting EA fails.
162 if (ntfs_attr_pwrite(nai
, 0, sizeof(EA_INFORMATION
),
164 != (s64
)sizeof(EA_INFORMATION
)) {
167 if (((na
->data_size
> (s64
)size
)
168 && ntfs_attr_truncate(na
, size
))
169 || (ntfs_attr_pwrite(na
, 0, size
, value
)
179 ntfs_attr_close(nai
);
187 * Return the existing EA
189 * The EA_INFORMATION is not examined and the consistency of the
190 * existing EA is not checked.
192 * If successful, the full attribute is returned unchanged
193 * and its size is returned.
194 * If the designated buffer is too small, the needed size is
195 * returned, and the buffer is left unchanged.
196 * If there is an error, a negative value is returned and errno
197 * is set according to the error.
200 int ntfs_get_ntfs_ea(ntfs_inode
*ni
, char *value
, size_t size
)
206 if (ntfs_attr_exist(ni
, AT_EA
, AT_UNNAMED
, 0)) {
207 ea_buf
= ntfs_attr_readall(ni
, AT_EA
, (ntfschar
*)NULL
, 0,
210 if (value
&& (ea_size
<= (s64
)size
))
211 memcpy(value
, ea_buf
, ea_size
);
215 ntfs_log_error("Failed to read EA from inode %lld\n",
216 (long long)ni
->mft_no
);
228 * Set a new EA, and set EA_INFORMATION accordingly
230 * This is roughly the same as ZwSetEaFile() on Windows, however
231 * the "offset to next" of the last EA should not be cleared.
233 * Consistency of the new EA is first checked.
235 * EA_INFORMATION is set first, and it is restored to its former
236 * state if setting EA fails.
238 * Returns 0 if successful
239 * a negative value if an error occurred.
242 int ntfs_set_ntfs_ea(ntfs_inode
*ni
, const char *value
, size_t size
, int flags
)
244 EA_INFORMATION ea_info
;
245 EA_INFORMATION
*old_ea_info
;
256 if (value
&& (size
> 0)) {
257 /* do consistency checks */
263 while (ok
&& (offs
< size
)) {
264 p_ea
= (const EA_ATTR
*)&value
[offs
];
265 nextoffs
= offs
+ le32_to_cpu(p_ea
->next_entry_offset
);
266 /* null offset to next not allowed */
267 ok
= (nextoffs
> offs
)
268 && (nextoffs
<= size
)
271 /* zero sized value are allowed */
272 && ((offs
+ offsetof(EA_ATTR
,name
)
273 + p_ea
->name_length
+ 1
274 + le16_to_cpu(p_ea
->value_length
))
276 && ((offs
+ offsetof(EA_ATTR
,name
)
277 + p_ea
->name_length
+ 1
278 + le16_to_cpu(p_ea
->value_length
))
280 && !p_ea
->name
[p_ea
->name_length
];
281 /* name not checked, as chkdsk accepts any chars */
283 if (p_ea
->flags
& NEED_EA
)
286 * Assume ea_packed includes :
287 * 4 bytes for header (flags and lengths)
291 ea_packed
+= 5 + p_ea
->name_length
292 + le16_to_cpu(p_ea
->value_length
);
297 * EA and REPARSE_POINT compatibility not checked any more,
298 * required by Windows 10, but having both may lead to
299 * problems with earlier versions.
302 ea_info
.ea_length
= cpu_to_le16(ea_packed
);
303 ea_info
.need_ea_count
= cpu_to_le16(ea_count
);
304 ea_info
.ea_query_length
= cpu_to_le32(nextoffs
);
308 /* Try to save the old EA_INFORMATION */
309 if (ntfs_attr_exist(ni
, AT_EA_INFORMATION
,
311 old_ea_info
= ntfs_attr_readall(ni
,
313 (ntfschar
*)NULL
, 0, &old_ea_size
);
316 * no EA or EA_INFORMATION : add them
318 if (!ntfs_need_ea(ni
, AT_EA_INFORMATION
,
319 sizeof(EA_INFORMATION
), flags
)
320 && !ntfs_need_ea(ni
, AT_EA
, 0, flags
)) {
321 res
= ntfs_update_ea(ni
, value
, size
,
322 &ea_info
, old_ea_info
);
340 * Remove the EA (including EA_INFORMATION)
342 * EA_INFORMATION is removed first, and it is restored to its former
343 * state if removing EA fails.
345 * Returns 0, or -1 if there is a problem
348 int ntfs_remove_ntfs_ea(ntfs_inode
*ni
)
350 EA_INFORMATION
*old_ea_info
;
359 * open and delete the EA_INFORMATION and the EA
361 nai
= ntfs_attr_open(ni
, AT_EA_INFORMATION
, AT_UNNAMED
, 0);
363 na
= ntfs_attr_open(ni
, AT_EA
, AT_UNNAMED
, 0);
365 /* Try to save the old EA_INFORMATION */
366 old_ea_info
= ntfs_attr_readall(ni
,
368 (ntfschar
*)NULL
, 0, &old_ea_size
);
369 res
= ntfs_attr_rm(na
);
370 NInoFileNameSetDirty(ni
);
372 res
= ntfs_attr_rm(nai
);
373 if (res
&& old_ea_info
) {
375 * Failed to remove the EA, try to
376 * restore the EA_INFORMATION
382 ntfs_log_error("Failed to remove the"
383 " EA_INFORMATION from inode %lld\n",
384 (long long)ni
->mft_no
);
389 /* EA_INFORMATION present, but no EA */
390 res
= ntfs_attr_rm(nai
);
391 NInoFileNameSetDirty(ni
);
393 ntfs_attr_close(nai
);
403 return (res
? -1 : 0);
407 * Check for the presence of an EA "$LXDEV" (used by WSL)
408 * and return its value as a device address
410 * Returns zero if successful
411 * -1 if failed, with errno set
414 int ntfs_ea_check_wsldev(ntfs_inode
*ni
, dev_t
*rdevp
)
430 bufsize
= 256; /* expected to be enough */
431 buf
= (char*)malloc(bufsize
);
433 lth
= ntfs_get_ntfs_ea(ni
, buf
, bufsize
);
434 /* retry if short buf */
438 buf
= (char*)malloc(bufsize
);
440 lth
= ntfs_get_ntfs_ea(ni
, buf
, bufsize
);
443 if (buf
&& (lth
> 0) && (lth
<= bufsize
)) {
447 p_ea
= (const EA_ATTR
*)&buf
[offset
];
448 next
= le32_to_cpu(p_ea
->next_entry_offset
);
449 found
= ((next
> (int)(sizeof(lxdev
) + sizeof(device
)))
450 && (p_ea
->name_length
== (sizeof(lxdev
) - 1))
451 && (p_ea
->value_length
452 == const_cpu_to_le16(sizeof(device
)))
453 && !memcmp(p_ea
->name
, lxdev
, sizeof(lxdev
)));
456 } while (!found
&& (next
> 0) && (offset
< lth
));
458 /* beware of alignment */
459 memcpy(&device
, &p_ea
->name
[p_ea
->name_length
+ 1],
461 *rdevp
= makedev(le32_to_cpu(device
.major
),
462 le32_to_cpu(device
.minor
));
470 int ntfs_ea_set_wsl_not_symlink(ntfs_inode
*ni
, mode_t type
, dev_t dev
)
478 struct EA_LXMOD
{ /* always inserted */
480 char name
[sizeof(lxmod
)];
481 char value
[sizeof(mode
)];
482 char stuff
[3 & -(sizeof(lxmod
) + sizeof(mode
))];
484 struct EA_LXDEV
{ /* char or block devices only */
486 char name
[sizeof(lxdev
)];
487 char value
[sizeof(device
)];
488 char stuff
[3 & -(sizeof(lxdev
) + sizeof(device
))];
494 memset(&attr
, 0, sizeof(attr
));
495 mode
= cpu_to_le32((u32
)(type
| 0644));
496 attr
.mod
.base
.next_entry_offset
497 = const_cpu_to_le32(sizeof(attr
.mod
));
498 attr
.mod
.base
.flags
= 0;
499 attr
.mod
.base
.name_length
= sizeof(lxmod
) - 1;
500 attr
.mod
.base
.value_length
= const_cpu_to_le16(sizeof(mode
));
501 memcpy(attr
.mod
.name
, lxmod
, sizeof(lxmod
));
502 memcpy(attr
.mod
.value
, &mode
, sizeof(mode
));
503 len
= sizeof(attr
.mod
);
505 if (S_ISCHR(type
) || S_ISBLK(type
)) {
506 device
.major
= cpu_to_le32(major(dev
));
507 device
.minor
= cpu_to_le32(minor(dev
));
508 attr
.dev
.base
.next_entry_offset
509 = const_cpu_to_le32(sizeof(attr
.dev
));
510 attr
.dev
.base
.flags
= 0;
511 attr
.dev
.base
.name_length
= sizeof(lxdev
) - 1;
512 attr
.dev
.base
.value_length
= const_cpu_to_le16(sizeof(device
));
513 memcpy(attr
.dev
.name
, lxdev
, sizeof(lxdev
));
514 memcpy(attr
.dev
.value
, &device
, sizeof(device
));
515 len
+= sizeof(attr
.dev
);
517 res
= ntfs_set_ntfs_ea(ni
, (char*)&attr
, len
, 0);