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