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