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