1/**
2 * reparse.c - Processing of reparse points
3 *
4 *	This module is part of ntfs-3g library
5 *
6 * Copyright (c) 2008-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_STDLIB_H
29#include <stdlib.h>
30#endif
31#ifdef HAVE_ERRNO_H
32#include <errno.h>
33#endif
34#ifdef HAVE_STRING_H
35#include <string.h>
36#endif
37#ifdef HAVE_SYS_STAT_H
38#include <sys/stat.h>
39#endif
40#ifdef MAJOR_IN_MKDEV
41#include <sys/mkdev.h>
42#endif
43#ifdef MAJOR_IN_SYSMACROS
44#include <sys/sysmacros.h>
45#endif
46
47#include "compat.h"
48#include "types.h"
49#include "debug.h"
50#include "layout.h"
51#include "attrib.h"
52#include "inode.h"
53#include "dir.h"
54#include "volume.h"
55#include "mft.h"
56#include "index.h"
57#include "lcnalloc.h"
58#include "logging.h"
59#include "misc.h"
60#include "reparse.h"
61#include "xattrs.h"
62#include "ea.h"
63
64struct MOUNT_POINT_REPARSE_DATA {      /* reparse data for junctions */
65	le16	subst_name_offset;
66	le16	subst_name_length;
67	le16	print_name_offset;
68	le16	print_name_length;
69	char	path_buffer[0];      /* above data assume this is char array */
70} ;
71
72struct SYMLINK_REPARSE_DATA {          /* reparse data for symlinks */
73	le16	subst_name_offset;
74	le16	subst_name_length;
75	le16	print_name_offset;
76	le16	print_name_length;
77	le32	flags;		     /* 1 for full target, otherwise 0 */
78	char	path_buffer[0];      /* above data assume this is char array */
79} ;
80
81struct WSL_LINK_REPARSE_DATA {
82	le32	type;
83	char	link[0];
84} ;
85
86struct REPARSE_INDEX {			/* index entry in $Extend/$Reparse */
87	INDEX_ENTRY_HEADER header;
88	REPARSE_INDEX_KEY key;
89	le32 filling;
90} ;
91
92static const ntfschar dir_junction_head[] = {
93	const_cpu_to_le16('\\'),
94	const_cpu_to_le16('?'),
95	const_cpu_to_le16('?'),
96	const_cpu_to_le16('\\')
97} ;
98
99static const ntfschar vol_junction_head[] = {
100	const_cpu_to_le16('\\'),
101	const_cpu_to_le16('?'),
102	const_cpu_to_le16('?'),
103	const_cpu_to_le16('\\'),
104	const_cpu_to_le16('V'),
105	const_cpu_to_le16('o'),
106	const_cpu_to_le16('l'),
107	const_cpu_to_le16('u'),
108	const_cpu_to_le16('m'),
109	const_cpu_to_le16('e'),
110	const_cpu_to_le16('{'),
111} ;
112
113static ntfschar reparse_index_name[] = { const_cpu_to_le16('$'),
114					 const_cpu_to_le16('R') };
115
116static const char mappingdir[] = ".NTFS-3G/";
117
118/*
119 *		Fix a file name with doubtful case in some directory index
120 *	and return the name with the casing used in directory.
121 *
122 *	Should only be used to translate paths stored with case insensitivity
123 *	(such as directory junctions) when no case conflict is expected.
124 *	If there some ambiguity, the name which collates first is returned.
125 *
126 *	The name is converted to upper case and searched the usual way.
127 *	The collation rules for file names are such that we should get the
128 *	first candidate if any.
129 */
130
131static u64 ntfs_fix_file_name(ntfs_inode *dir_ni, ntfschar *uname,
132		int uname_len)
133{
134	ntfs_volume *vol = dir_ni->vol;
135	ntfs_index_context *icx;
136	u64 mref;
137	le64 lemref;
138	int lkup;
139	int olderrno;
140	int i;
141	u32 cpuchar;
142	INDEX_ENTRY *entry;
143	FILE_NAME_ATTR *found;
144	struct {
145		FILE_NAME_ATTR attr;
146		ntfschar file_name[NTFS_MAX_NAME_LEN + 1];
147	} find;
148
149	mref = (u64)-1; /* default return (not found) */
150	icx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4);
151	if (icx) {
152		if (uname_len > NTFS_MAX_NAME_LEN)
153			uname_len = NTFS_MAX_NAME_LEN;
154		find.attr.file_name_length = uname_len;
155		for (i=0; i<uname_len; i++) {
156			cpuchar = le16_to_cpu(uname[i]);
157			/*
158			 * We need upper or lower value, whichever is smaller,
159			 * but we can only convert to upper case, so we
160			 * will fail when searching for an upper case char
161			 * whose lower case is smaller (such as umlauted Y)
162			 */
163			if ((cpuchar < vol->upcase_len)
164			    && (le16_to_cpu(vol->upcase[cpuchar]) < cpuchar))
165				find.attr.file_name[i] = vol->upcase[cpuchar];
166			else
167				find.attr.file_name[i] = uname[i];
168		}
169		olderrno = errno;
170		lkup = ntfs_index_lookup((char*)&find, uname_len, icx);
171		if (errno == ENOENT)
172			errno = olderrno;
173		/*
174		 * We generally only get the first matching candidate,
175		 * so we still have to check whether this is a real match
176		 */
177		if (icx->entry && (icx->entry->ie_flags & INDEX_ENTRY_END))
178				/* get next entry if reaching end of block */
179			entry = ntfs_index_next(icx->entry, icx);
180		else
181			entry = icx->entry;
182		if (entry) {
183			found = &entry->key.file_name;
184			if (lkup
185			   && ntfs_names_are_equal(find.attr.file_name,
186				find.attr.file_name_length,
187				found->file_name, found->file_name_length,
188				IGNORE_CASE,
189				vol->upcase, vol->upcase_len))
190					lkup = 0;
191			if (!lkup) {
192				/*
193				 * name found :
194				 *    fix original name and return inode
195				 */
196				lemref = entry->indexed_file;
197				mref = le64_to_cpu(lemref);
198				if (NVolCaseSensitive(vol) || !vol->locase) {
199					for (i=0; i<found->file_name_length; i++)
200						uname[i] = found->file_name[i];
201				} else {
202					for (i=0; i<found->file_name_length; i++)
203						uname[i] = vol->locase[le16_to_cpu(found->file_name[i])];
204				}
205			}
206		}
207		ntfs_index_ctx_put(icx);
208	}
209	return (mref);
210}
211
212/*
213 *		Search for a directory junction or a symbolic link
214 *	along the target path, with target defined as a full absolute path
215 *
216 *	Returns the path translated to a Linux path
217 *		or NULL if the path is not valid
218 */
219
220static char *search_absolute(ntfs_volume *vol, ntfschar *path,
221				int count, BOOL isdir)
222{
223	ntfs_inode *ni;
224	u64 inum;
225	char *target;
226	int start;
227	int len;
228
229	target = (char*)NULL; /* default return */
230	ni = ntfs_inode_open(vol, (MFT_REF)FILE_root);
231	if (ni) {
232		start = 0;
233		/*
234		 * Examine and translate the path, until we reach either
235		 *  - the end,
236		 *  - an unknown item
237		 *  - a non-directory
238		 *  - another reparse point,
239		 * A reparse point is not dereferenced, it will be
240		 * examined later when the translated path is dereferenced,
241		 * however the final part of the path will not be adjusted
242		 * to correct case.
243		 */
244		do {
245			len = 0;
246			while (((start + len) < count)
247			    && (path[start + len] != const_cpu_to_le16('\\')))
248				len++;
249			inum = ntfs_fix_file_name(ni, &path[start], len);
250			ntfs_inode_close(ni);
251			ni = (ntfs_inode*)NULL;
252			if (inum != (u64)-1) {
253				inum = MREF(inum);
254				ni = ntfs_inode_open(vol, inum);
255				start += len;
256				if (start < count)
257					path[start++] = const_cpu_to_le16('/');
258			}
259		} while (ni
260		    && (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
261		    && !(ni->flags & FILE_ATTR_REPARSE_POINT)
262		    && (start < count));
263	if (ni
264	    && ((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ? isdir : !isdir)
265		|| (ni->flags & FILE_ATTR_REPARSE_POINT)))
266		if (ntfs_ucstombs(path, count, &target, 0) < 0) {
267			if (target) {
268				free(target);
269				target = (char*)NULL;
270			}
271		}
272	if (ni)
273		ntfs_inode_close(ni);
274	}
275	return (target);
276}
277
278/*
279 *		Search for a symbolic link along the target path,
280 *	with the target defined as a relative path
281 *
282 *	Note : the path used to access the current inode, may be
283 *	different from the one implied in the target definition,
284 *	when an inode has names in several directories.
285 *
286 *	Returns the path translated to a Linux path
287 *		or NULL if the path is not valid
288 */
289
290static char *search_relative(ntfs_inode *ni, ntfschar *path, int count)
291{
292	char *target = (char*)NULL;
293	ntfs_inode *curni;
294	ntfs_inode *newni;
295	u64 inum;
296	int pos;
297	int lth;
298	BOOL ok;
299	BOOL morelinks;
300	int max = 32; /* safety */
301
302	pos = 0;
303	ok = TRUE;
304	morelinks = FALSE;
305	curni = ntfs_dir_parent_inode(ni);
306		/*
307		 * Examine and translate the path, until we reach either
308		 *  - the end,
309		 *  - an unknown item
310		 *  - a non-directory
311		 *  - another reparse point,
312		 * A reparse point is not dereferenced, it will be
313		 * examined later when the translated path is dereferenced,
314		 * however the final part of the path will not be adjusted
315		 * to correct case.
316		 */
317	while (curni && ok && !morelinks && (pos < (count - 1)) && --max) {
318		if ((count >= (pos + 2))
319		    && (path[pos] == const_cpu_to_le16('.'))
320		    && (path[pos+1] == const_cpu_to_le16('\\'))) {
321			path[pos+1] = const_cpu_to_le16('/');
322			pos += 2;
323		} else {
324			if ((count >= (pos + 3))
325			    && (path[pos] == const_cpu_to_le16('.'))
326			    &&(path[pos+1] == const_cpu_to_le16('.'))
327			    && (path[pos+2] == const_cpu_to_le16('\\'))) {
328				path[pos+2] = const_cpu_to_le16('/');
329				pos += 3;
330				newni = ntfs_dir_parent_inode(curni);
331				if (curni != ni)
332					ntfs_inode_close(curni);
333				curni = newni;
334				if (!curni)
335					ok = FALSE;
336			} else {
337				lth = 0;
338				while (((pos + lth) < count)
339				    && (path[pos + lth] != const_cpu_to_le16('\\')))
340					lth++;
341				if (lth > 0)
342					inum = ntfs_fix_file_name(curni,&path[pos],lth);
343				else
344					inum = (u64)-1;
345				if (!lth
346				    || ((curni != ni)
347					&& ntfs_inode_close(curni))
348				    || (inum == (u64)-1))
349					ok = FALSE;
350				else {
351					curni = ntfs_inode_open(ni->vol, MREF(inum));
352					if (!curni)
353						ok = FALSE;
354					else {
355						if (curni->flags & FILE_ATTR_REPARSE_POINT)
356							morelinks = TRUE;
357						if (ok && ((pos + lth) < count)) {
358							path[pos + lth] = const_cpu_to_le16('/');
359							pos += lth + 1;
360							if (morelinks
361							   && ntfs_inode_close(curni))
362								ok = FALSE;
363						} else {
364							pos += lth;
365							if (!morelinks
366							  && (ni->mrec->flags ^ curni->mrec->flags)
367							    & MFT_RECORD_IS_DIRECTORY)
368								ok = FALSE;
369							if (ntfs_inode_close(curni))
370								ok = FALSE;
371						}
372					}
373				}
374			}
375		}
376	}
377
378	if (ok && (ntfs_ucstombs(path, count, &target, 0) < 0)) {
379		free(target); // needed ?
380		target = (char*)NULL;
381	}
382	return (target);
383}
384
385/*
386 *		Check whether a drive letter has been defined in .NTFS-3G
387 *
388 *	Returns 1 if found,
389 *		0 if not found,
390 *		-1 if there was an error (described by errno)
391 */
392
393static int ntfs_drive_letter(ntfs_volume *vol, ntfschar letter)
394{
395	char defines[NTFS_MAX_NAME_LEN + 5];
396	char *drive;
397	int ret;
398	int sz;
399	int olderrno;
400	ntfs_inode *ni;
401
402	ret = -1;
403	drive = (char*)NULL;
404	sz = ntfs_ucstombs(&letter, 1, &drive, 0);
405	if (sz > 0) {
406		strcpy(defines,mappingdir);
407		if ((*drive >= 'a') && (*drive <= 'z'))
408			*drive += 'A' - 'a';
409		strcat(defines,drive);
410		strcat(defines,":");
411		olderrno = errno;
412		ni = ntfs_pathname_to_inode(vol, NULL, defines);
413		if (ni && !ntfs_inode_close(ni))
414			ret = 1;
415		else
416			if (errno == ENOENT) {
417				ret = 0;
418					/* avoid errno pollution */
419				errno = olderrno;
420			}
421	}
422	if (drive)
423		free(drive);
424	return (ret);
425}
426
427/*
428 *		Check whether reparse data describes a valid wsl special file
429 *	which is either a socket, a fifo, or a character or block device
430 *
431 *	Return zero if valid, otherwise returns a negative error code
432 */
433
434int ntfs_reparse_check_wsl(ntfs_inode *ni, const REPARSE_POINT *reparse)
435{
436	int res;
437
438	res = -EOPNOTSUPP;
439	switch (reparse->reparse_tag) {
440	case IO_REPARSE_TAG_AF_UNIX :
441	case IO_REPARSE_TAG_LX_FIFO :
442	case IO_REPARSE_TAG_LX_CHR :
443	case IO_REPARSE_TAG_LX_BLK :
444		if (!reparse->reparse_data_length
445		    && (ni->flags & FILE_ATTRIBUTE_RECALL_ON_OPEN))
446			res = 0;
447		break;
448	default :
449		break;
450	}
451	if (res)
452		errno = EOPNOTSUPP;
453	return (res);
454}
455
456/*
457 *		Do some sanity checks on reparse data
458 *
459 *	Microsoft reparse points have an 8-byte header whereas
460 *	non-Microsoft reparse points have a 24-byte header.  In each case,
461 *	'reparse_data_length' must equal the number of non-header bytes.
462 *
463 *	If the reparse data looks like a junction point or symbolic
464 *	link, more checks can be done.
465 *
466 */
467
468static BOOL valid_reparse_data(ntfs_inode *ni,
469			const REPARSE_POINT *reparse_attr, size_t size)
470{
471	BOOL ok;
472	unsigned int offs;
473	unsigned int lth;
474	const struct MOUNT_POINT_REPARSE_DATA *mount_point_data;
475	const struct SYMLINK_REPARSE_DATA *symlink_data;
476	const struct WSL_LINK_REPARSE_DATA *wsl_reparse_data;
477
478	ok = ni && reparse_attr
479		&& (size >= sizeof(REPARSE_POINT))
480		&& (reparse_attr->reparse_tag != IO_REPARSE_TAG_RESERVED_ZERO)
481		&& (((size_t)le16_to_cpu(reparse_attr->reparse_data_length)
482			 + sizeof(REPARSE_POINT)
483			 + ((reparse_attr->reparse_tag &
484			     IO_REPARSE_TAG_IS_MICROSOFT) ? 0 : sizeof(GUID))) == size);
485	if (ok) {
486		switch (reparse_attr->reparse_tag) {
487		case IO_REPARSE_TAG_MOUNT_POINT :
488			if (size < sizeof(REPARSE_POINT) +
489				   sizeof(struct MOUNT_POINT_REPARSE_DATA)) {
490				ok = FALSE;
491				break;
492			}
493			mount_point_data = (const struct MOUNT_POINT_REPARSE_DATA*)
494						reparse_attr->reparse_data;
495			offs = le16_to_cpu(mount_point_data->subst_name_offset);
496			lth = le16_to_cpu(mount_point_data->subst_name_length);
497				/* consistency checks */
498			if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
499			    || ((size_t)((sizeof(REPARSE_POINT)
500				 + sizeof(struct MOUNT_POINT_REPARSE_DATA)
501				 + offs + lth)) > size))
502				ok = FALSE;
503			break;
504		case IO_REPARSE_TAG_SYMLINK :
505			if (size < sizeof(REPARSE_POINT) +
506				   sizeof(struct SYMLINK_REPARSE_DATA)) {
507				ok = FALSE;
508				break;
509			}
510			symlink_data = (const struct SYMLINK_REPARSE_DATA*)
511						reparse_attr->reparse_data;
512			offs = le16_to_cpu(symlink_data->subst_name_offset);
513			lth = le16_to_cpu(symlink_data->subst_name_length);
514			if ((size_t)((sizeof(REPARSE_POINT)
515				 + sizeof(struct SYMLINK_REPARSE_DATA)
516				 + offs + lth)) > size)
517				ok = FALSE;
518			break;
519		case IO_REPARSE_TAG_LX_SYMLINK :
520			wsl_reparse_data = (const struct WSL_LINK_REPARSE_DATA*)
521						reparse_attr->reparse_data;
522			if ((le16_to_cpu(reparse_attr->reparse_data_length)
523					<= sizeof(wsl_reparse_data->type))
524			    || (wsl_reparse_data->type != const_cpu_to_le32(2)))
525				ok = FALSE;
526			break;
527		case IO_REPARSE_TAG_AF_UNIX :
528		case IO_REPARSE_TAG_LX_FIFO :
529		case IO_REPARSE_TAG_LX_CHR :
530		case IO_REPARSE_TAG_LX_BLK :
531			if (reparse_attr->reparse_data_length
532			    || !(ni->flags & FILE_ATTRIBUTE_RECALL_ON_OPEN))
533				ok = FALSE;
534			break;
535		default :
536			break;
537		}
538	}
539	if (!ok)
540		errno = EINVAL;
541	return (ok);
542}
543
544/*
545 *		Check and translate the target of a junction point or
546 *	a full absolute symbolic link.
547 *
548 *	A full target definition begins with "\??\" or "\\?\"
549 *
550 *	The fully defined target is redefined as a relative link,
551 *		- either to the target if found on the same device.
552 *		- or into the /.NTFS-3G directory for the user to define
553 *	In the first situation, the target is translated to case-sensitive path.
554 *
555 *	returns the target converted to a relative symlink
556 *		or NULL if there were some problem, as described by errno
557 */
558
559static char *ntfs_get_fulllink(ntfs_volume *vol, ntfschar *junction,
560			int count, const char *mnt_point, BOOL isdir)
561{
562	char *target;
563	char *fulltarget;
564	int sz;
565	char *q;
566	enum { DIR_JUNCTION, VOL_JUNCTION, NO_JUNCTION } kind;
567
568	target = (char*)NULL;
569	fulltarget = (char*)NULL;
570			/*
571			 * For a valid directory junction we want \??\x:\
572			 * where \ is an individual char and x a non-null char
573			 */
574	if ((count >= 7)
575	    && !memcmp(junction,dir_junction_head,8)
576	    && junction[4]
577	    && (junction[5] == const_cpu_to_le16(':'))
578	    && (junction[6] == const_cpu_to_le16('\\')))
579		kind = DIR_JUNCTION;
580	else
581			/*
582			 * For a valid volume junction we want \\?\Volume{
583			 * and a final \ (where \ is an individual char)
584			 */
585		if ((count >= 12)
586		    && !memcmp(junction,vol_junction_head,22)
587		    && (junction[count-1] == const_cpu_to_le16('\\')))
588			kind = VOL_JUNCTION;
589		else
590			kind = NO_JUNCTION;
591			/*
592			 * Directory junction with an explicit path and
593			 * no specific definition for the drive letter :
594			 * try to interpret as a target on the same volume
595			 */
596	if ((kind == DIR_JUNCTION)
597	    && (count >= 7)
598	    && junction[7]
599	    && !ntfs_drive_letter(vol, junction[4])) {
600		target = search_absolute(vol,&junction[7],count - 7, isdir);
601		if (target) {
602			fulltarget = (char*)ntfs_malloc(strlen(mnt_point)
603					+ strlen(target) + 2);
604			if (fulltarget) {
605				strcpy(fulltarget,mnt_point);
606				strcat(fulltarget,"/");
607				strcat(fulltarget,target);
608			}
609			free(target);
610		}
611	}
612			/*
613			 * Volume junctions or directory junctions with
614			 * target not found on current volume :
615			 * link to /.NTFS-3G/target which the user can
616			 * define as a symbolic link to the real target
617			 */
618	if (((kind == DIR_JUNCTION) && !fulltarget)
619	    || (kind == VOL_JUNCTION)) {
620		sz = ntfs_ucstombs(&junction[4],
621			(kind == VOL_JUNCTION ? count - 5 : count - 4),
622			&target, 0);
623		if ((sz > 0) && target) {
624				/* reverse slashes */
625			for (q=target; *q; q++)
626				if (*q == '\\')
627					*q = '/';
628				/* force uppercase drive letter */
629			if ((target[1] == ':')
630			    && (target[0] >= 'a')
631			    && (target[0] <= 'z'))
632				target[0] += 'A' - 'a';
633			fulltarget = (char*)ntfs_malloc(strlen(mnt_point)
634				    + sizeof(mappingdir) + strlen(target) + 1);
635			if (fulltarget) {
636				strcpy(fulltarget,mnt_point);
637				strcat(fulltarget,"/");
638				strcat(fulltarget,mappingdir);
639				strcat(fulltarget,target);
640			}
641		}
642		if (target)
643			free(target);
644	}
645	return (fulltarget);
646}
647
648/*
649 *		Check and translate the target of an absolute symbolic link.
650 *
651 *	An absolute target definition begins with "\" or "x:\"
652 *
653 *	The absolute target is redefined as a relative link,
654 *		- either to the target if found on the same device.
655 *		- or into the /.NTFS-3G directory for the user to define
656 *	In the first situation, the target is translated to case-sensitive path.
657 *
658 *	returns the target converted to a relative symlink
659 *		or NULL if there were some problem, as described by errno
660 */
661
662char *ntfs_get_abslink(ntfs_volume *vol, ntfschar *junction, int count,
663			const char *mnt_point __attribute__((unused)),
664			BOOL isdir)
665{
666	char *target;
667	char *fulltarget;
668	int sz;
669	char *q;
670	enum { FULL_PATH, ABS_PATH, REJECTED_PATH } kind;
671
672	target = (char*)NULL;
673	fulltarget = (char*)NULL;
674			/*
675			 * For a full valid path we want x:\
676			 * where \ is an individual char and x a non-null char
677			 */
678	if ((count >= 3)
679	    && junction[0]
680	    && (junction[1] == const_cpu_to_le16(':'))
681	    && (junction[2] == const_cpu_to_le16('\\')))
682		kind = FULL_PATH;
683	else
684			/*
685			 * For an absolute path we want an initial \
686			 */
687		if ((count >= 0)
688		    && (junction[0] == const_cpu_to_le16('\\')))
689			kind = ABS_PATH;
690		else
691			kind = REJECTED_PATH;
692			/*
693			 * Full path, with a drive letter and
694			 * no specific definition for the drive letter :
695			 * try to interpret as a target on the same volume.
696			 * Do the same for an abs path with no drive letter.
697			 */
698	if (((kind == FULL_PATH)
699	    && (count >= 3)
700	    && junction[3]
701	    && !ntfs_drive_letter(vol, junction[0]))
702	    || (kind == ABS_PATH)) {
703		if (kind == ABS_PATH)
704			target = search_absolute(vol, &junction[1],
705				count - 1, isdir);
706		else
707			target = search_absolute(vol, &junction[3],
708				count - 3, isdir);
709		if (target) {
710			fulltarget = (char*)ntfs_malloc(
711					strlen(vol->abs_mnt_point)
712					+ strlen(target) + 2);
713			if (fulltarget) {
714				strcpy(fulltarget,vol->abs_mnt_point);
715				strcat(fulltarget,"/");
716				strcat(fulltarget,target);
717			}
718			free(target);
719		}
720	}
721			/*
722			 * full path with target not found on current volume :
723			 * link to /.NTFS-3G/target which the user can
724			 * define as a symbolic link to the real target
725			 */
726	if ((kind == FULL_PATH) && !fulltarget) {
727		sz = ntfs_ucstombs(&junction[0],
728			count,&target, 0);
729		if ((sz > 0) && target) {
730				/* reverse slashes */
731			for (q=target; *q; q++)
732				if (*q == '\\')
733					*q = '/';
734				/* force uppercase drive letter */
735			if ((target[1] == ':')
736			    && (target[0] >= 'a')
737			    && (target[0] <= 'z'))
738				target[0] += 'A' - 'a';
739			fulltarget = (char*)ntfs_malloc(
740				strlen(vol->abs_mnt_point)
741				    + sizeof(mappingdir) + strlen(target) + 1);
742			if (fulltarget) {
743				strcpy(fulltarget,vol->abs_mnt_point);
744				strcat(fulltarget,"/");
745				strcat(fulltarget,mappingdir);
746				strcat(fulltarget,target);
747			}
748		}
749		if (target)
750			free(target);
751	}
752	return (fulltarget);
753}
754
755/*
756 *		Check and translate the target of a relative symbolic link.
757 *
758 *	A relative target definition does not begin with "\"
759 *
760 *	The original definition of relative target is kept, it is just
761 *	translated to a case-sensitive path.
762 *
763 *	returns the target converted to a relative symlink
764 *		or NULL if there were some problem, as described by errno
765 */
766
767static char *ntfs_get_rellink(ntfs_inode *ni, ntfschar *junction, int count)
768{
769	char *target;
770
771	target = search_relative(ni,junction,count);
772	return (target);
773}
774
775/*
776 *		Get the target for a junction point or symbolic link
777 *	Should only be called for files or directories with reparse data
778 *
779 *	returns the target converted to a relative path, or NULL
780 *		if some error occurred, as described by errno
781 *		errno is EOPNOTSUPP if the reparse point is not a valid
782 *			symbolic link or directory junction
783 */
784
785char *ntfs_make_symlink(ntfs_inode *ni, const char *mnt_point)
786{
787	s64 attr_size = 0;
788	char *target;
789	unsigned int offs;
790	unsigned int lth;
791	ntfs_volume *vol;
792	REPARSE_POINT *reparse_attr;
793	struct MOUNT_POINT_REPARSE_DATA *mount_point_data;
794	struct SYMLINK_REPARSE_DATA *symlink_data;
795	struct WSL_LINK_REPARSE_DATA *wsl_link_data;
796	enum { FULL_TARGET, ABS_TARGET, REL_TARGET } kind;
797	ntfschar *p;
798	BOOL bad;
799	BOOL isdir;
800
801	target = (char*)NULL;
802	bad = TRUE;
803	isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
804			 != const_cpu_to_le16(0);
805	vol = ni->vol;
806	reparse_attr = (REPARSE_POINT*)ntfs_attr_readall(ni,
807			AT_REPARSE_POINT,(ntfschar*)NULL, 0, &attr_size);
808	if (reparse_attr && attr_size
809			&& valid_reparse_data(ni, reparse_attr, attr_size)) {
810		switch (reparse_attr->reparse_tag) {
811		case IO_REPARSE_TAG_MOUNT_POINT :
812			mount_point_data = (struct MOUNT_POINT_REPARSE_DATA*)
813						reparse_attr->reparse_data;
814			offs = le16_to_cpu(mount_point_data->subst_name_offset);
815			lth = le16_to_cpu(mount_point_data->subst_name_length);
816				/* reparse data consistency has been checked */
817			target = ntfs_get_fulllink(vol,
818				(ntfschar*)&mount_point_data->path_buffer[offs],
819				lth/2, mnt_point, isdir);
820			if (target)
821				bad = FALSE;
822			break;
823		case IO_REPARSE_TAG_SYMLINK :
824			symlink_data = (struct SYMLINK_REPARSE_DATA*)
825						reparse_attr->reparse_data;
826			offs = le16_to_cpu(symlink_data->subst_name_offset);
827			lth = le16_to_cpu(symlink_data->subst_name_length);
828			p = (ntfschar*)&symlink_data->path_buffer[offs];
829				/*
830				 * Predetermine the kind of target,
831				 * the called function has to make a full check
832				 */
833			if (*p++ == const_cpu_to_le16('\\')) {
834				if ((*p == const_cpu_to_le16('?'))
835				    || (*p == const_cpu_to_le16('\\')))
836					kind = FULL_TARGET;
837				else
838					kind = ABS_TARGET;
839			} else
840				if (*p == const_cpu_to_le16(':'))
841					kind = ABS_TARGET;
842				else
843					kind = REL_TARGET;
844			p--;
845				/* reparse data consistency has been checked */
846			switch (kind) {
847			case FULL_TARGET :
848				if (!(symlink_data->flags
849				   & const_cpu_to_le32(1))) {
850					target = ntfs_get_fulllink(vol,
851						p, lth/2,
852						mnt_point, isdir);
853					if (target)
854						bad = FALSE;
855				}
856				break;
857			case ABS_TARGET :
858				if (symlink_data->flags
859				   & const_cpu_to_le32(1)) {
860					target = ntfs_get_abslink(vol,
861						p, lth/2,
862						mnt_point, isdir);
863					if (target)
864						bad = FALSE;
865				}
866				break;
867			case REL_TARGET :
868				if (symlink_data->flags
869				   & const_cpu_to_le32(1)) {
870					target = ntfs_get_rellink(ni,
871						p, lth/2);
872					if (target)
873						bad = FALSE;
874				}
875				break;
876			}
877			break;
878		case IO_REPARSE_TAG_LX_SYMLINK :
879			wsl_link_data = (struct WSL_LINK_REPARSE_DATA*)
880						reparse_attr->reparse_data;
881			if (wsl_link_data->type == const_cpu_to_le32(2)) {
882				lth = le16_to_cpu(
883					reparse_attr->reparse_data_length)
884					- sizeof(wsl_link_data->type);
885				target = (char*)ntfs_malloc(lth + 1);
886				if (target) {
887					memcpy(target, wsl_link_data->link,
888						lth);
889					target[lth] = 0;
890					bad = FALSE;
891				}
892			}
893			break;
894		}
895		free(reparse_attr);
896	}
897	if (bad)
898		errno = EOPNOTSUPP;
899	return (target);
900}
901
902/*
903 *		Check whether a reparse point looks like a junction point
904 *	or a symbolic link.
905 *	Should only be called for files or directories with reparse data
906 *
907 *	The validity of the target is not checked.
908 */
909
910BOOL ntfs_possible_symlink(ntfs_inode *ni)
911{
912	s64 attr_size = 0;
913	REPARSE_POINT *reparse_attr;
914	BOOL possible;
915
916	possible = FALSE;
917	reparse_attr = (REPARSE_POINT*)ntfs_attr_readall(ni,
918			AT_REPARSE_POINT,(ntfschar*)NULL, 0, &attr_size);
919	if (reparse_attr && attr_size) {
920		switch (reparse_attr->reparse_tag) {
921		case IO_REPARSE_TAG_MOUNT_POINT :
922		case IO_REPARSE_TAG_SYMLINK :
923		case IO_REPARSE_TAG_LX_SYMLINK :
924			possible = TRUE;
925		default : ;
926		}
927		free(reparse_attr);
928	}
929	return (possible);
930}
931
932
933/*
934 *			Set the index for new reparse data
935 *
936 *	Returns 0 if success
937 *		-1 if failure, explained by errno
938 */
939
940static int set_reparse_index(ntfs_inode *ni, ntfs_index_context *xr,
941			le32 reparse_tag)
942{
943	struct REPARSE_INDEX indx;
944	u64 file_id_cpu;
945	le64 file_id;
946	le16 seqn;
947
948	seqn = ni->mrec->sequence_number;
949	file_id_cpu = MK_MREF(ni->mft_no,le16_to_cpu(seqn));
950	file_id = cpu_to_le64(file_id_cpu);
951	indx.header.data_offset = const_cpu_to_le16(
952					sizeof(INDEX_ENTRY_HEADER)
953					+ sizeof(REPARSE_INDEX_KEY));
954	indx.header.data_length = const_cpu_to_le16(0);
955	indx.header.reservedV = const_cpu_to_le32(0);
956	indx.header.length = const_cpu_to_le16(
957					sizeof(struct REPARSE_INDEX));
958	indx.header.key_length = const_cpu_to_le16(
959					sizeof(REPARSE_INDEX_KEY));
960	indx.header.flags = const_cpu_to_le16(0);
961	indx.header.reserved = const_cpu_to_le16(0);
962	indx.key.reparse_tag = reparse_tag;
963		/* danger on processors which require proper alignment ! */
964	memcpy(&indx.key.file_id, &file_id, 8);
965	indx.filling = const_cpu_to_le32(0);
966	ntfs_index_ctx_reinit(xr);
967	return (ntfs_ie_add(xr,(INDEX_ENTRY*)&indx));
968}
969
970
971/*
972 *		Remove a reparse data index entry if attribute present
973 *
974 *	Returns the size of existing reparse data
975 *			(the existing reparse tag is returned)
976 *		-1 if failure, explained by errno
977 */
978
979static int remove_reparse_index(ntfs_attr *na, ntfs_index_context *xr,
980				le32 *preparse_tag)
981{
982	REPARSE_INDEX_KEY key;
983	u64 file_id_cpu;
984	le64 file_id;
985	s64 size;
986	le16 seqn;
987	int ret;
988
989	ret = na->data_size;
990	if (ret) {
991			/* read the existing reparse_tag */
992		size = ntfs_attr_pread(na, 0, 4, preparse_tag);
993		if (size == 4) {
994			seqn = na->ni->mrec->sequence_number;
995			file_id_cpu = MK_MREF(na->ni->mft_no,le16_to_cpu(seqn));
996			file_id = cpu_to_le64(file_id_cpu);
997			key.reparse_tag = *preparse_tag;
998		/* danger on processors which require proper alignment ! */
999			memcpy(&key.file_id, &file_id, 8);
1000			if (!ntfs_index_lookup(&key, sizeof(REPARSE_INDEX_KEY), xr)
1001			    && ntfs_index_rm(xr))
1002				ret = -1;
1003		} else {
1004			ret = -1;
1005			errno = ENODATA;
1006		}
1007	}
1008	return (ret);
1009}
1010
1011/*
1012 *		Open the $Extend/$Reparse file and its index
1013 *
1014 *	Return the index context if opened
1015 *		or NULL if an error occurred (errno tells why)
1016 *
1017 *	The index has to be freed and inode closed when not needed any more.
1018 */
1019
1020static ntfs_index_context *open_reparse_index(ntfs_volume *vol)
1021{
1022	u64 inum;
1023	ntfs_inode *ni;
1024	ntfs_inode *dir_ni;
1025	ntfs_index_context *xr;
1026
1027		/* do not use path_name_to inode - could reopen root */
1028	dir_ni = ntfs_inode_open(vol, FILE_Extend);
1029	ni = (ntfs_inode*)NULL;
1030	if (dir_ni) {
1031		inum = ntfs_inode_lookup_by_mbsname(dir_ni,"$Reparse");
1032		if (inum != (u64)-1)
1033			ni = ntfs_inode_open(vol, inum);
1034		ntfs_inode_close(dir_ni);
1035	}
1036	if (ni) {
1037		xr = ntfs_index_ctx_get(ni, reparse_index_name, 2);
1038		if (!xr) {
1039			ntfs_inode_close(ni);
1040		}
1041	} else
1042		xr = (ntfs_index_context*)NULL;
1043	return (xr);
1044}
1045
1046
1047/*
1048 *		Update the reparse data and index
1049 *
1050 *	The reparse data attribute should have been created, and
1051 *	an existing index is expected if there is an existing value.
1052 *
1053 *	Returns 0 if success
1054 *		-1 if failure, explained by errno
1055 *	If could not remove the existing index, nothing is done,
1056 *	If could not write the new data, no index entry is inserted
1057 *	If failed to insert the index, data is removed
1058 */
1059
1060static int update_reparse_data(ntfs_inode *ni, ntfs_index_context *xr,
1061			const char *value, size_t size)
1062{
1063	int res;
1064	int written;
1065	int oldsize;
1066	ntfs_attr *na;
1067	le32 reparse_tag;
1068
1069	res = 0;
1070	na = ntfs_attr_open(ni, AT_REPARSE_POINT, AT_UNNAMED, 0);
1071	if (na) {
1072			/* remove the existing reparse data */
1073		oldsize = remove_reparse_index(na,xr,&reparse_tag);
1074		if (oldsize < 0)
1075			res = -1;
1076		else {
1077			/* resize attribute */
1078			res = ntfs_attr_truncate(na, (s64)size);
1079			/* overwrite value if any */
1080			if (!res && value) {
1081				written = (int)ntfs_attr_pwrite(na,
1082						 (s64)0, (s64)size, value);
1083				if (written != (s64)size) {
1084					ntfs_log_error("Failed to update "
1085						"reparse data\n");
1086					errno = EIO;
1087					res = -1;
1088				}
1089			}
1090			if (!res
1091			    && set_reparse_index(ni,xr,
1092				((const REPARSE_POINT*)value)->reparse_tag)
1093			    && (oldsize > 0)) {
1094				/*
1095				 * If cannot index, try to remove the reparse
1096				 * data and log the error. There will be an
1097				 * inconsistency if removal fails.
1098				 */
1099				ntfs_attr_rm(na);
1100				ntfs_log_error("Failed to index reparse data."
1101						" Possible corruption.\n");
1102			}
1103		}
1104		ntfs_attr_close(na);
1105		NInoSetDirty(ni);
1106	} else
1107		res = -1;
1108	return (res);
1109}
1110
1111
1112/*
1113 *		Delete a reparse index entry
1114 *
1115 *	Returns 0 if success
1116 *		-1 if failure, explained by errno
1117 */
1118
1119int ntfs_delete_reparse_index(ntfs_inode *ni)
1120{
1121	ntfs_index_context *xr;
1122	ntfs_inode *xrni;
1123	ntfs_attr *na;
1124	le32 reparse_tag;
1125	int res;
1126
1127	res = 0;
1128	na = ntfs_attr_open(ni, AT_REPARSE_POINT, AT_UNNAMED, 0);
1129	if (na) {
1130			/*
1131			 * read the existing reparse data (the tag is enough)
1132			 * and un-index it
1133			 */
1134		xr = open_reparse_index(ni->vol);
1135		if (xr) {
1136			if (remove_reparse_index(na,xr,&reparse_tag) < 0)
1137				res = -1;
1138			xrni = xr->ni;
1139			ntfs_index_entry_mark_dirty(xr);
1140			NInoSetDirty(xrni);
1141			ntfs_index_ctx_put(xr);
1142			ntfs_inode_close(xrni);
1143		}
1144		ntfs_attr_close(na);
1145	}
1146	return (res);
1147}
1148
1149
1150/*
1151 *		Get the ntfs reparse data into an extended attribute
1152 *
1153 *	Returns the reparse data size
1154 *		and the buffer is updated if it is long enough
1155 */
1156
1157int ntfs_get_ntfs_reparse_data(ntfs_inode *ni, char *value, size_t size)
1158{
1159	REPARSE_POINT *reparse_attr;
1160	s64 attr_size;
1161
1162	attr_size = 0;	/* default to no data and no error */
1163	if (ni) {
1164		if (ni->flags & FILE_ATTR_REPARSE_POINT) {
1165			reparse_attr = (REPARSE_POINT*)ntfs_attr_readall(ni,
1166				AT_REPARSE_POINT,(ntfschar*)NULL, 0, &attr_size);
1167			if (reparse_attr) {
1168				if (attr_size <= (s64)size) {
1169					if (value)
1170						memcpy(value,reparse_attr,
1171							attr_size);
1172					else
1173						errno = EINVAL;
1174				}
1175				free(reparse_attr);
1176			}
1177		} else
1178			errno = ENODATA;
1179	}
1180	return (attr_size ? (int)attr_size : -errno);
1181}
1182
1183/*
1184 *		Set the reparse data from an extended attribute
1185 *
1186 *	Warning : the new data is not checked
1187 *
1188 *	Returns 0, or -1 if there is a problem
1189 */
1190
1191int ntfs_set_ntfs_reparse_data(ntfs_inode *ni,
1192			const char *value, size_t size, int flags)
1193{
1194	int res;
1195	u8 dummy;
1196	ntfs_inode *xrni;
1197	ntfs_index_context *xr;
1198
1199	res = 0;
1200			/*
1201			 * reparse data compatibily with EA is not checked
1202			 * any more, it is required by Windows 10, but may
1203			 * lead to problems with earlier versions.
1204			 */
1205	if (ni && valid_reparse_data(ni, (const REPARSE_POINT*)value, size)) {
1206		xr = open_reparse_index(ni->vol);
1207		if (xr) {
1208			if (!ntfs_attr_exist(ni,AT_REPARSE_POINT,
1209						AT_UNNAMED,0)) {
1210				if (!(flags & XATTR_REPLACE)) {
1211			/*
1212			 * no reparse data attribute : add one,
1213			 * apparently, this does not feed the new value in
1214			 * Note : NTFS version must be >= 3
1215			 */
1216					if (ni->vol->major_ver >= 3) {
1217						res = ntfs_attr_add(ni,
1218							AT_REPARSE_POINT,
1219							AT_UNNAMED,0,&dummy,
1220							(s64)0);
1221						if (!res) {
1222						    ni->flags |=
1223							FILE_ATTR_REPARSE_POINT;
1224						    NInoFileNameSetDirty(ni);
1225						}
1226						NInoSetDirty(ni);
1227					} else {
1228						errno = EOPNOTSUPP;
1229						res = -1;
1230					}
1231				} else {
1232					errno = ENODATA;
1233					res = -1;
1234				}
1235			} else {
1236				if (flags & XATTR_CREATE) {
1237					errno = EEXIST;
1238					res = -1;
1239				}
1240			}
1241			if (!res) {
1242					/* update value and index */
1243				res = update_reparse_data(ni,xr,value,size);
1244			}
1245			xrni = xr->ni;
1246			ntfs_index_entry_mark_dirty(xr);
1247			NInoSetDirty(xrni);
1248			ntfs_index_ctx_put(xr);
1249			ntfs_inode_close(xrni);
1250		} else {
1251			res = -1;
1252		}
1253	} else {
1254		errno = EINVAL;
1255		res = -1;
1256	}
1257	return (res ? -1 : 0);
1258}
1259
1260/*
1261 *		Remove the reparse data
1262 *
1263 *	Returns 0, or -1 if there is a problem
1264 */
1265
1266int ntfs_remove_ntfs_reparse_data(ntfs_inode *ni)
1267{
1268	int res;
1269	int olderrno;
1270	ntfs_attr *na;
1271	ntfs_inode *xrni;
1272	ntfs_index_context *xr;
1273	le32 reparse_tag;
1274
1275	res = 0;
1276	if (ni) {
1277		/*
1278		 * open and delete the reparse data
1279		 */
1280		na = ntfs_attr_open(ni, AT_REPARSE_POINT,
1281			AT_UNNAMED,0);
1282		if (na) {
1283			/* first remove index (reparse data needed) */
1284			xr = open_reparse_index(ni->vol);
1285			if (xr) {
1286				if (remove_reparse_index(na,xr,
1287						&reparse_tag) < 0) {
1288					res = -1;
1289				} else {
1290					/* now remove attribute */
1291					res = ntfs_attr_rm(na);
1292					if (!res) {
1293						ni->flags &=
1294						    ~FILE_ATTR_REPARSE_POINT;
1295						NInoFileNameSetDirty(ni);
1296					} else {
1297					/*
1298					 * If we could not remove the
1299					 * attribute, try to restore the
1300					 * index and log the error. There
1301					 * will be an inconsistency if
1302					 * the reindexing fails.
1303					 */
1304						set_reparse_index(ni, xr,
1305							reparse_tag);
1306						ntfs_log_error(
1307						"Failed to remove reparse data."
1308						" Possible corruption.\n");
1309					}
1310				}
1311				xrni = xr->ni;
1312				ntfs_index_entry_mark_dirty(xr);
1313				NInoSetDirty(xrni);
1314				ntfs_index_ctx_put(xr);
1315				ntfs_inode_close(xrni);
1316			}
1317			olderrno = errno;
1318			ntfs_attr_close(na);
1319					/* avoid errno pollution */
1320			if (errno == ENOENT)
1321				errno = olderrno;
1322		} else {
1323			errno = ENODATA;
1324			res = -1;
1325		}
1326		NInoSetDirty(ni);
1327	} else {
1328		errno = EINVAL;
1329		res = -1;
1330	}
1331	return (res ? -1 : 0);
1332}
1333
1334/*
1335 *		Set reparse data for a WSL type symlink
1336 */
1337
1338int ntfs_reparse_set_wsl_symlink(ntfs_inode *ni,
1339			const ntfschar *target, int target_len)
1340{
1341	int res;
1342	int len;
1343	int reparse_len;
1344	char *utarget;
1345	REPARSE_POINT *reparse;
1346	struct WSL_LINK_REPARSE_DATA *data;
1347
1348	res = -1;
1349	utarget = (char*)NULL;
1350	len = ntfs_ucstombs(target, target_len, &utarget, 0);
1351	if (len > 0) {
1352		reparse_len = sizeof(REPARSE_POINT) + sizeof(data->type) + len;
1353		reparse = (REPARSE_POINT*)malloc(reparse_len);
1354		if (reparse) {
1355			data = (struct WSL_LINK_REPARSE_DATA*)
1356					reparse->reparse_data;
1357			reparse->reparse_tag = IO_REPARSE_TAG_LX_SYMLINK;
1358			reparse->reparse_data_length
1359				= cpu_to_le16(sizeof(data->type) + len);
1360			reparse->reserved = const_cpu_to_le16(0);
1361			data->type = const_cpu_to_le32(2);
1362			memcpy(data->link, utarget, len);
1363			res = ntfs_set_ntfs_reparse_data(ni,
1364				(char*)reparse, reparse_len, 0);
1365			free(reparse);
1366		}
1367	}
1368	free(utarget);
1369	return (res);
1370}
1371
1372/*
1373 *		Set reparse data for a WSL special file other than a symlink
1374 *	(socket, fifo, character or block device)
1375 */
1376
1377int ntfs_reparse_set_wsl_not_symlink(ntfs_inode *ni, mode_t mode)
1378{
1379	int res;
1380	int len;
1381	int reparse_len;
1382	le32 reparse_tag;
1383	REPARSE_POINT *reparse;
1384
1385	res = -1;
1386	len = 0;
1387	switch (mode) {
1388	case S_IFSOCK :
1389		reparse_tag = IO_REPARSE_TAG_AF_UNIX;
1390		break;
1391	case S_IFIFO :
1392		reparse_tag = IO_REPARSE_TAG_LX_FIFO;
1393		break;
1394	case S_IFCHR :
1395		reparse_tag = IO_REPARSE_TAG_LX_CHR;
1396		break;
1397	case S_IFBLK :
1398		reparse_tag = IO_REPARSE_TAG_LX_BLK;
1399		break;
1400	default :
1401		len = -1;
1402		errno = EOPNOTSUPP;
1403		break;
1404	}
1405	if (len >= 0) {
1406		reparse_len = sizeof(REPARSE_POINT) + len;
1407		reparse = (REPARSE_POINT*)malloc(reparse_len);
1408		if (reparse) {
1409			reparse->reparse_tag = reparse_tag;
1410			reparse->reparse_data_length = cpu_to_le16(len);
1411			reparse->reserved = const_cpu_to_le16(0);
1412			res = ntfs_set_ntfs_reparse_data(ni,
1413				(char*)reparse, reparse_len, 0);
1414			free(reparse);
1415		}
1416	}
1417	return (res);
1418}
1419
1420
1421/*
1422 *		Get the reparse data into a buffer
1423 *
1424 *	Returns the buffer if the reparse data exists and is valid
1425 *		NULL otherwise (with errno set according to the cause).
1426 *	When a buffer is returned, it has to be freed by caller.
1427 */
1428
1429REPARSE_POINT *ntfs_get_reparse_point(ntfs_inode *ni)
1430{
1431	s64 attr_size = 0;
1432	REPARSE_POINT *reparse_attr;
1433
1434	reparse_attr = (REPARSE_POINT*)NULL;
1435	if (ni) {
1436		reparse_attr = (REPARSE_POINT*)ntfs_attr_readall(ni,
1437			AT_REPARSE_POINT,(ntfschar*)NULL, 0, &attr_size);
1438		if (reparse_attr
1439		    && !valid_reparse_data(ni, reparse_attr, attr_size)) {
1440			free(reparse_attr);
1441			reparse_attr = (REPARSE_POINT*)NULL;
1442			errno = EINVAL;
1443		}
1444	} else
1445		errno = EINVAL;
1446	return (reparse_attr);
1447}
1448