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