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