1 /**
2 * ea.c - Processing of EA's
3 *
4 * This module is part of ntfs-3g library
5 *
6 * Copyright (c) 2014-2021 Jean-Pierre Andre
7 *
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.
12 *
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.
17 *
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
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #ifdef HAVE_STDIO_H
29 #include <stdio.h>
30 #endif
31 #ifdef HAVE_STDLIB_H
32 #include <stdlib.h>
33 #endif
34 #ifdef HAVE_STRING_H
35 #include <string.h>
36 #endif
37 #ifdef HAVE_FCNTL_H
38 #include <fcntl.h>
39 #endif
40 #ifdef HAVE_UNISTD_H
41 #include <unistd.h>
42 #endif
43 #ifdef HAVE_ERRNO_H
44 #include <errno.h>
45 #endif
46 #ifdef MAJOR_IN_MKDEV
47 #include <sys/mkdev.h>
48 #endif
49 #ifdef MAJOR_IN_SYSMACROS
50 #include <sys/sysmacros.h>
51 #endif
52
53 #include "types.h"
54 #include "param.h"
55 #include "layout.h"
56 #include "attrib.h"
57 #include "index.h"
58 #include "dir.h"
59 #include "ea.h"
60 #include "misc.h"
61 #include "logging.h"
62 #include "xattrs.h"
63
64 static const char lxdev[] = "$LXDEV";
65 static const char lxmod[] = "$LXMOD";
66
67
68 /*
69 * Create a needed attribute (EA or EA_INFORMATION)
70 *
71 * Returns 0 if successful,
72 * -1 otherwise, with errno indicating why it failed.
73 */
74
ntfs_need_ea(ntfs_inode * ni,ATTR_TYPES type,int size,int flags)75 static int ntfs_need_ea(ntfs_inode *ni, ATTR_TYPES type, int size, int flags)
76 {
77 u8 dummy;
78 int res;
79
80 res = 0;
81 if (!ntfs_attr_exist(ni,type, AT_UNNAMED,0)) {
82 if (!(flags & XATTR_REPLACE)) {
83 /*
84 * no needed attribute : add one,
85 * apparently, this does not feed the new value in
86 * Note : NTFS version must be >= 3
87 */
88 if (ni->vol->major_ver >= 3) {
89 res = ntfs_attr_add(ni, type,
90 AT_UNNAMED,0,&dummy,(s64)size);
91 if (!res) {
92 NInoFileNameSetDirty(ni);
93 }
94 NInoSetDirty(ni);
95 } else {
96 errno = EOPNOTSUPP;
97 res = -1;
98 }
99 } else {
100 errno = ENODATA;
101 res = -1;
102 }
103 }
104 return (res);
105 }
106
107 /*
108 * Restore the old EA_INFORMATION or delete the current one,
109 * when EA cannot be updated.
110 *
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
113 * left unchanged.
114 * Only double errors are logged here.
115 */
116
restore_ea_info(ntfs_attr * nai,const EA_INFORMATION * old_ea_info)117 static void restore_ea_info(ntfs_attr *nai, const EA_INFORMATION *old_ea_info)
118 {
119 s64 written;
120 int olderrno;
121
122 olderrno = errno;
123 if (old_ea_info) {
124 written = ntfs_attr_pwrite(nai, 0, sizeof(EA_INFORMATION),
125 old_ea_info);
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);
130 }
131 } else {
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);
136 }
137 }
138 errno = olderrno;
139 }
140
141 /*
142 * Update both EA and EA_INFORMATION
143 */
144
ntfs_update_ea(ntfs_inode * ni,const char * value,size_t size,const EA_INFORMATION * ea_info,const EA_INFORMATION * old_ea_info)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)
148 {
149 ntfs_attr *na;
150 ntfs_attr *nai;
151 int res;
152
153 res = 0;
154 nai = ntfs_attr_open(ni, AT_EA_INFORMATION, AT_UNNAMED, 0);
155 if (nai) {
156 na = ntfs_attr_open(ni, AT_EA, AT_UNNAMED, 0);
157 if (na) {
158 /*
159 * Set EA_INFORMATION first, it is easier to
160 * restore the old value, if setting EA fails.
161 */
162 if (ntfs_attr_pwrite(nai, 0, sizeof(EA_INFORMATION),
163 ea_info)
164 != (s64)sizeof(EA_INFORMATION)) {
165 res = -errno;
166 } else {
167 if (((na->data_size > (s64)size)
168 && ntfs_attr_truncate(na, size))
169 || (ntfs_attr_pwrite(na, 0, size, value)
170 != (s64)size)) {
171 res = -errno;
172 if (old_ea_info)
173 restore_ea_info(nai,
174 old_ea_info);
175 }
176 }
177 ntfs_attr_close(na);
178 }
179 ntfs_attr_close(nai);
180 } else {
181 res = -errno;
182 }
183 return (res);
184 }
185
186 /*
187 * Return the existing EA
188 *
189 * The EA_INFORMATION is not examined and the consistency of the
190 * existing EA is not checked.
191 *
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.
198 */
199
ntfs_get_ntfs_ea(ntfs_inode * ni,char * value,size_t size)200 int ntfs_get_ntfs_ea(ntfs_inode *ni, char *value, size_t size)
201 {
202 s64 ea_size;
203 void *ea_buf;
204 int res = 0;
205
206 if (ntfs_attr_exist(ni, AT_EA, AT_UNNAMED, 0)) {
207 ea_buf = ntfs_attr_readall(ni, AT_EA, (ntfschar*)NULL, 0,
208 &ea_size);
209 if (ea_buf) {
210 if (value && (ea_size <= (s64)size))
211 memcpy(value, ea_buf, ea_size);
212 free(ea_buf);
213 res = ea_size;
214 } else {
215 ntfs_log_error("Failed to read EA from inode %lld\n",
216 (long long)ni->mft_no);
217 errno = ENODATA;
218 res = -errno;
219 }
220 } else {
221 errno = ENODATA;
222 res = -errno;
223 }
224 return (res);
225 }
226
227 /*
228 * Set a new EA, and set EA_INFORMATION accordingly
229 *
230 * This is roughly the same as ZwSetEaFile() on Windows, however
231 * the "offset to next" of the last EA should not be cleared.
232 *
233 * Consistency of the new EA is first checked.
234 *
235 * EA_INFORMATION is set first, and it is restored to its former
236 * state if setting EA fails.
237 *
238 * Returns 0 if successful
239 * a negative value if an error occurred.
240 */
241
ntfs_set_ntfs_ea(ntfs_inode * ni,const char * value,size_t size,int flags)242 int ntfs_set_ntfs_ea(ntfs_inode *ni, const char *value, size_t size, int flags)
243 {
244 EA_INFORMATION ea_info;
245 EA_INFORMATION *old_ea_info;
246 s64 old_ea_size;
247 int res;
248 size_t offs;
249 size_t nextoffs;
250 BOOL ok;
251 int ea_count;
252 int ea_packed;
253 const EA_ATTR *p_ea;
254
255 res = -1;
256 if (value && (size > 0)) {
257 /* do consistency checks */
258 offs = 0;
259 ok = TRUE;
260 ea_count = 0;
261 ea_packed = 0;
262 nextoffs = 0;
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)
269 && !(nextoffs & 3)
270 && p_ea->name_length
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))
275 <= nextoffs)
276 && ((offs + offsetof(EA_ATTR,name)
277 + p_ea->name_length + 1
278 + le16_to_cpu(p_ea->value_length))
279 >= (nextoffs - 3))
280 && !p_ea->name[p_ea->name_length];
281 /* name not checked, as chkdsk accepts any chars */
282 if (ok) {
283 if (p_ea->flags & NEED_EA)
284 ea_count++;
285 /*
286 * Assume ea_packed includes :
287 * 4 bytes for header (flags and lengths)
288 * + name length + 1
289 * + value length
290 */
291 ea_packed += 5 + p_ea->name_length
292 + le16_to_cpu(p_ea->value_length);
293 offs = nextoffs;
294 }
295 }
296 /*
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.
300 */
301 if (ok) {
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);
305
306 old_ea_size = 0;
307 old_ea_info = NULL;
308 /* Try to save the old EA_INFORMATION */
309 if (ntfs_attr_exist(ni, AT_EA_INFORMATION,
310 AT_UNNAMED, 0)) {
311 old_ea_info = ntfs_attr_readall(ni,
312 AT_EA_INFORMATION,
313 (ntfschar*)NULL, 0, &old_ea_size);
314 }
315 /*
316 * no EA or EA_INFORMATION : add them
317 */
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);
323 } else {
324 res = -errno;
325 }
326 if (old_ea_info)
327 free(old_ea_info);
328 } else {
329 errno = EINVAL;
330 res = -errno;
331 }
332 } else {
333 errno = EINVAL;
334 res = -errno;
335 }
336 return (res);
337 }
338
339 /*
340 * Remove the EA (including EA_INFORMATION)
341 *
342 * EA_INFORMATION is removed first, and it is restored to its former
343 * state if removing EA fails.
344 *
345 * Returns 0, or -1 if there is a problem
346 */
347
ntfs_remove_ntfs_ea(ntfs_inode * ni)348 int ntfs_remove_ntfs_ea(ntfs_inode *ni)
349 {
350 EA_INFORMATION *old_ea_info;
351 s64 old_ea_size;
352 int res;
353 ntfs_attr *na;
354 ntfs_attr *nai;
355
356 res = 0;
357 if (ni) {
358 /*
359 * open and delete the EA_INFORMATION and the EA
360 */
361 nai = ntfs_attr_open(ni, AT_EA_INFORMATION, AT_UNNAMED, 0);
362 if (nai) {
363 na = ntfs_attr_open(ni, AT_EA, AT_UNNAMED, 0);
364 if (na) {
365 /* Try to save the old EA_INFORMATION */
366 old_ea_info = ntfs_attr_readall(ni,
367 AT_EA_INFORMATION,
368 (ntfschar*)NULL, 0, &old_ea_size);
369 res = ntfs_attr_rm(na);
370 NInoFileNameSetDirty(ni);
371 if (!res) {
372 res = ntfs_attr_rm(nai);
373 if (res && old_ea_info) {
374 /*
375 * Failed to remove the EA, try to
376 * restore the EA_INFORMATION
377 */
378 restore_ea_info(nai,
379 old_ea_info);
380 }
381 } else {
382 ntfs_log_error("Failed to remove the"
383 " EA_INFORMATION from inode %lld\n",
384 (long long)ni->mft_no);
385 }
386 free(old_ea_info);
387 ntfs_attr_close(na);
388 } else {
389 /* EA_INFORMATION present, but no EA */
390 res = ntfs_attr_rm(nai);
391 NInoFileNameSetDirty(ni);
392 }
393 ntfs_attr_close(nai);
394 } else {
395 errno = ENODATA;
396 res = -1;
397 }
398 NInoSetDirty(ni);
399 } else {
400 errno = EINVAL;
401 res = -1;
402 }
403 return (res ? -1 : 0);
404 }
405
406 /*
407 * Check for the presence of an EA "$LXDEV" (used by WSL)
408 * and return its value as a device address
409 *
410 * Returns zero if successful
411 * -1 if failed, with errno set
412 */
413
ntfs_ea_check_wsldev(ntfs_inode * ni,dev_t * rdevp)414 int ntfs_ea_check_wsldev(ntfs_inode *ni, dev_t *rdevp)
415 {
416 const EA_ATTR *p_ea;
417 int bufsize;
418 char *buf;
419 int lth;
420 int res;
421 int offset;
422 int next;
423 BOOL found;
424 struct {
425 le32 major;
426 le32 minor;
427 } device;
428
429 res = -EOPNOTSUPP;
430 bufsize = 256; /* expected to be enough */
431 buf = (char*)malloc(bufsize);
432 if (buf) {
433 lth = ntfs_get_ntfs_ea(ni, buf, bufsize);
434 /* retry if short buf */
435 if (lth > bufsize) {
436 free(buf);
437 bufsize = lth;
438 buf = (char*)malloc(bufsize);
439 if (buf)
440 lth = ntfs_get_ntfs_ea(ni, buf, bufsize);
441 }
442 }
443 if (buf && (lth > 0) && (lth <= bufsize)) {
444 offset = 0;
445 found = FALSE;
446 do {
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)));
454 if (!found)
455 offset += next;
456 } while (!found && (next > 0) && (offset < lth));
457 if (found) {
458 /* beware of alignment */
459 memcpy(&device, &p_ea->name[p_ea->name_length + 1],
460 sizeof(device));
461 *rdevp = makedev(le32_to_cpu(device.major),
462 le32_to_cpu(device.minor));
463 res = 0;
464 }
465 }
466 free(buf);
467 return (res);
468 }
469
ntfs_ea_set_wsl_not_symlink(ntfs_inode * ni,mode_t type,dev_t dev)470 int ntfs_ea_set_wsl_not_symlink(ntfs_inode *ni, mode_t type, dev_t dev)
471 {
472 le32 mode;
473 struct {
474 le32 major;
475 le32 minor;
476 } device;
477 struct EA_WSL {
478 struct EA_LXMOD { /* always inserted */
479 EA_ATTR base;
480 char name[sizeof(lxmod)];
481 char value[sizeof(mode)];
482 char stuff[3 & -(sizeof(lxmod) + sizeof(mode))];
483 } mod;
484 struct EA_LXDEV { /* char or block devices only */
485 EA_ATTR base;
486 char name[sizeof(lxdev)];
487 char value[sizeof(device)];
488 char stuff[3 & -(sizeof(lxdev) + sizeof(device))];
489 } dev;
490 } attr;
491 int len;
492 int res;
493
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);
504
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);
516 }
517 res = ntfs_set_ntfs_ea(ni, (char*)&attr, len, 0);
518 return (res);
519 }
520