2 * reparse.c - Processing of reparse points
4 * This module is part of ntfs-3g library
6 * Copyright (c) 2008-2012 Jean-Pierre Andre
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.
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.
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
37 #ifdef HAVE_SYS_STAT_H
42 #include <sys/xattr.h>
45 #ifdef HAVE_SYS_SYSMACROS_H
46 #include <sys/sysmacros.h>
64 struct MOUNT_POINT_REPARSE_DATA
{ /* reparse data for junctions */
65 le16 subst_name_offset
;
66 le16 subst_name_length
;
67 le16 print_name_offset
;
68 le16 print_name_length
;
69 char path_buffer
[0]; /* above data assume this is char array */
72 struct SYMLINK_REPARSE_DATA
{ /* reparse data for symlinks */
73 le16 subst_name_offset
;
74 le16 subst_name_length
;
75 le16 print_name_offset
;
76 le16 print_name_length
;
77 le32 flags
; /* 1 for full target, otherwise 0 */
78 char path_buffer
[0]; /* above data assume this is char array */
81 struct REPARSE_INDEX
{ /* index entry in $Extend/$Reparse */
82 INDEX_ENTRY_HEADER header
;
83 REPARSE_INDEX_KEY key
;
87 static 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('\\')
94 static 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('{'),
108 static ntfschar reparse_index_name
[] = { const_cpu_to_le16('$'),
109 const_cpu_to_le16('R') };
111 static const char mappingdir
[] = ".NTFS-3G/";
114 * Fix a file name with doubtful case in some directory index
115 * and return the name with the casing used in directory.
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.
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.
126 static u64
ntfs_fix_file_name(ntfs_inode
*dir_ni
, ntfschar
*uname
,
129 ntfs_volume
*vol
= dir_ni
->vol
;
130 ntfs_index_context
*icx
;
138 FILE_NAME_ATTR
*found
;
141 ntfschar file_name
[NTFS_MAX_NAME_LEN
+ 1];
144 mref
= (u64
)-1; /* default return (not found) */
145 icx
= ntfs_index_ctx_get(dir_ni
, NTFS_INDEX_I30
, 4);
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
]);
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)
158 if ((cpuchar
< vol
->upcase_len
)
159 && (le16_to_cpu(vol
->upcase
[cpuchar
]) < cpuchar
))
160 find
.attr
.file_name
[i
] = vol
->upcase
[cpuchar
];
162 find
.attr
.file_name
[i
] = uname
[i
];
165 lkup
= ntfs_index_lookup((char*)&find
, uname_len
, icx
);
169 * We generally only get the first matching candidate,
170 * so we still have to check whether this is a real match
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
);
178 found
= &entry
->key
.file_name
;
180 && ntfs_names_are_equal(find
.attr
.file_name
,
181 find
.attr
.file_name_length
,
182 found
->file_name
, found
->file_name_length
,
184 vol
->upcase
, vol
->upcase_len
))
189 * fix original name and return inode
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
];
197 for (i
=0; i
<found
->file_name_length
; i
++)
198 uname
[i
] = vol
->locase
[found
->file_name
[i
]];
202 ntfs_index_ctx_put(icx
);
208 * Search for a directory junction or a symbolic link
209 * along the target path, with target defined as a full absolute path
211 * Returns the path translated to a Linux path
212 * or NULL if the path is not valid
215 static char *search_absolute(ntfs_volume
*vol
, ntfschar
*path
,
216 int count
, BOOL isdir
)
224 target
= (char*)NULL
; /* default return */
225 ni
= ntfs_inode_open(vol
, (MFT_REF
)FILE_root
);
229 * Examine and translate the path, until we reach either
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
241 while (((start
+ len
) < count
)
242 && (path
[start
+ len
] != const_cpu_to_le16('\\')))
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) {
249 ni
= ntfs_inode_open(vol
, inum
);
252 path
[start
++] = const_cpu_to_le16('/');
255 && (ni
->mrec
->flags
& MFT_RECORD_IS_DIRECTORY
)
256 && !(ni
->flags
& FILE_ATTR_REPARSE_POINT
)
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) {
264 target
= (char*)NULL
;
268 ntfs_inode_close(ni
);
274 * Search for a symbolic link along the target path,
275 * with the target defined as a relative path
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.
281 * Returns the path translated to a Linux path
282 * or NULL if the path is not valid
285 static char *search_relative(ntfs_inode
*ni
, ntfschar
*path
, int count
)
287 char *target
= (char*)NULL
;
295 int max
= 32; /* safety */
300 curni
= ntfs_dir_parent_inode(ni
);
302 * Examine and translate the path, until we reach either
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
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('\\'))) {
316 path
[1] = const_cpu_to_le16('/');
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('\\'))) {
323 path
[2] = const_cpu_to_le16('/');
325 newni
= ntfs_dir_parent_inode(curni
);
327 ntfs_inode_close(curni
);
333 while (((pos
+ lth
) < count
)
334 && (path
[pos
+ lth
] != const_cpu_to_le16('\\')))
337 inum
= ntfs_fix_file_name(curni
,&path
[pos
],lth
);
342 && ntfs_inode_close(curni
))
343 || (inum
== (u64
)-1))
346 curni
= ntfs_inode_open(ni
->vol
, MREF(inum
));
350 if (curni
->flags
& FILE_ATTR_REPARSE_POINT
)
352 if (ok
&& ((pos
+ lth
) < count
)) {
353 path
[pos
+ lth
] = const_cpu_to_le16('/');
356 && ntfs_inode_close(curni
))
361 && (ni
->mrec
->flags
^ curni
->mrec
->flags
)
362 & MFT_RECORD_IS_DIRECTORY
)
364 if (ntfs_inode_close(curni
))
373 if (ok
&& (ntfs_ucstombs(path
, count
, &target
, 0) < 0)) {
374 free(target
); // needed ?
375 target
= (char*)NULL
;
381 * Check whether a drive letter has been defined in .NTFS-3G
383 * Returns 1 if found,
385 * -1 if there was an error (described by errno)
388 static int ntfs_drive_letter(ntfs_volume
*vol
, ntfschar letter
)
390 char defines
[NTFS_MAX_NAME_LEN
+ 5];
399 sz
= ntfs_ucstombs(&letter
, 1, &drive
, 0);
401 strcpy(defines
,mappingdir
);
402 if ((*drive
>= 'a') && (*drive
<= 'z'))
404 strcat(defines
,drive
);
407 ni
= ntfs_pathname_to_inode(vol
, NULL
, defines
);
408 if (ni
&& !ntfs_inode_close(ni
))
411 if (errno
== ENOENT
) {
413 /* avoid errno pollution */
423 * Do some sanity checks on reparse data
425 * The only general check is about the size (at least the tag must
427 * If the reparse data looks like a junction point or symbolic
428 * link, more checks can be done.
432 static BOOL
valid_reparse_data(ntfs_inode
*ni
,
433 const REPARSE_POINT
*reparse_attr
, size_t size
)
438 const struct MOUNT_POINT_REPARSE_DATA
*mount_point_data
;
439 const struct SYMLINK_REPARSE_DATA
*symlink_data
;
441 ok
= ni
&& reparse_attr
442 && (size
>= sizeof(REPARSE_POINT
))
443 && (((size_t)le16_to_cpu(reparse_attr
->reparse_data_length
)
444 + sizeof(REPARSE_POINT
)) == size
);
446 switch (reparse_attr
->reparse_tag
) {
447 case IO_REPARSE_TAG_MOUNT_POINT
:
448 mount_point_data
= (const struct MOUNT_POINT_REPARSE_DATA
*)
449 reparse_attr
->reparse_data
;
450 offs
= le16_to_cpu(mount_point_data
->subst_name_offset
);
451 lth
= le16_to_cpu(mount_point_data
->subst_name_length
);
452 /* consistency checks */
453 if (!(ni
->mrec
->flags
& MFT_RECORD_IS_DIRECTORY
)
454 || ((size_t)((sizeof(REPARSE_POINT
)
455 + sizeof(struct MOUNT_POINT_REPARSE_DATA
)
456 + offs
+ lth
)) > size
))
459 case IO_REPARSE_TAG_SYMLINK
:
460 symlink_data
= (const struct SYMLINK_REPARSE_DATA
*)
461 reparse_attr
->reparse_data
;
462 offs
= le16_to_cpu(symlink_data
->subst_name_offset
);
463 lth
= le16_to_cpu(symlink_data
->subst_name_length
);
464 if ((size_t)((sizeof(REPARSE_POINT
)
465 + sizeof(struct SYMLINK_REPARSE_DATA
)
466 + offs
+ lth
)) > size
)
479 * Check and translate the target of a junction point or
480 * a full absolute symbolic link.
482 * A full target definition begins with "\??\" or "\\?\"
484 * The fully defined target is redefined as a relative link,
485 * - either to the target if found on the same device.
486 * - or into the /.NTFS-3G directory for the user to define
487 * In the first situation, the target is translated to case-sensitive path.
489 * returns the target converted to a relative symlink
490 * or NULL if there were some problem, as described by errno
493 static char *ntfs_get_fulllink(ntfs_volume
*vol
, ntfschar
*junction
,
494 int count
, const char *mnt_point
, BOOL isdir
)
500 enum { DIR_JUNCTION
, VOL_JUNCTION
, NO_JUNCTION
} kind
;
502 target
= (char*)NULL
;
503 fulltarget
= (char*)NULL
;
505 * For a valid directory junction we want \??\x:\
506 * where \ is an individual char and x a non-null char
509 && !memcmp(junction
,dir_junction_head
,8)
511 && (junction
[5] == const_cpu_to_le16(':'))
512 && (junction
[6] == const_cpu_to_le16('\\')))
516 * For a valid volume junction we want \\?\Volume{
517 * and a final \ (where \ is an individual char)
520 && !memcmp(junction
,vol_junction_head
,22)
521 && (junction
[count
-1] == const_cpu_to_le16('\\')))
526 * Directory junction with an explicit path and
527 * no specific definition for the drive letter :
528 * try to interpret as a target on the same volume
530 if ((kind
== DIR_JUNCTION
)
533 && !ntfs_drive_letter(vol
, junction
[4])) {
534 target
= search_absolute(vol
,&junction
[7],count
- 7, isdir
);
536 fulltarget
= (char*)ntfs_malloc(strlen(mnt_point
)
537 + strlen(target
) + 2);
539 strcpy(fulltarget
,mnt_point
);
540 strcat(fulltarget
,"/");
541 strcat(fulltarget
,target
);
547 * Volume junctions or directory junctions with
548 * target not found on current volume :
549 * link to /.NTFS-3G/target which the user can
550 * define as a symbolic link to the real target
552 if (((kind
== DIR_JUNCTION
) && !fulltarget
)
553 || (kind
== VOL_JUNCTION
)) {
554 sz
= ntfs_ucstombs(&junction
[4],
555 (kind
== VOL_JUNCTION
? count
- 5 : count
- 4),
557 if ((sz
> 0) && target
) {
558 /* reverse slashes */
559 for (q
=target
; *q
; q
++)
562 /* force uppercase drive letter */
563 if ((target
[1] == ':')
564 && (target
[0] >= 'a')
565 && (target
[0] <= 'z'))
566 target
[0] += 'A' - 'a';
567 fulltarget
= (char*)ntfs_malloc(strlen(mnt_point
)
568 + sizeof(mappingdir
) + strlen(target
) + 1);
570 strcpy(fulltarget
,mnt_point
);
571 strcat(fulltarget
,"/");
572 strcat(fulltarget
,mappingdir
);
573 strcat(fulltarget
,target
);
583 * Check and translate the target of an absolute symbolic link.
585 * An absolute target definition begins with "\" or "x:\"
587 * The absolute target is redefined as a relative link,
588 * - either to the target if found on the same device.
589 * - or into the /.NTFS-3G directory for the user to define
590 * In the first situation, the target is translated to case-sensitive path.
592 * returns the target converted to a relative symlink
593 * or NULL if there were some problem, as described by errno
596 static char *ntfs_get_abslink(ntfs_volume
*vol
, ntfschar
*junction
,
597 int count
, const char *mnt_point
, BOOL isdir
)
603 enum { FULL_PATH
, ABS_PATH
, REJECTED_PATH
} kind
;
605 target
= (char*)NULL
;
606 fulltarget
= (char*)NULL
;
608 * For a full valid path we want x:\
609 * where \ is an individual char and x a non-null char
613 && (junction
[1] == const_cpu_to_le16(':'))
614 && (junction
[2] == const_cpu_to_le16('\\')))
618 * For an absolute path we want an initial \
621 && (junction
[0] == const_cpu_to_le16('\\')))
624 kind
= REJECTED_PATH
;
626 * Full path, with a drive letter and
627 * no specific definition for the drive letter :
628 * try to interpret as a target on the same volume.
629 * Do the same for an abs path with no drive letter.
631 if (((kind
== FULL_PATH
)
634 && !ntfs_drive_letter(vol
, junction
[0]))
635 || (kind
== ABS_PATH
)) {
636 if (kind
== ABS_PATH
)
637 target
= search_absolute(vol
, &junction
[1],
640 target
= search_absolute(vol
, &junction
[3],
643 fulltarget
= (char*)ntfs_malloc(strlen(mnt_point
)
644 + strlen(target
) + 2);
646 strcpy(fulltarget
,mnt_point
);
647 strcat(fulltarget
,"/");
648 strcat(fulltarget
,target
);
654 * full path with target not found on current volume :
655 * link to /.NTFS-3G/target which the user can
656 * define as a symbolic link to the real target
658 if ((kind
== FULL_PATH
) && !fulltarget
) {
659 sz
= ntfs_ucstombs(&junction
[0],
661 if ((sz
> 0) && target
) {
662 /* reverse slashes */
663 for (q
=target
; *q
; q
++)
666 /* force uppercase drive letter */
667 if ((target
[1] == ':')
668 && (target
[0] >= 'a')
669 && (target
[0] <= 'z'))
670 target
[0] += 'A' - 'a';
671 fulltarget
= (char*)ntfs_malloc(strlen(mnt_point
)
672 + sizeof(mappingdir
) + strlen(target
) + 1);
674 strcpy(fulltarget
,mnt_point
);
675 strcat(fulltarget
,"/");
676 strcat(fulltarget
,mappingdir
);
677 strcat(fulltarget
,target
);
687 * Check and translate the target of a relative symbolic link.
689 * A relative target definition does not begin with "\"
691 * The original definition of relative target is kept, it is just
692 * translated to a case-sensitive path.
694 * returns the target converted to a relative symlink
695 * or NULL if there were some problem, as described by errno
698 static char *ntfs_get_rellink(ntfs_inode
*ni
, ntfschar
*junction
, int count
)
702 target
= search_relative(ni
,junction
,count
);
707 * Get the target for a junction point or symbolic link
708 * Should only be called for files or directories with reparse data
710 * returns the target converted to a relative path, or NULL
711 * if some error occurred, as described by errno
712 * errno is EOPNOTSUPP if the reparse point is not a valid
713 * symbolic link or directory junction
716 char *ntfs_make_symlink(ntfs_inode
*ni
, const char *mnt_point
,
724 REPARSE_POINT
*reparse_attr
;
725 struct MOUNT_POINT_REPARSE_DATA
*mount_point_data
;
726 struct SYMLINK_REPARSE_DATA
*symlink_data
;
727 enum { FULL_TARGET
, ABS_TARGET
, REL_TARGET
} kind
;
732 target
= (char*)NULL
;
734 isdir
= (ni
->mrec
->flags
& MFT_RECORD_IS_DIRECTORY
)
735 != const_cpu_to_le16(0);
737 reparse_attr
= (REPARSE_POINT
*)ntfs_attr_readall(ni
,
738 AT_REPARSE_POINT
,(ntfschar
*)NULL
, 0, &attr_size
);
739 if (reparse_attr
&& attr_size
740 && valid_reparse_data(ni
, reparse_attr
, attr_size
)) {
741 switch (reparse_attr
->reparse_tag
) {
742 case IO_REPARSE_TAG_MOUNT_POINT
:
743 mount_point_data
= (struct MOUNT_POINT_REPARSE_DATA
*)
744 reparse_attr
->reparse_data
;
745 offs
= le16_to_cpu(mount_point_data
->subst_name_offset
);
746 lth
= le16_to_cpu(mount_point_data
->subst_name_length
);
747 /* reparse data consistency has been checked */
748 target
= ntfs_get_fulllink(vol
,
749 (ntfschar
*)&mount_point_data
->path_buffer
[offs
],
750 lth
/2, mnt_point
, isdir
);
754 case IO_REPARSE_TAG_SYMLINK
:
755 symlink_data
= (struct SYMLINK_REPARSE_DATA
*)
756 reparse_attr
->reparse_data
;
757 offs
= le16_to_cpu(symlink_data
->subst_name_offset
);
758 lth
= le16_to_cpu(symlink_data
->subst_name_length
);
759 p
= (ntfschar
*)&symlink_data
->path_buffer
[offs
];
761 * Predetermine the kind of target,
762 * the called function has to make a full check
764 if (*p
++ == const_cpu_to_le16('\\')) {
765 if ((*p
== const_cpu_to_le16('?'))
766 || (*p
== const_cpu_to_le16('\\')))
771 if (*p
== const_cpu_to_le16(':'))
776 /* reparse data consistency has been checked */
779 if (!(symlink_data
->flags
780 & const_cpu_to_le32(1))) {
781 target
= ntfs_get_fulllink(vol
,
789 if (symlink_data
->flags
790 & const_cpu_to_le32(1)) {
791 target
= ntfs_get_abslink(vol
,
799 if (symlink_data
->flags
800 & const_cpu_to_le32(1)) {
801 target
= ntfs_get_rellink(ni
,
812 *pattr_size
= attr_size
;
819 * Check whether a reparse point looks like a junction point
820 * or a symbolic link.
821 * Should only be called for files or directories with reparse data
823 * The validity of the target is not checked.
826 BOOL
ntfs_possible_symlink(ntfs_inode
*ni
)
829 REPARSE_POINT
*reparse_attr
;
833 reparse_attr
= (REPARSE_POINT
*)ntfs_attr_readall(ni
,
834 AT_REPARSE_POINT
,(ntfschar
*)NULL
, 0, &attr_size
);
835 if (reparse_attr
&& attr_size
) {
836 switch (reparse_attr
->reparse_tag
) {
837 case IO_REPARSE_TAG_MOUNT_POINT
:
838 case IO_REPARSE_TAG_SYMLINK
:
847 #ifdef HAVE_SETXATTR /* extended attributes interface required */
850 * Set the index for new reparse data
852 * Returns 0 if success
853 * -1 if failure, explained by errno
856 static int set_reparse_index(ntfs_inode
*ni
, ntfs_index_context
*xr
,
859 struct REPARSE_INDEX indx
;
864 seqn
= ni
->mrec
->sequence_number
;
865 file_id_cpu
= MK_MREF(ni
->mft_no
,le16_to_cpu(seqn
));
866 file_id
= cpu_to_le64(file_id_cpu
);
867 indx
.header
.data_offset
= const_cpu_to_le16(
868 sizeof(INDEX_ENTRY_HEADER
)
869 + sizeof(REPARSE_INDEX_KEY
));
870 indx
.header
.data_length
= const_cpu_to_le16(0);
871 indx
.header
.reservedV
= const_cpu_to_le32(0);
872 indx
.header
.length
= const_cpu_to_le16(
873 sizeof(struct REPARSE_INDEX
));
874 indx
.header
.key_length
= const_cpu_to_le16(
875 sizeof(REPARSE_INDEX_KEY
));
876 indx
.header
.flags
= const_cpu_to_le16(0);
877 indx
.header
.reserved
= const_cpu_to_le16(0);
878 indx
.key
.reparse_tag
= reparse_tag
;
879 /* danger on processors which require proper alignment ! */
880 memcpy(&indx
.key
.file_id
, &file_id
, 8);
881 indx
.filling
= const_cpu_to_le32(0);
882 ntfs_index_ctx_reinit(xr
);
883 return (ntfs_ie_add(xr
,(INDEX_ENTRY
*)&indx
));
886 #endif /* HAVE_SETXATTR */
889 * Remove a reparse data index entry if attribute present
891 * Returns the size of existing reparse data
892 * (the existing reparse tag is returned)
893 * -1 if failure, explained by errno
896 static int remove_reparse_index(ntfs_attr
*na
, ntfs_index_context
*xr
,
899 REPARSE_INDEX_KEY key
;
908 /* read the existing reparse_tag */
909 size
= ntfs_attr_pread(na
, 0, 4, preparse_tag
);
911 seqn
= na
->ni
->mrec
->sequence_number
;
912 file_id_cpu
= MK_MREF(na
->ni
->mft_no
,le16_to_cpu(seqn
));
913 file_id
= cpu_to_le64(file_id_cpu
);
914 key
.reparse_tag
= *preparse_tag
;
915 /* danger on processors which require proper alignment ! */
916 memcpy(&key
.file_id
, &file_id
, 8);
917 if (!ntfs_index_lookup(&key
, sizeof(REPARSE_INDEX_KEY
), xr
)
918 && ntfs_index_rm(xr
))
929 * Open the $Extend/$Reparse file and its index
931 * Return the index context if opened
932 * or NULL if an error occurred (errno tells why)
934 * The index has to be freed and inode closed when not needed any more.
937 static ntfs_index_context
*open_reparse_index(ntfs_volume
*vol
)
942 ntfs_index_context
*xr
;
944 /* do not use path_name_to inode - could reopen root */
945 dir_ni
= ntfs_inode_open(vol
, FILE_Extend
);
946 ni
= (ntfs_inode
*)NULL
;
948 inum
= ntfs_inode_lookup_by_mbsname(dir_ni
,"$Reparse");
950 ni
= ntfs_inode_open(vol
, inum
);
951 ntfs_inode_close(dir_ni
);
954 xr
= ntfs_index_ctx_get(ni
, reparse_index_name
, 2);
956 ntfs_inode_close(ni
);
959 xr
= (ntfs_index_context
*)NULL
;
963 #ifdef HAVE_SETXATTR /* extended attributes interface required */
966 * Update the reparse data and index
968 * The reparse data attribute should have been created, and
969 * an existing index is expected if there is an existing value.
971 * Returns 0 if success
972 * -1 if failure, explained by errno
973 * If could not remove the existing index, nothing is done,
974 * If could not write the new data, no index entry is inserted
975 * If failed to insert the index, data is removed
978 static int update_reparse_data(ntfs_inode
*ni
, ntfs_index_context
*xr
,
979 const char *value
, size_t size
)
988 na
= ntfs_attr_open(ni
, AT_REPARSE_POINT
, AT_UNNAMED
, 0);
990 /* remove the existing reparse data */
991 oldsize
= remove_reparse_index(na
,xr
,&reparse_tag
);
995 /* resize attribute */
996 res
= ntfs_attr_truncate(na
, (s64
)size
);
997 /* overwrite value if any */
999 written
= (int)ntfs_attr_pwrite(na
,
1000 (s64
)0, (s64
)size
, value
);
1001 if (written
!= (s64
)size
) {
1002 ntfs_log_error("Failed to update "
1009 && set_reparse_index(ni
,xr
,
1010 ((const REPARSE_POINT
*)value
)->reparse_tag
)
1013 * If cannot index, try to remove the reparse
1014 * data and log the error. There will be an
1015 * inconsistency if removal fails.
1018 ntfs_log_error("Failed to index reparse data."
1019 " Possible corruption.\n");
1022 ntfs_attr_close(na
);
1029 #endif /* HAVE_SETXATTR */
1032 * Delete a reparse index entry
1034 * Returns 0 if success
1035 * -1 if failure, explained by errno
1038 int ntfs_delete_reparse_index(ntfs_inode
*ni
)
1040 ntfs_index_context
*xr
;
1047 na
= ntfs_attr_open(ni
, AT_REPARSE_POINT
, AT_UNNAMED
, 0);
1050 * read the existing reparse data (the tag is enough)
1053 xr
= open_reparse_index(ni
->vol
);
1055 if (remove_reparse_index(na
,xr
,&reparse_tag
) < 0)
1058 ntfs_index_entry_mark_dirty(xr
);
1060 ntfs_index_ctx_put(xr
);
1061 ntfs_inode_close(xrni
);
1063 ntfs_attr_close(na
);
1068 #ifdef HAVE_SETXATTR /* extended attributes interface required */
1071 * Get the ntfs reparse data into an extended attribute
1073 * Returns the reparse data size
1074 * and the buffer is updated if it is long enough
1077 int ntfs_get_ntfs_reparse_data(ntfs_inode
*ni
, char *value
, size_t size
)
1079 REPARSE_POINT
*reparse_attr
;
1082 attr_size
= 0; /* default to no data and no error */
1084 if (ni
->flags
& FILE_ATTR_REPARSE_POINT
) {
1085 reparse_attr
= (REPARSE_POINT
*)ntfs_attr_readall(ni
,
1086 AT_REPARSE_POINT
,(ntfschar
*)NULL
, 0, &attr_size
);
1088 if (attr_size
<= (s64
)size
) {
1090 memcpy(value
,reparse_attr
,
1100 return (attr_size
? (int)attr_size
: -errno
);
1104 * Set the reparse data from an extended attribute
1106 * Warning : the new data is not checked
1108 * Returns 0, or -1 if there is a problem
1111 int ntfs_set_ntfs_reparse_data(ntfs_inode
*ni
,
1112 const char *value
, size_t size
, int flags
)
1117 ntfs_index_context
*xr
;
1120 if (ni
&& valid_reparse_data(ni
, (const REPARSE_POINT
*)value
, size
)) {
1121 xr
= open_reparse_index(ni
->vol
);
1123 if (!ntfs_attr_exist(ni
,AT_REPARSE_POINT
,
1125 if (!(flags
& XATTR_REPLACE
)) {
1127 * no reparse data attribute : add one,
1128 * apparently, this does not feed the new value in
1129 * Note : NTFS version must be >= 3
1131 if (ni
->vol
->major_ver
>= 3) {
1132 res
= ntfs_attr_add(ni
,
1134 AT_UNNAMED
,0,&dummy
,
1138 FILE_ATTR_REPARSE_POINT
;
1139 NInoFileNameSetDirty(ni
);
1151 if (flags
& XATTR_CREATE
) {
1157 /* update value and index */
1158 res
= update_reparse_data(ni
,xr
,value
,size
);
1161 ntfs_index_entry_mark_dirty(xr
);
1163 ntfs_index_ctx_put(xr
);
1164 ntfs_inode_close(xrni
);
1172 return (res
? -1 : 0);
1176 * Remove the reparse data
1178 * Returns 0, or -1 if there is a problem
1181 int ntfs_remove_ntfs_reparse_data(ntfs_inode
*ni
)
1187 ntfs_index_context
*xr
;
1193 * open and delete the reparse data
1195 na
= ntfs_attr_open(ni
, AT_REPARSE_POINT
,
1198 /* first remove index (reparse data needed) */
1199 xr
= open_reparse_index(ni
->vol
);
1201 if (remove_reparse_index(na
,xr
,
1202 &reparse_tag
) < 0) {
1205 /* now remove attribute */
1206 res
= ntfs_attr_rm(na
);
1209 ~FILE_ATTR_REPARSE_POINT
;
1210 NInoFileNameSetDirty(ni
);
1213 * If we could not remove the
1214 * attribute, try to restore the
1215 * index and log the error. There
1216 * will be an inconsistency if
1217 * the reindexing fails.
1219 set_reparse_index(ni
, xr
,
1222 "Failed to remove reparse data."
1223 " Possible corruption.\n");
1227 ntfs_index_entry_mark_dirty(xr
);
1229 ntfs_index_ctx_put(xr
);
1230 ntfs_inode_close(xrni
);
1233 ntfs_attr_close(na
);
1234 /* avoid errno pollution */
1235 if (errno
== ENOENT
)
1246 return (res
? -1 : 0);
1249 #endif /* HAVE_SETXATTR */