xref: /haiku/src/add-ons/kernel/file_systems/ntfs/libntfs/reparse.c (revision 776c58b2b56d8bcf33638a2ecb6c697f95a1cbf3)
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 				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 
225 static 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 
282 static 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 
366 static 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 
410 static 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 
471 static 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 
574 static 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 
676 static 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 
694 char *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 
804 BOOL 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 
834 static 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 
874 static 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 
915 static 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 
956 static 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 
1016 int 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 
1055 int 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 
1089 int 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 
1159 int 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