Win32: Fix cross-drive capture rpfix
[wimlib] / src / reparse.c
1 /*
2  * reparse.c
3  *
4  * Handle reparse data.
5  */
6
7 /*
8  * Copyright (C) 2012, 2013 Eric Biggers
9  *
10  * This file is part of wimlib, a library for working with WIM files.
11  *
12  * wimlib is free software; you can redistribute it and/or modify it under the
13  * terms of the GNU General Public License as published by the Free
14  * Software Foundation; either version 3 of the License, or (at your option)
15  * any later version.
16  *
17  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
18  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
19  * A PARTICULAR PURPOSE. See the GNU General Public License for more
20  * details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with wimlib; if not, see http://www.gnu.org/licenses/.
24  */
25
26 #include "dentry.h"
27 #include "buffer_io.h"
28 #include "lookup_table.h"
29 #include "sha1.h"
30 #include <errno.h>
31
32 static const utf16lechar volume_junction_prefix[11] = {
33         cpu_to_le16('\\'),
34         cpu_to_le16('\\'),
35         cpu_to_le16('?'),
36         cpu_to_le16('\\'),
37         cpu_to_le16('V'),
38         cpu_to_le16('o'),
39         cpu_to_le16('l'),
40         cpu_to_le16('u'),
41         cpu_to_le16('m'),
42         cpu_to_le16('e'),
43         cpu_to_le16('{'),
44 };
45
46 /* Parse the "substitute name" (link target) from a symbolic link or junction
47  * reparse point.
48  *
49  * Return value is:
50  *
51  * Non-negative integer:
52  *      The name is an absolute symbolic link in one of several formats,
53  *      and the return value is the number of UTF-16LE characters that need to
54  *      be advanced to reach a simple "absolute" path starting with a backslash
55  *      (i.e. skip over \??\ and/or drive letter)
56  * Negative integer:
57  *      SUBST_NAME_IS_VOLUME_JUNCTION:
58  *              The name is a volume junction.
59  *      SUBST_NAME_IS_RELATIVE_LINK:
60  *              The name is a relative symbolic link.
61  *      SUBST_NAME_IS_UNKNOWN:
62  *              The name does not appear to be a valid symbolic link, junction,
63  *              or mount point.
64  */
65 int
66 parse_substitute_name(const utf16lechar *substitute_name,
67                       u16 substitute_name_nbytes, u32 rptag)
68 {
69         u16 substitute_name_nchars = substitute_name_nbytes / 2;
70
71         if (substitute_name_nchars >= 7 &&
72             substitute_name[0] == cpu_to_le16('\\') &&
73             substitute_name[1] == cpu_to_le16('?') &&
74             substitute_name[2] == cpu_to_le16('?') &&
75             substitute_name[3] == cpu_to_le16('\\') &&
76             substitute_name[4] != cpu_to_le16('\0') &&
77             substitute_name[5] == cpu_to_le16(':') &&
78             substitute_name[6] == cpu_to_le16('\\'))
79         {
80                 /* "Full" symlink or junction (\??\x:\ prefixed path) */
81                 return 6;
82         } else if (rptag == WIM_IO_REPARSE_TAG_MOUNT_POINT &&
83                    substitute_name_nchars >= 12 &&
84                    memcmp(substitute_name, volume_junction_prefix,
85                           sizeof(volume_junction_prefix)) == 0 &&
86                    substitute_name[substitute_name_nchars - 1] == cpu_to_le16('\\'))
87         {
88                 /* Volume junction.  Can't really do anything with it. */
89                 return SUBST_NAME_IS_VOLUME_JUNCTION;
90         } else if (rptag == WIM_IO_REPARSE_TAG_SYMLINK &&
91                    substitute_name_nchars >= 3 &&
92                    substitute_name[0] != cpu_to_le16('\0') &&
93                    substitute_name[1] == cpu_to_le16(':') &&
94                    substitute_name[2] == cpu_to_le16('\\'))
95         {
96                 /* "Absolute" symlink, with drive letter */
97                 return 2;
98         } else if (rptag == WIM_IO_REPARSE_TAG_SYMLINK &&
99                    substitute_name_nchars >= 1)
100         {
101                 if (substitute_name[0] == cpu_to_le16('\\'))
102                         /* "Absolute" symlink, without drive letter */
103                         return 0;
104                 else
105                         /* "Relative" symlink, without drive letter */
106                         return SUBST_NAME_IS_RELATIVE_LINK;
107         } else {
108                 return SUBST_NAME_IS_UNKNOWN;
109         }
110 }
111
112 /*
113  * Read the data from a symbolic link, junction, or mount point reparse point
114  * buffer into a `struct reparse_data'.
115  *
116  * See http://msdn.microsoft.com/en-us/library/cc232006(v=prot.10).aspx for a
117  * description of the format of the reparse point buffers.
118  */
119 int
120 parse_reparse_data(const u8 *rpbuf, u16 rpbuflen, struct reparse_data *rpdata)
121 {
122         const u8 *p = rpbuf;
123         u16 substitute_name_offset;
124         u16 print_name_offset;
125
126         memset(rpdata, 0, sizeof(*rpdata));
127         if (rpbuflen < 16)
128                 goto out_invalid;
129         p = get_u32(p, &rpdata->rptag);
130         wimlib_assert(rpdata->rptag == WIM_IO_REPARSE_TAG_SYMLINK ||
131                       rpdata->rptag == WIM_IO_REPARSE_TAG_MOUNT_POINT);
132         p = get_u16(p, &rpdata->rpdatalen);
133         p = get_u16(p, &rpdata->rpreserved);
134         p = get_u16(p, &substitute_name_offset);
135         p = get_u16(p, &rpdata->substitute_name_nbytes);
136         p = get_u16(p, &print_name_offset);
137         p = get_u16(p, &rpdata->print_name_nbytes);
138         if (rpdata->rptag == WIM_IO_REPARSE_TAG_SYMLINK) {
139                 if (rpbuflen < 20)
140                         goto out_invalid;
141                 p = get_u32(p, &rpdata->rpflags);
142         }
143         if ((size_t)substitute_name_offset + rpdata->substitute_name_nbytes +
144             (p - rpbuf) > rpbuflen)
145                 goto out_invalid;
146         if ((size_t)print_name_offset + rpdata->print_name_nbytes +
147             (p - rpbuf) > rpbuflen)
148                 goto out_invalid;
149         rpdata->substitute_name = (utf16lechar*)&p[substitute_name_offset];
150         rpdata->print_name = (utf16lechar*)&p[print_name_offset];
151         return 0;
152 out_invalid:
153         ERROR("Invalid reparse data");
154         return WIMLIB_ERR_INVALID_REPARSE_DATA;
155 }
156
157 /*
158  * Create a reparse point data buffer.
159  *
160  * @rpdata:  Structure that contains the data we need.
161  *
162  * @rpbuf:     Buffer into which to write the reparse point data buffer.  Must be
163  *              at least REPARSE_POINT_MAX_SIZE bytes long.
164  */
165 int
166 make_reparse_buffer(const struct reparse_data *rpdata, u8 *rpbuf)
167 {
168         u8 *p = rpbuf;
169
170         p = put_u32(p, rpdata->rptag);
171         p += 2; /* We set ReparseDataLength later */
172         p = put_u16(p, rpdata->rpreserved);
173         p = put_u16(p, 0); /* substitute name offset */
174         p = put_u16(p, rpdata->substitute_name_nbytes); /* substitute name nbytes */
175         p = put_u16(p, rpdata->substitute_name_nbytes + 2); /* print name offset */
176         p = put_u16(p, rpdata->print_name_nbytes); /* print name nbytes */
177         if (rpdata->rptag == WIM_IO_REPARSE_TAG_SYMLINK)
178                 p = put_u32(p, rpdata->rpflags);
179         /* We null-terminate the substitute and print names, although this may
180          * not be strictly necessary.  Note that the byte counts should not
181          * include the null terminators. */
182         if (p + rpdata->substitute_name_nbytes +
183             rpdata->print_name_nbytes +
184             2 * sizeof(utf16lechar) - rpbuf > REPARSE_POINT_MAX_SIZE)
185         {
186                 ERROR("Reparse data is too long!");
187                 return WIMLIB_ERR_INVALID_REPARSE_DATA;
188         }
189         p = put_bytes(p, rpdata->substitute_name_nbytes, rpdata->substitute_name);
190         p = put_u16(p, 0);
191         p = put_bytes(p, rpdata->print_name_nbytes, rpdata->print_name);
192         p = put_u16(p, 0);
193         put_u16(rpbuf + 4, p - rpbuf - 8); /* Set ReparseDataLength */
194         return 0;
195 }
196
197 /*
198  * Read the reparse data from a WIM inode that is a reparse point.
199  *
200  * @rpbuf points to a buffer at least REPARSE_POINT_MAX_SIZE bytes into which
201  * the reparse point data buffer will be reconstructed.
202  *
203  * Note: in the WIM format, the first 8 bytes of the reparse point data buffer
204  * are omitted, presumably because we already know the reparse tag from the
205  * dentry, and we already know the reparse tag length from the lookup table
206  * entry resource length.  However, we reconstruct the first 8 bytes in the
207  * buffer returned by this function.
208  */
209 int
210 wim_inode_get_reparse_data(const struct wim_inode *inode, u8 *rpbuf)
211 {
212         struct wim_lookup_table_entry *lte;
213         int ret;
214
215         wimlib_assert(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT);
216
217         lte = inode_unnamed_lte_resolved(inode);
218         if (!lte) {
219                 ERROR("Reparse point has no reparse data!");
220                 return WIMLIB_ERR_INVALID_REPARSE_DATA;
221         }
222         if (wim_resource_size(lte) > REPARSE_POINT_MAX_SIZE - 8) {
223                 ERROR("Reparse data is too long!");
224                 return WIMLIB_ERR_INVALID_REPARSE_DATA;
225         }
226
227         /* Read the data from the WIM file */
228         ret = read_full_resource_into_buf(lte, rpbuf + 8, true);
229         if (ret)
230                 return ret;
231
232         /* Reconstruct the first 8 bytes of the reparse point buffer */
233
234         /* ReparseTag */
235         put_u32(rpbuf, inode->i_reparse_tag);
236
237         /* ReparseDataLength */
238         put_u16(rpbuf + 4, wim_resource_size(lte));
239
240         /* ReparseReserved
241          * XXX this could be one of the unknown fields in the WIM dentry. */
242         put_u16(rpbuf + 6, 0);
243         return 0;
244 }
245
246 /* UNIX version of getting and setting the data in reparse points */
247 #if !defined(__WIN32__)
248
249 /* Get the UNIX symlink target from a WIM inode.  The inode may be either a
250  * "real" symlink (reparse tag WIM_IO_REPARSE_TAG_SYMLINK), or it may be a
251  * junction point (reparse tag WIM_IO_REPARSE_TAG_MOUNT_POINT).
252  *
253  * This has similar semantics to the UNIX readlink() function, except the path
254  * argument is swapped out with the `struct wim_inode' for a reparse point, and
255  * on failure a negated error code is returned rather than -1 with errno set.  */
256 ssize_t
257 wim_inode_readlink(const struct wim_inode *inode, char *buf, size_t bufsize)
258 {
259         int ret;
260         u8 rpbuf[REPARSE_POINT_MAX_SIZE];
261         u16 rpdatalen;
262         struct reparse_data rpdata;
263         char *link_target;
264         char *translated_target;
265         size_t link_target_len;
266
267         wimlib_assert(inode_is_symlink(inode));
268
269         if (wim_inode_get_reparse_data(inode, rpbuf))
270                 return -EIO;
271
272         get_u16(rpbuf + 4, &rpdatalen);
273
274         if (parse_reparse_data(rpbuf, rpdatalen + 8, &rpdata))
275                 return -EIO;
276
277         ret = utf16le_to_tstr(rpdata.substitute_name,
278                               rpdata.substitute_name_nbytes,
279                               &link_target, &link_target_len);
280         if (ret)
281                 return -errno;
282
283         translated_target = link_target;
284         ret = parse_substitute_name(rpdata.substitute_name,
285                                     rpdata.substitute_name_nbytes,
286                                     rpdata.rptag);
287         switch (ret) {
288         case SUBST_NAME_IS_RELATIVE_LINK:
289                 goto out_translate_slashes;
290         case SUBST_NAME_IS_VOLUME_JUNCTION:
291                 goto out_have_link;
292         case SUBST_NAME_IS_UNKNOWN:
293                 ERROR("Can't understand reparse point "
294                       "substitute name \"%s\"", link_target);
295                 return -EIO;
296         default:
297                 translated_target += ret;
298                 link_target_len -= ret;
299                 break;
300         }
301
302 out_translate_slashes:
303         for (size_t i = 0; i < link_target_len; i++)
304                 if (translated_target[i] == '\\')
305                         translated_target[i] = '/';
306 out_have_link:
307         if (link_target_len > bufsize) {
308                 link_target_len = bufsize;
309                 ret = -ENAMETOOLONG;
310         } else {
311                 ret = link_target_len;
312         }
313         memcpy(buf, translated_target, link_target_len);
314         FREE(link_target);
315         return ret;
316 }
317
318 #ifdef HAVE_ALLOCA_H
319 #  include <alloca.h>
320 #endif
321
322 int
323 wim_inode_set_symlink(struct wim_inode *inode,
324                       const char *target,
325                       struct wim_lookup_table *lookup_table)
326
327 {
328         u8 rpbuf[REPARSE_POINT_MAX_SIZE];
329         u16 rpdatalen;
330         struct reparse_data rpdata;
331         static const char abs_subst_name_prefix[12] = "\\\0?\0?\0\\\0C\0:\0";
332         static const char abs_print_name_prefix[4] = "C\0:\0";
333         utf16lechar *name_utf16le;
334         size_t name_utf16le_nbytes;
335         int ret;
336
337         DEBUG("Creating reparse point data buffer for UNIX "
338               "symlink target \"%s\"", target);
339         memset(&rpdata, 0, sizeof(rpdata));
340         ret = tstr_to_utf16le(target, strlen(target),
341                               &name_utf16le, &name_utf16le_nbytes);
342         if (ret)
343                 return ret;
344
345         for (size_t i = 0; i < name_utf16le_nbytes / 2; i++)
346                 if (name_utf16le[i] == cpu_to_le16('/'))
347                         name_utf16le[i] = cpu_to_le16('\\');
348
349         /* Compatability notes:
350          *
351          * On UNIX, an absolute symbolic link begins with '/'; everything else
352          * is a relative symbolic link.  (Quite simple compared to the various
353          * ways to provide Windows paths.)
354          *
355          * To change a UNIX relative symbolic link to Windows format, we only
356          * need to translate it to UTF-16LE and replace backslashes with forward
357          * slashes.  We do not make any attempt to handle filename character
358          * problems, such as a link target that itself contains backslashes on
359          * UNIX.  Then, for these relative links, we set the reparse header
360          * @flags field to SYMBOLIC_LINK_RELATIVE.
361          *
362          * For UNIX absolute symbolic links, we must set the @flags field to 0.
363          * Then, there are multiple options as to actually represent the
364          * absolute link targets:
365          *
366          * (1) An absolute path beginning with one backslash character. similar
367          * to UNIX-style, just with a different path separator.  Print name same
368          * as substitute name.
369          *
370          * (2) Absolute path beginning with drive letter followed by a
371          * backslash.  Print name same as substitute name.
372          *
373          * (3) Absolute path beginning with drive letter followed by a
374          * backslash; substitute name prefixed with \??\, otherwise same as
375          * print name.
376          *
377          * We choose option (3) here, and we just assume C: for the drive
378          * letter.  The reasoning for this is:
379          *
380          * (1) Microsoft imagex.exe has a bug where it does not attempt to do
381          * reparse point fixups for these links, even though they are valid
382          * absolute links.  (Note: in this case prefixing the substitute name
383          * with \??\ does not work; it just makes the data unable to be restored
384          * at all.)
385          * (2) Microsoft imagex.exe will fail when doing reparse point fixups
386          * for these.  It apparently contains a bug that causes it to create an
387          * invalid reparse point, which then cannot be restored.
388          * (3) This is the only option I tested for which reparse point fixups
389          * worked properly in Microsoft imagex.exe.
390          *
391          * So option (3) it is.
392          */
393
394         rpdata.rptag = inode->i_reparse_tag;
395         if (target[0] == '/') {
396                 rpdata.substitute_name_nbytes = name_utf16le_nbytes +
397                                                 sizeof(abs_subst_name_prefix);
398                 rpdata.print_name_nbytes = name_utf16le_nbytes +
399                                            sizeof(abs_print_name_prefix);
400                 rpdata.substitute_name = alloca(rpdata.substitute_name_nbytes);
401                 rpdata.print_name = alloca(rpdata.print_name_nbytes);
402                 memcpy(rpdata.substitute_name, abs_subst_name_prefix,
403                        sizeof(abs_subst_name_prefix));
404                 memcpy(rpdata.print_name, abs_print_name_prefix,
405                        sizeof(abs_print_name_prefix));
406                 memcpy((void*)rpdata.substitute_name + sizeof(abs_subst_name_prefix),
407                        name_utf16le, name_utf16le_nbytes);
408                 memcpy((void*)rpdata.print_name + sizeof(abs_print_name_prefix),
409                        name_utf16le, name_utf16le_nbytes);
410         } else {
411                 rpdata.substitute_name_nbytes = name_utf16le_nbytes;
412                 rpdata.print_name_nbytes = name_utf16le_nbytes;
413                 rpdata.substitute_name = name_utf16le;
414                 rpdata.print_name = name_utf16le;
415                 rpdata.rpflags = SYMBOLIC_LINK_RELATIVE;
416         }
417
418         ret = make_reparse_buffer(&rpdata, rpbuf);
419         if (ret == 0) {
420                 get_u16(rpbuf + 4, &rpdatalen);
421                 ret = inode_set_unnamed_stream(inode, rpbuf + 8, rpdatalen,
422                                                lookup_table);
423         }
424         FREE(name_utf16le);
425         return ret;
426 }
427
428 #include <sys/stat.h>
429
430 static int
431 unix_get_ino_and_dev(const char *path, u64 *ino_ret, u64 *dev_ret)
432 {
433         struct stat stbuf;
434         if (stat(path, &stbuf)) {
435                 if (errno != ENOENT)
436                         WARNING_WITH_ERRNO("Failed to stat \"%s\"", path);
437                 /* Treat as a link pointing outside the capture root (it
438                  * most likely is). */
439                 return WIMLIB_ERR_STAT;
440         } else {
441                 *ino_ret = stbuf.st_ino;
442                 *dev_ret = stbuf.st_dev;
443                 return 0;
444         }
445 }
446
447 #endif /* !defined(__WIN32__) */
448
449 #ifdef __WIN32__
450 #  include "win32.h"
451 #  define RP_PATH_SEPARATOR L'\\'
452 #  define is_rp_path_separator(c) ((c) == L'\\' || (c) == L'/')
453 #  define os_get_ino_and_dev win32_get_file_and_vol_ids
454 #else
455 #  define RP_PATH_SEPARATOR '/'
456 #  define is_rp_path_separator(c) ((c) == '/')
457 #  define os_get_ino_and_dev unix_get_ino_and_dev
458 #endif
459
460 /* Fix up absolute symbolic link targets--- mostly shared between UNIX and
461  * Windows */
462 tchar *
463 capture_fixup_absolute_symlink(tchar *dest,
464                                u64 capture_root_ino, u64 capture_root_dev)
465 {
466         tchar *p = dest;
467
468 #ifdef __WIN32__
469         /* Skip drive letter */
470         if (!is_rp_path_separator(*dest))
471                 p += 2;
472 #endif
473
474         DEBUG("Fixing symlink or junction \"%"TS"\"", dest);
475         for (;;) {
476                 tchar save;
477                 int ret;
478                 u64 ino;
479                 u64 dev;
480
481                 while (is_rp_path_separator(*p))
482                         p++;
483
484                 save = *p;
485                 *p = T('\0');
486                 ret = os_get_ino_and_dev(dest, &ino, &dev);
487                 *p = save;
488
489                 if (ret) /* stat() failed before we got to the capture root---
490                             assume the link points outside it. */
491                         return NULL;
492
493                 if (ino == capture_root_ino && dev == capture_root_dev) {
494                         /* Link points inside capture root.  Return abbreviated
495                          * path. */
496                         if (*p == T('\0'))
497                                 *(p - 1) = RP_PATH_SEPARATOR;
498                         while (p - 1 >= dest && is_rp_path_separator(*(p - 1)))
499                                 p--;
500                 #ifdef __WIN32__
501                         if (!is_rp_path_separator(dest[0])) {
502                                 *--p = dest[1];
503                                 *--p = dest[0];
504                         }
505                 #endif
506                         wimlib_assert(p >= dest);
507                         return p;
508                 }
509
510                 if (*p == T('\0')) {
511                         /* Link points outside capture root. */
512                         return NULL;
513                 }
514
515                 do {
516                         p++;
517                 } while (!is_rp_path_separator(*p) && *p != T('\0'));
518         }
519 }