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