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
64static const char lxdev[] = "$LXDEV";
65static 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
75static 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
117static 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
145static 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
200int 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
242int 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
348int 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
414int 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
470int 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