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