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