1987da915Sopenharmony_ci/**
2987da915Sopenharmony_ci * ea.c - Processing of EA's
3987da915Sopenharmony_ci *
4987da915Sopenharmony_ci *      This module is part of ntfs-3g library
5987da915Sopenharmony_ci *
6987da915Sopenharmony_ci * Copyright (c) 2014-2021 Jean-Pierre Andre
7987da915Sopenharmony_ci *
8987da915Sopenharmony_ci * This program/include file is free software; you can redistribute it and/or
9987da915Sopenharmony_ci * modify it under the terms of the GNU General Public License as published
10987da915Sopenharmony_ci * by the Free Software Foundation; either version 2 of the License, or
11987da915Sopenharmony_ci * (at your option) any later version.
12987da915Sopenharmony_ci *
13987da915Sopenharmony_ci * This program/include file is distributed in the hope that it will be
14987da915Sopenharmony_ci * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15987da915Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16987da915Sopenharmony_ci * GNU General Public License for more details.
17987da915Sopenharmony_ci *
18987da915Sopenharmony_ci * You should have received a copy of the GNU General Public License
19987da915Sopenharmony_ci * along with this program (in the main directory of the NTFS-3G
20987da915Sopenharmony_ci * distribution in the file COPYING); if not, write to the Free Software
21987da915Sopenharmony_ci * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22987da915Sopenharmony_ci */
23987da915Sopenharmony_ci
24987da915Sopenharmony_ci#ifdef HAVE_CONFIG_H
25987da915Sopenharmony_ci#include "config.h"
26987da915Sopenharmony_ci#endif
27987da915Sopenharmony_ci
28987da915Sopenharmony_ci#ifdef HAVE_STDIO_H
29987da915Sopenharmony_ci#include <stdio.h>
30987da915Sopenharmony_ci#endif
31987da915Sopenharmony_ci#ifdef HAVE_STDLIB_H
32987da915Sopenharmony_ci#include <stdlib.h>
33987da915Sopenharmony_ci#endif
34987da915Sopenharmony_ci#ifdef HAVE_STRING_H
35987da915Sopenharmony_ci#include <string.h>
36987da915Sopenharmony_ci#endif
37987da915Sopenharmony_ci#ifdef HAVE_FCNTL_H
38987da915Sopenharmony_ci#include <fcntl.h>
39987da915Sopenharmony_ci#endif
40987da915Sopenharmony_ci#ifdef HAVE_UNISTD_H
41987da915Sopenharmony_ci#include <unistd.h>
42987da915Sopenharmony_ci#endif
43987da915Sopenharmony_ci#ifdef HAVE_ERRNO_H
44987da915Sopenharmony_ci#include <errno.h>
45987da915Sopenharmony_ci#endif
46987da915Sopenharmony_ci#ifdef MAJOR_IN_MKDEV
47987da915Sopenharmony_ci#include <sys/mkdev.h>
48987da915Sopenharmony_ci#endif
49987da915Sopenharmony_ci#ifdef MAJOR_IN_SYSMACROS
50987da915Sopenharmony_ci#include <sys/sysmacros.h>
51987da915Sopenharmony_ci#endif
52987da915Sopenharmony_ci
53987da915Sopenharmony_ci#include "types.h"
54987da915Sopenharmony_ci#include "param.h"
55987da915Sopenharmony_ci#include "layout.h"
56987da915Sopenharmony_ci#include "attrib.h"
57987da915Sopenharmony_ci#include "index.h"
58987da915Sopenharmony_ci#include "dir.h"
59987da915Sopenharmony_ci#include "ea.h"
60987da915Sopenharmony_ci#include "misc.h"
61987da915Sopenharmony_ci#include "logging.h"
62987da915Sopenharmony_ci#include "xattrs.h"
63987da915Sopenharmony_ci
64987da915Sopenharmony_cistatic const char lxdev[] = "$LXDEV";
65987da915Sopenharmony_cistatic const char lxmod[] = "$LXMOD";
66987da915Sopenharmony_ci
67987da915Sopenharmony_ci
68987da915Sopenharmony_ci/*
69987da915Sopenharmony_ci *		Create a needed attribute (EA or EA_INFORMATION)
70987da915Sopenharmony_ci *
71987da915Sopenharmony_ci *	Returns 0 if successful,
72987da915Sopenharmony_ci *		-1 otherwise, with errno indicating why it failed.
73987da915Sopenharmony_ci */
74987da915Sopenharmony_ci
75987da915Sopenharmony_cistatic int ntfs_need_ea(ntfs_inode *ni, ATTR_TYPES type, int size, int flags)
76987da915Sopenharmony_ci{
77987da915Sopenharmony_ci	u8 dummy;
78987da915Sopenharmony_ci	int res;
79987da915Sopenharmony_ci
80987da915Sopenharmony_ci	res = 0;
81987da915Sopenharmony_ci	if (!ntfs_attr_exist(ni,type, AT_UNNAMED,0)) {
82987da915Sopenharmony_ci		if (!(flags & XATTR_REPLACE)) {
83987da915Sopenharmony_ci			/*
84987da915Sopenharmony_ci			 * no needed attribute : add one,
85987da915Sopenharmony_ci			 * apparently, this does not feed the new value in
86987da915Sopenharmony_ci			 * Note : NTFS version must be >= 3
87987da915Sopenharmony_ci			 */
88987da915Sopenharmony_ci			if (ni->vol->major_ver >= 3) {
89987da915Sopenharmony_ci				res = ntfs_attr_add(ni,	type,
90987da915Sopenharmony_ci					AT_UNNAMED,0,&dummy,(s64)size);
91987da915Sopenharmony_ci				if (!res) {
92987da915Sopenharmony_ci					    NInoFileNameSetDirty(ni);
93987da915Sopenharmony_ci				}
94987da915Sopenharmony_ci				NInoSetDirty(ni);
95987da915Sopenharmony_ci			} else {
96987da915Sopenharmony_ci				errno = EOPNOTSUPP;
97987da915Sopenharmony_ci				res = -1;
98987da915Sopenharmony_ci			}
99987da915Sopenharmony_ci		} else {
100987da915Sopenharmony_ci			errno = ENODATA;
101987da915Sopenharmony_ci			res = -1;
102987da915Sopenharmony_ci		}
103987da915Sopenharmony_ci	}
104987da915Sopenharmony_ci	return (res);
105987da915Sopenharmony_ci}
106987da915Sopenharmony_ci
107987da915Sopenharmony_ci/*
108987da915Sopenharmony_ci *		Restore the old EA_INFORMATION or delete the current one,
109987da915Sopenharmony_ci *	 when EA cannot be updated.
110987da915Sopenharmony_ci *
111987da915Sopenharmony_ci *	As this is used in the context of some other error, the caller
112987da915Sopenharmony_ci *	is responsible for returning the proper error, and errno is
113987da915Sopenharmony_ci *	left unchanged.
114987da915Sopenharmony_ci *	Only double errors are logged here.
115987da915Sopenharmony_ci */
116987da915Sopenharmony_ci
117987da915Sopenharmony_cistatic void restore_ea_info(ntfs_attr *nai, const EA_INFORMATION *old_ea_info)
118987da915Sopenharmony_ci{
119987da915Sopenharmony_ci	s64 written;
120987da915Sopenharmony_ci	int olderrno;
121987da915Sopenharmony_ci
122987da915Sopenharmony_ci	olderrno = errno;
123987da915Sopenharmony_ci	if (old_ea_info) {
124987da915Sopenharmony_ci		written = ntfs_attr_pwrite(nai,	0, sizeof(EA_INFORMATION),
125987da915Sopenharmony_ci				old_ea_info);
126987da915Sopenharmony_ci		if ((size_t)written != sizeof(EA_INFORMATION)) {
127987da915Sopenharmony_ci			ntfs_log_error("Could not restore the EA_INFORMATION,"
128987da915Sopenharmony_ci				" possible inconsistency in inode %lld\n",
129987da915Sopenharmony_ci				(long long)nai->ni->mft_no);
130987da915Sopenharmony_ci		}
131987da915Sopenharmony_ci	} else {
132987da915Sopenharmony_ci		if (ntfs_attr_rm(nai)) {
133987da915Sopenharmony_ci			ntfs_log_error("Could not delete the EA_INFORMATION,"
134987da915Sopenharmony_ci				" possible inconsistency in inode %lld\n",
135987da915Sopenharmony_ci				(long long)nai->ni->mft_no);
136987da915Sopenharmony_ci		}
137987da915Sopenharmony_ci	}
138987da915Sopenharmony_ci	errno = olderrno;
139987da915Sopenharmony_ci}
140987da915Sopenharmony_ci
141987da915Sopenharmony_ci/*
142987da915Sopenharmony_ci *		Update both EA and EA_INFORMATION
143987da915Sopenharmony_ci */
144987da915Sopenharmony_ci
145987da915Sopenharmony_cistatic int ntfs_update_ea(ntfs_inode *ni, const char *value, size_t size,
146987da915Sopenharmony_ci			const EA_INFORMATION *ea_info,
147987da915Sopenharmony_ci			const EA_INFORMATION *old_ea_info)
148987da915Sopenharmony_ci{
149987da915Sopenharmony_ci	ntfs_attr *na;
150987da915Sopenharmony_ci	ntfs_attr *nai;
151987da915Sopenharmony_ci	int res;
152987da915Sopenharmony_ci
153987da915Sopenharmony_ci	res = 0;
154987da915Sopenharmony_ci	nai = ntfs_attr_open(ni, AT_EA_INFORMATION, AT_UNNAMED, 0);
155987da915Sopenharmony_ci	if (nai) {
156987da915Sopenharmony_ci		na = ntfs_attr_open(ni, AT_EA, AT_UNNAMED, 0);
157987da915Sopenharmony_ci		if (na) {
158987da915Sopenharmony_ci				/*
159987da915Sopenharmony_ci				 * Set EA_INFORMATION first, it is easier to
160987da915Sopenharmony_ci				 * restore the old value, if setting EA fails.
161987da915Sopenharmony_ci				 */
162987da915Sopenharmony_ci			if (ntfs_attr_pwrite(nai, 0, sizeof(EA_INFORMATION),
163987da915Sopenharmony_ci						ea_info)
164987da915Sopenharmony_ci					!= (s64)sizeof(EA_INFORMATION)) {
165987da915Sopenharmony_ci				res = -errno;
166987da915Sopenharmony_ci			} else {
167987da915Sopenharmony_ci				if (((na->data_size > (s64)size)
168987da915Sopenharmony_ci					&& ntfs_attr_truncate(na, size))
169987da915Sopenharmony_ci				    || (ntfs_attr_pwrite(na, 0, size, value)
170987da915Sopenharmony_ci							!= (s64)size)) {
171987da915Sopenharmony_ci					res = -errno;
172987da915Sopenharmony_ci                                        if (old_ea_info)
173987da915Sopenharmony_ci						restore_ea_info(nai,
174987da915Sopenharmony_ci							old_ea_info);
175987da915Sopenharmony_ci				}
176987da915Sopenharmony_ci			}
177987da915Sopenharmony_ci			ntfs_attr_close(na);
178987da915Sopenharmony_ci		}
179987da915Sopenharmony_ci		ntfs_attr_close(nai);
180987da915Sopenharmony_ci	} else {
181987da915Sopenharmony_ci		res = -errno;
182987da915Sopenharmony_ci	}
183987da915Sopenharmony_ci	return (res);
184987da915Sopenharmony_ci}
185987da915Sopenharmony_ci
186987da915Sopenharmony_ci/*
187987da915Sopenharmony_ci *		Return the existing EA
188987da915Sopenharmony_ci *
189987da915Sopenharmony_ci *	The EA_INFORMATION is not examined and the consistency of the
190987da915Sopenharmony_ci *	existing EA is not checked.
191987da915Sopenharmony_ci *
192987da915Sopenharmony_ci *	If successful, the full attribute is returned unchanged
193987da915Sopenharmony_ci *		and its size is returned.
194987da915Sopenharmony_ci *	If the designated buffer is too small, the needed size is
195987da915Sopenharmony_ci *		returned, and the buffer is left unchanged.
196987da915Sopenharmony_ci *	If there is an error, a negative value is returned and errno
197987da915Sopenharmony_ci *		is set according to the error.
198987da915Sopenharmony_ci */
199987da915Sopenharmony_ci
200987da915Sopenharmony_ciint ntfs_get_ntfs_ea(ntfs_inode *ni, char *value, size_t size)
201987da915Sopenharmony_ci{
202987da915Sopenharmony_ci	s64 ea_size;
203987da915Sopenharmony_ci	void *ea_buf;
204987da915Sopenharmony_ci	int res = 0;
205987da915Sopenharmony_ci
206987da915Sopenharmony_ci	if (ntfs_attr_exist(ni, AT_EA, AT_UNNAMED, 0)) {
207987da915Sopenharmony_ci		ea_buf = ntfs_attr_readall(ni, AT_EA, (ntfschar*)NULL, 0,
208987da915Sopenharmony_ci					&ea_size);
209987da915Sopenharmony_ci		if (ea_buf) {
210987da915Sopenharmony_ci			if (value && (ea_size <= (s64)size))
211987da915Sopenharmony_ci				memcpy(value, ea_buf, ea_size);
212987da915Sopenharmony_ci			free(ea_buf);
213987da915Sopenharmony_ci			res = ea_size;
214987da915Sopenharmony_ci		} else {
215987da915Sopenharmony_ci			ntfs_log_error("Failed to read EA from inode %lld\n",
216987da915Sopenharmony_ci					(long long)ni->mft_no);
217987da915Sopenharmony_ci			errno = ENODATA;
218987da915Sopenharmony_ci			res = -errno;
219987da915Sopenharmony_ci		}
220987da915Sopenharmony_ci	} else {
221987da915Sopenharmony_ci		errno = ENODATA;
222987da915Sopenharmony_ci		res = -errno;
223987da915Sopenharmony_ci	}
224987da915Sopenharmony_ci	return (res);
225987da915Sopenharmony_ci}
226987da915Sopenharmony_ci
227987da915Sopenharmony_ci/*
228987da915Sopenharmony_ci *		Set a new EA, and set EA_INFORMATION accordingly
229987da915Sopenharmony_ci *
230987da915Sopenharmony_ci *	This is roughly the same as ZwSetEaFile() on Windows, however
231987da915Sopenharmony_ci *	the "offset to next" of the last EA should not be cleared.
232987da915Sopenharmony_ci *
233987da915Sopenharmony_ci *	Consistency of the new EA is first checked.
234987da915Sopenharmony_ci *
235987da915Sopenharmony_ci *	EA_INFORMATION is set first, and it is restored to its former
236987da915Sopenharmony_ci *	state if setting EA fails.
237987da915Sopenharmony_ci *
238987da915Sopenharmony_ci *	Returns 0 if successful
239987da915Sopenharmony_ci *		a negative value if an error occurred.
240987da915Sopenharmony_ci */
241987da915Sopenharmony_ci
242987da915Sopenharmony_ciint ntfs_set_ntfs_ea(ntfs_inode *ni, const char *value, size_t size, int flags)
243987da915Sopenharmony_ci{
244987da915Sopenharmony_ci	EA_INFORMATION ea_info;
245987da915Sopenharmony_ci	EA_INFORMATION *old_ea_info;
246987da915Sopenharmony_ci	s64 old_ea_size;
247987da915Sopenharmony_ci	int res;
248987da915Sopenharmony_ci	size_t offs;
249987da915Sopenharmony_ci	size_t nextoffs;
250987da915Sopenharmony_ci	BOOL ok;
251987da915Sopenharmony_ci	int ea_count;
252987da915Sopenharmony_ci	int ea_packed;
253987da915Sopenharmony_ci	const EA_ATTR *p_ea;
254987da915Sopenharmony_ci
255987da915Sopenharmony_ci	res = -1;
256987da915Sopenharmony_ci	if (value && (size > 0)) {
257987da915Sopenharmony_ci					/* do consistency checks */
258987da915Sopenharmony_ci		offs = 0;
259987da915Sopenharmony_ci		ok = TRUE;
260987da915Sopenharmony_ci		ea_count = 0;
261987da915Sopenharmony_ci		ea_packed = 0;
262987da915Sopenharmony_ci		nextoffs = 0;
263987da915Sopenharmony_ci		while (ok && (offs < size)) {
264987da915Sopenharmony_ci			p_ea = (const EA_ATTR*)&value[offs];
265987da915Sopenharmony_ci			nextoffs = offs + le32_to_cpu(p_ea->next_entry_offset);
266987da915Sopenharmony_ci				/* null offset to next not allowed */
267987da915Sopenharmony_ci			ok = (nextoffs > offs)
268987da915Sopenharmony_ci			    && (nextoffs <= size)
269987da915Sopenharmony_ci			    && !(nextoffs & 3)
270987da915Sopenharmony_ci			    && p_ea->name_length
271987da915Sopenharmony_ci				/* zero sized value are allowed */
272987da915Sopenharmony_ci			    && ((offs + offsetof(EA_ATTR,name)
273987da915Sopenharmony_ci				+ p_ea->name_length + 1
274987da915Sopenharmony_ci				+ le16_to_cpu(p_ea->value_length))
275987da915Sopenharmony_ci				    <= nextoffs)
276987da915Sopenharmony_ci			    && ((offs + offsetof(EA_ATTR,name)
277987da915Sopenharmony_ci				+ p_ea->name_length + 1
278987da915Sopenharmony_ci				+ le16_to_cpu(p_ea->value_length))
279987da915Sopenharmony_ci				    >= (nextoffs - 3))
280987da915Sopenharmony_ci			    && !p_ea->name[p_ea->name_length];
281987da915Sopenharmony_ci			/* name not checked, as chkdsk accepts any chars */
282987da915Sopenharmony_ci			if (ok) {
283987da915Sopenharmony_ci				if (p_ea->flags & NEED_EA)
284987da915Sopenharmony_ci					ea_count++;
285987da915Sopenharmony_ci				/*
286987da915Sopenharmony_ci				 * Assume ea_packed includes :
287987da915Sopenharmony_ci				 * 4 bytes for header (flags and lengths)
288987da915Sopenharmony_ci				 * + name length + 1
289987da915Sopenharmony_ci				 * + value length
290987da915Sopenharmony_ci				 */
291987da915Sopenharmony_ci				ea_packed += 5 + p_ea->name_length
292987da915Sopenharmony_ci					+ le16_to_cpu(p_ea->value_length);
293987da915Sopenharmony_ci				offs = nextoffs;
294987da915Sopenharmony_ci			}
295987da915Sopenharmony_ci		}
296987da915Sopenharmony_ci		/*
297987da915Sopenharmony_ci		 * EA and REPARSE_POINT compatibility not checked any more,
298987da915Sopenharmony_ci		 * required by Windows 10, but having both may lead to
299987da915Sopenharmony_ci		 * problems with earlier versions.
300987da915Sopenharmony_ci		 */
301987da915Sopenharmony_ci		if (ok) {
302987da915Sopenharmony_ci			ea_info.ea_length = cpu_to_le16(ea_packed);
303987da915Sopenharmony_ci			ea_info.need_ea_count = cpu_to_le16(ea_count);
304987da915Sopenharmony_ci			ea_info.ea_query_length = cpu_to_le32(nextoffs);
305987da915Sopenharmony_ci
306987da915Sopenharmony_ci			old_ea_size = 0;
307987da915Sopenharmony_ci			old_ea_info = NULL;
308987da915Sopenharmony_ci				/* Try to save the old EA_INFORMATION */
309987da915Sopenharmony_ci			if (ntfs_attr_exist(ni, AT_EA_INFORMATION,
310987da915Sopenharmony_ci							AT_UNNAMED, 0)) {
311987da915Sopenharmony_ci				old_ea_info = ntfs_attr_readall(ni,
312987da915Sopenharmony_ci					AT_EA_INFORMATION,
313987da915Sopenharmony_ci					(ntfschar*)NULL, 0, &old_ea_size);
314987da915Sopenharmony_ci			}
315987da915Sopenharmony_ci			/*
316987da915Sopenharmony_ci			 * no EA or EA_INFORMATION : add them
317987da915Sopenharmony_ci			 */
318987da915Sopenharmony_ci			if (!ntfs_need_ea(ni, AT_EA_INFORMATION,
319987da915Sopenharmony_ci					sizeof(EA_INFORMATION), flags)
320987da915Sopenharmony_ci			    && !ntfs_need_ea(ni, AT_EA, 0, flags)) {
321987da915Sopenharmony_ci				res = ntfs_update_ea(ni, value, size,
322987da915Sopenharmony_ci						&ea_info, old_ea_info);
323987da915Sopenharmony_ci			} else {
324987da915Sopenharmony_ci				res = -errno;
325987da915Sopenharmony_ci			}
326987da915Sopenharmony_ci			if (old_ea_info)
327987da915Sopenharmony_ci				free(old_ea_info);
328987da915Sopenharmony_ci		} else {
329987da915Sopenharmony_ci			errno = EINVAL;
330987da915Sopenharmony_ci			res = -errno;
331987da915Sopenharmony_ci		}
332987da915Sopenharmony_ci	} else {
333987da915Sopenharmony_ci		errno = EINVAL;
334987da915Sopenharmony_ci		res = -errno;
335987da915Sopenharmony_ci	}
336987da915Sopenharmony_ci	return (res);
337987da915Sopenharmony_ci}
338987da915Sopenharmony_ci
339987da915Sopenharmony_ci/*
340987da915Sopenharmony_ci *		Remove the EA (including EA_INFORMATION)
341987da915Sopenharmony_ci *
342987da915Sopenharmony_ci *	EA_INFORMATION is removed first, and it is restored to its former
343987da915Sopenharmony_ci *	state if removing EA fails.
344987da915Sopenharmony_ci *
345987da915Sopenharmony_ci *	Returns 0, or -1 if there is a problem
346987da915Sopenharmony_ci */
347987da915Sopenharmony_ci
348987da915Sopenharmony_ciint ntfs_remove_ntfs_ea(ntfs_inode *ni)
349987da915Sopenharmony_ci{
350987da915Sopenharmony_ci	EA_INFORMATION *old_ea_info;
351987da915Sopenharmony_ci	s64 old_ea_size;
352987da915Sopenharmony_ci	int res;
353987da915Sopenharmony_ci	ntfs_attr *na;
354987da915Sopenharmony_ci	ntfs_attr *nai;
355987da915Sopenharmony_ci
356987da915Sopenharmony_ci	res = 0;
357987da915Sopenharmony_ci	if (ni) {
358987da915Sopenharmony_ci		/*
359987da915Sopenharmony_ci		 * open and delete the EA_INFORMATION and the EA
360987da915Sopenharmony_ci		 */
361987da915Sopenharmony_ci		nai = ntfs_attr_open(ni, AT_EA_INFORMATION, AT_UNNAMED, 0);
362987da915Sopenharmony_ci		if (nai) {
363987da915Sopenharmony_ci			na = ntfs_attr_open(ni, AT_EA, AT_UNNAMED, 0);
364987da915Sopenharmony_ci			if (na) {
365987da915Sopenharmony_ci				/* Try to save the old EA_INFORMATION */
366987da915Sopenharmony_ci				old_ea_info = ntfs_attr_readall(ni,
367987da915Sopenharmony_ci					 AT_EA_INFORMATION,
368987da915Sopenharmony_ci					 (ntfschar*)NULL, 0, &old_ea_size);
369987da915Sopenharmony_ci				res = ntfs_attr_rm(na);
370987da915Sopenharmony_ci				NInoFileNameSetDirty(ni);
371987da915Sopenharmony_ci				if (!res) {
372987da915Sopenharmony_ci					res = ntfs_attr_rm(nai);
373987da915Sopenharmony_ci					if (res && old_ea_info) {
374987da915Sopenharmony_ci					/*
375987da915Sopenharmony_ci					 * Failed to remove the EA, try to
376987da915Sopenharmony_ci					 * restore the EA_INFORMATION
377987da915Sopenharmony_ci					 */
378987da915Sopenharmony_ci						restore_ea_info(nai,
379987da915Sopenharmony_ci							old_ea_info);
380987da915Sopenharmony_ci					}
381987da915Sopenharmony_ci				} else {
382987da915Sopenharmony_ci					ntfs_log_error("Failed to remove the"
383987da915Sopenharmony_ci						" EA_INFORMATION from inode %lld\n",
384987da915Sopenharmony_ci						(long long)ni->mft_no);
385987da915Sopenharmony_ci				}
386987da915Sopenharmony_ci				free(old_ea_info);
387987da915Sopenharmony_ci				ntfs_attr_close(na);
388987da915Sopenharmony_ci			} else {
389987da915Sopenharmony_ci				/* EA_INFORMATION present, but no EA */
390987da915Sopenharmony_ci				res = ntfs_attr_rm(nai);
391987da915Sopenharmony_ci				NInoFileNameSetDirty(ni);
392987da915Sopenharmony_ci			}
393987da915Sopenharmony_ci			ntfs_attr_close(nai);
394987da915Sopenharmony_ci		} else {
395987da915Sopenharmony_ci			errno = ENODATA;
396987da915Sopenharmony_ci			res = -1;
397987da915Sopenharmony_ci		}
398987da915Sopenharmony_ci		NInoSetDirty(ni);
399987da915Sopenharmony_ci	} else {
400987da915Sopenharmony_ci		errno = EINVAL;
401987da915Sopenharmony_ci		res = -1;
402987da915Sopenharmony_ci	}
403987da915Sopenharmony_ci	return (res ? -1 : 0);
404987da915Sopenharmony_ci}
405987da915Sopenharmony_ci
406987da915Sopenharmony_ci/*
407987da915Sopenharmony_ci *		Check for the presence of an EA "$LXDEV" (used by WSL)
408987da915Sopenharmony_ci *	and return its value as a device address
409987da915Sopenharmony_ci *
410987da915Sopenharmony_ci *	Returns zero if successful
411987da915Sopenharmony_ci *		-1 if failed, with errno set
412987da915Sopenharmony_ci */
413987da915Sopenharmony_ci
414987da915Sopenharmony_ciint ntfs_ea_check_wsldev(ntfs_inode *ni, dev_t *rdevp)
415987da915Sopenharmony_ci{
416987da915Sopenharmony_ci	const EA_ATTR *p_ea;
417987da915Sopenharmony_ci	int bufsize;
418987da915Sopenharmony_ci	char *buf;
419987da915Sopenharmony_ci	int lth;
420987da915Sopenharmony_ci	int res;
421987da915Sopenharmony_ci	int offset;
422987da915Sopenharmony_ci	int next;
423987da915Sopenharmony_ci	BOOL found;
424987da915Sopenharmony_ci	struct {
425987da915Sopenharmony_ci		le32 major;
426987da915Sopenharmony_ci		le32 minor;
427987da915Sopenharmony_ci	} device;
428987da915Sopenharmony_ci
429987da915Sopenharmony_ci	res = -EOPNOTSUPP;
430987da915Sopenharmony_ci	bufsize = 256; /* expected to be enough */
431987da915Sopenharmony_ci	buf = (char*)malloc(bufsize);
432987da915Sopenharmony_ci	if (buf) {
433987da915Sopenharmony_ci		lth = ntfs_get_ntfs_ea(ni, buf, bufsize);
434987da915Sopenharmony_ci			/* retry if short buf */
435987da915Sopenharmony_ci		if (lth > bufsize) {
436987da915Sopenharmony_ci			free(buf);
437987da915Sopenharmony_ci			bufsize = lth;
438987da915Sopenharmony_ci			buf = (char*)malloc(bufsize);
439987da915Sopenharmony_ci			if (buf)
440987da915Sopenharmony_ci				lth = ntfs_get_ntfs_ea(ni, buf, bufsize);
441987da915Sopenharmony_ci		}
442987da915Sopenharmony_ci	}
443987da915Sopenharmony_ci	if (buf && (lth > 0) && (lth <= bufsize)) {
444987da915Sopenharmony_ci		offset = 0;
445987da915Sopenharmony_ci		found = FALSE;
446987da915Sopenharmony_ci		do {
447987da915Sopenharmony_ci			p_ea = (const EA_ATTR*)&buf[offset];
448987da915Sopenharmony_ci			next = le32_to_cpu(p_ea->next_entry_offset);
449987da915Sopenharmony_ci			found = ((next > (int)(sizeof(lxdev) + sizeof(device)))
450987da915Sopenharmony_ci				&& (p_ea->name_length == (sizeof(lxdev) - 1))
451987da915Sopenharmony_ci				&& (p_ea->value_length
452987da915Sopenharmony_ci					== const_cpu_to_le16(sizeof(device)))
453987da915Sopenharmony_ci				&& !memcmp(p_ea->name, lxdev, sizeof(lxdev)));
454987da915Sopenharmony_ci			if (!found)
455987da915Sopenharmony_ci				offset += next;
456987da915Sopenharmony_ci		} while (!found && (next > 0) && (offset < lth));
457987da915Sopenharmony_ci		if (found) {
458987da915Sopenharmony_ci				/* beware of alignment */
459987da915Sopenharmony_ci			memcpy(&device, &p_ea->name[p_ea->name_length + 1],
460987da915Sopenharmony_ci					sizeof(device));
461987da915Sopenharmony_ci			*rdevp = makedev(le32_to_cpu(device.major),
462987da915Sopenharmony_ci					le32_to_cpu(device.minor));
463987da915Sopenharmony_ci			res = 0;
464987da915Sopenharmony_ci		}
465987da915Sopenharmony_ci	}
466987da915Sopenharmony_ci	free(buf);
467987da915Sopenharmony_ci	return (res);
468987da915Sopenharmony_ci}
469987da915Sopenharmony_ci
470987da915Sopenharmony_ciint ntfs_ea_set_wsl_not_symlink(ntfs_inode *ni, mode_t type, dev_t dev)
471987da915Sopenharmony_ci{
472987da915Sopenharmony_ci	le32 mode;
473987da915Sopenharmony_ci	struct {
474987da915Sopenharmony_ci		le32 major;
475987da915Sopenharmony_ci		le32 minor;
476987da915Sopenharmony_ci	} device;
477987da915Sopenharmony_ci	struct EA_WSL {
478987da915Sopenharmony_ci		struct EA_LXMOD {	/* always inserted */
479987da915Sopenharmony_ci			EA_ATTR base;
480987da915Sopenharmony_ci			char name[sizeof(lxmod)];
481987da915Sopenharmony_ci			char value[sizeof(mode)];
482987da915Sopenharmony_ci			char stuff[3 & -(sizeof(lxmod) + sizeof(mode))];
483987da915Sopenharmony_ci		} mod;
484987da915Sopenharmony_ci		struct EA_LXDEV {	/* char or block devices only */
485987da915Sopenharmony_ci			EA_ATTR base;
486987da915Sopenharmony_ci			char name[sizeof(lxdev)];
487987da915Sopenharmony_ci			char value[sizeof(device)];
488987da915Sopenharmony_ci			char stuff[3 & -(sizeof(lxdev) + sizeof(device))];
489987da915Sopenharmony_ci		} dev;
490987da915Sopenharmony_ci	} attr;
491987da915Sopenharmony_ci	int len;
492987da915Sopenharmony_ci	int res;
493987da915Sopenharmony_ci
494987da915Sopenharmony_ci	memset(&attr, 0, sizeof(attr));
495987da915Sopenharmony_ci	mode = cpu_to_le32((u32)(type | 0644));
496987da915Sopenharmony_ci	attr.mod.base.next_entry_offset
497987da915Sopenharmony_ci			= const_cpu_to_le32(sizeof(attr.mod));
498987da915Sopenharmony_ci	attr.mod.base.flags = 0;
499987da915Sopenharmony_ci	attr.mod.base.name_length = sizeof(lxmod) - 1;
500987da915Sopenharmony_ci	attr.mod.base.value_length = const_cpu_to_le16(sizeof(mode));
501987da915Sopenharmony_ci	memcpy(attr.mod.name, lxmod, sizeof(lxmod));
502987da915Sopenharmony_ci	memcpy(attr.mod.value, &mode, sizeof(mode));
503987da915Sopenharmony_ci	len = sizeof(attr.mod);
504987da915Sopenharmony_ci
505987da915Sopenharmony_ci	if (S_ISCHR(type) || S_ISBLK(type)) {
506987da915Sopenharmony_ci		device.major = cpu_to_le32(major(dev));
507987da915Sopenharmony_ci		device.minor = cpu_to_le32(minor(dev));
508987da915Sopenharmony_ci		attr.dev.base.next_entry_offset
509987da915Sopenharmony_ci			= const_cpu_to_le32(sizeof(attr.dev));
510987da915Sopenharmony_ci		attr.dev.base.flags = 0;
511987da915Sopenharmony_ci		attr.dev.base.name_length = sizeof(lxdev) - 1;
512987da915Sopenharmony_ci		attr.dev.base.value_length = const_cpu_to_le16(sizeof(device));
513987da915Sopenharmony_ci		memcpy(attr.dev.name, lxdev, sizeof(lxdev));
514987da915Sopenharmony_ci		memcpy(attr.dev.value, &device, sizeof(device));
515987da915Sopenharmony_ci		len += sizeof(attr.dev);
516987da915Sopenharmony_ci		}
517987da915Sopenharmony_ci	res = ntfs_set_ntfs_ea(ni, (char*)&attr, len, 0);
518987da915Sopenharmony_ci	return (res);
519987da915Sopenharmony_ci}
520