• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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