"Fossies" - the Fresh Open Source Software Archive

Member "ntfs-3g_ntfsprogs-2017.3.23/libntfs-3g/reparse.c" (23 Mar 2017, 33134 Bytes) of package /linux/misc/ntfs-3g_ntfsprogs-2017.3.23.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "reparse.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3g_ntfsprogs-2016.2.22_vs_3g_ntfsprogs-2017.3.23.

    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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 = ENODATA;
  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 
  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 
  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 
 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 
 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 = ENODATA;
 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 
 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 = ENODATA;
 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 
 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 = ENODATA;
 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 
 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 }