]> wimlib.net Git - wimlib/blob - src/symlink.c
Fix capturing non-directories in source list mode
[wimlib] / src / symlink.c
1 /*
2  * symlink.c
3  *
4  * Code to read and set symbolic links in WIM files.
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 /* UNIX version of getting and setting the data in reparse points */
33 #if !defined(__WIN32__)
34
35 #include <sys/stat.h>
36
37 #ifdef HAVE_ALLOCA_H
38 #  include <alloca.h>
39 #endif
40
41 /*
42  * Find the symlink target of a symbolic link or junction point in the WIM.
43  *
44  * See http://msdn.microsoft.com/en-us/library/cc232006(v=prot.10).aspx for a
45  * description of the format of the so-called "reparse point data buffers".
46  *
47  * But, in the WIM format, the first 8 bytes of the reparse point data buffer
48  * are omitted, presumably because we already know the reparse tag from the
49  * dentry, and we already know the reparse tag length from the lookup table
50  * entry resource length.
51  */
52 static ssize_t
53 get_symlink_name(const void *resource, size_t resource_len, char *buf,
54                  size_t buf_len, u32 reparse_tag)
55 {
56         const void *p = resource;
57         u16 substitute_name_offset;
58         u16 substitute_name_len;
59         u16 print_name_offset;
60         u16 print_name_len;
61         char *link_target;
62         char *translated_target;
63         size_t link_target_len;
64         ssize_t ret;
65         unsigned header_size;
66         bool translate_slashes;
67
68         if (resource_len < 12)
69                 return -EIO;
70         p = get_u16(p, &substitute_name_offset);
71         p = get_u16(p, &substitute_name_len);
72         p = get_u16(p, &print_name_offset);
73         p = get_u16(p, &print_name_len);
74
75         wimlib_assert(reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
76                       reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT);
77
78         if (reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT)
79                 header_size = 8;
80         else {
81                 header_size = 12;
82                 p += 4;
83         }
84         if (header_size +
85             substitute_name_offset + substitute_name_len > resource_len)
86                 return -EIO;
87
88         ret = utf16le_to_tstr((const utf16lechar*)(p + substitute_name_offset),
89                               substitute_name_len,
90                               &link_target, &link_target_len);
91         if (ret)
92                 return -errno;
93
94         DEBUG("Interpeting substitute name \"%s\" (ReparseTag=0x%x)",
95               link_target, reparse_tag);
96         translate_slashes = true;
97         translated_target = link_target;
98         if (link_target_len >= 7 &&
99             translated_target[0] == '\\' &&
100             translated_target[1] == '?' &&
101             translated_target[2] == '?' &&
102             translated_target[3] == '\\' &&
103             translated_target[4] != '\0' &&
104             translated_target[5] == ':' &&
105             translated_target[6] == '\\')
106         {
107                 /* "Full" symlink or junction (\??\x:\ prefixed path) */
108                 translated_target += 6;
109                 link_target_len -= 6;
110         } else if (reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT &&
111                    link_target_len >= 12 &&
112                    memcmp(translated_target, "\\\\?\\Volume{", 11) == 0 &&
113                    translated_target[link_target_len - 1] == '\\')
114         {
115                 /* Volume junction.  Can't really do anything with it. */
116                 translate_slashes = false;
117         } else if (reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK &&
118                    link_target_len >= 3 &&
119                    translated_target[0] != '\0' &&
120                    translated_target[1] == ':' &&
121                    translated_target[2] == '\\')
122         {
123                 /* "Absolute" symlink, with drive letter */
124                 translated_target += 2;
125                 link_target_len -= 2;
126         } else if (reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK &&
127                    link_target_len >= 1)
128         {
129                 if (translated_target[0] == '\\')
130                         /* "Absolute" symlink, without drive letter */
131                         ;
132                 else
133                         /* "Relative" symlink, without drive letter */
134                         ;
135         } else {
136                 ERROR("Invalid reparse point substitute name: \"%s\"", translated_target);
137                 ret = -EIO;
138                 goto out;
139         }
140
141         if (translate_slashes)
142                 for (size_t i = 0; i < link_target_len; i++)
143                         if (translated_target[i] == '\\')
144                                 translated_target[i] = '/';
145
146         if (link_target_len > buf_len) {
147                 link_target_len = buf_len;
148                 ret = -ENAMETOOLONG;
149         } else {
150                 ret = link_target_len;
151         }
152         memcpy(buf, translated_target, link_target_len);
153 out:
154         FREE(link_target);
155         return ret;
156 }
157
158 #define SYMBOLIC_LINK_RELATIVE 0x00000001
159
160 /* Given a UNIX symlink target, prepare the corresponding symbolic link reparse
161  * data buffer. */
162 static int
163 make_symlink_reparse_data_buf(const char *symlink_target,
164                               size_t *len_ret, void **buf_ret)
165 {
166         int ret;
167         utf16lechar *name_utf16le;
168         size_t name_utf16le_nbytes;
169         size_t substitute_name_nbytes;
170         size_t print_name_nbytes;
171         static const char abs_subst_name_prefix[12] = "\\\0?\0?\0\\\0C\0:\0";
172         static const char abs_print_name_prefix[4] = "C\0:\0";
173         u32 flags;
174         size_t len;
175         void *buf;
176         void *p;
177
178         ret = tstr_to_utf16le(symlink_target, strlen(symlink_target),
179                               &name_utf16le, &name_utf16le_nbytes);
180         if (ret)
181                 return ret;
182
183         for (size_t i = 0; i < name_utf16le_nbytes / 2; i++)
184                 if (name_utf16le[i] == cpu_to_le16('/'))
185                         name_utf16le[i] = cpu_to_le16('\\');
186
187         /* Compatability notes:
188          *
189          * On UNIX, an absolute symbolic link begins with '/'; everything else
190          * is a relative symbolic link.  (Quite simple compared to the various
191          * ways to provide Windows paths.)
192          *
193          * To change a UNIX relative symbolic link to Windows format, we only
194          * need to translate it to UTF-16LE and replace backslashes with forward
195          * slashes.  We do not make any attempt to handle filename character
196          * problems, such as a link target that itself contains backslashes on
197          * UNIX.  Then, for these relative links, we set the reparse header
198          * @flags field to SYMBOLIC_LINK_RELATIVE.
199          *
200          * For UNIX absolute symbolic links, we must set the @flags field to 0.
201          * Then, there are multiple options as to actually represent the
202          * absolute link targets:
203          *
204          * (1) An absolute path beginning with one backslash character. similar
205          * to UNIX-style, just with a different path separator.  Print name same
206          * as substitute name.
207          *
208          * (2) Absolute path beginning with drive letter followed by a
209          * backslash.  Print name same as substitute name.
210          *
211          * (3) Absolute path beginning with drive letter followed by a
212          * backslash; substitute name prefixed with \??\, otherwise same as
213          * print name.
214          *
215          * We choose option (3) here, and we just assume C: for the drive
216          * letter.  The reasoning for this is:
217          *
218          * (1) Microsoft imagex.exe has a bug where it does not attempt to do
219          * reparse point fixups for these links, even though they are valid
220          * absolute links.  (Note: in this case prefixing the substitute name
221          * with \??\ does not work; it just makes the data unable to be restored
222          * at all.)
223          * (2) Microsoft imagex.exe will fail when doing reparse point fixups
224          * for these.  It apparently contains a bug that causes it to create an
225          * invalid reparse point, which then cannot be restored.
226          * (3) This is the only option I tested for which reparse point fixups
227          * worked properly in Microsoft imagex.exe.
228          *
229          * So option (3) it is.
230          */
231
232         substitute_name_nbytes = name_utf16le_nbytes;
233         print_name_nbytes = name_utf16le_nbytes;
234         if (symlink_target[0] == '/') {
235                 substitute_name_nbytes += sizeof(abs_subst_name_prefix);
236                 print_name_nbytes += sizeof(abs_print_name_prefix);
237         }
238
239         len = 12 + substitute_name_nbytes + print_name_nbytes +
240                         2 * sizeof(utf16lechar);
241         buf = MALLOC(len);
242
243         if (!buf) {
244                 ret = WIMLIB_ERR_NOMEM;
245                 goto out_free_name_utf16le;
246         }
247
248         p = buf;
249
250         /* Substitute name offset */
251         p = put_u16(p, 0);
252
253         /* Substitute name length */
254         p = put_u16(p, substitute_name_nbytes);
255
256         /* Print name offset */
257         p = put_u16(p, substitute_name_nbytes + sizeof(utf16lechar));
258
259         /* Print name length */
260         p = put_u16(p, print_name_nbytes);
261
262         /* Flags */
263         flags = 0;
264         if (symlink_target[0] != '/')
265                 flags |= SYMBOLIC_LINK_RELATIVE;
266         p = put_u32(p, flags);
267
268         /* Substitute name */
269         if (symlink_target[0] == '/')
270                 p = put_bytes(p, sizeof(abs_subst_name_prefix), abs_subst_name_prefix);
271         p = put_bytes(p, name_utf16le_nbytes, name_utf16le);
272         p = put_u16(p, 0);
273
274         /* Print name */
275         if (symlink_target[0] == '/')
276                 p = put_bytes(p, sizeof(abs_print_name_prefix), abs_print_name_prefix);
277         p = put_bytes(p, name_utf16le_nbytes, name_utf16le);
278         p = put_u16(p, 0);
279
280         *len_ret = len;
281         *buf_ret = buf;
282         ret = 0;
283 out_free_name_utf16le:
284         FREE(name_utf16le);
285         return ret;
286 }
287
288 /* Get the symlink target from a WIM inode.
289  *
290  * The inode may be either a "real" symlink (reparse tag
291  * WIM_IO_REPARSE_TAG_SYMLINK), or it may be a junction point (reparse tag
292  * WIM_IO_REPARSE_TAG_MOUNT_POINT).
293  */
294 ssize_t
295 inode_readlink(const struct wim_inode *inode, char *buf, size_t buf_len,
296                const WIMStruct *w, bool threadsafe)
297 {
298         const struct wim_lookup_table_entry *lte;
299         int ret;
300         u8 *res_buf;
301
302         wimlib_assert(inode_is_symlink(inode));
303
304         lte = inode_unnamed_lte(inode, w->lookup_table);
305         if (!lte)
306                 return -EIO;
307
308         if (wim_resource_size(lte) > REPARSE_POINT_MAX_SIZE)
309                 return -EIO;
310
311         res_buf = alloca(wim_resource_size(lte));
312         ret = read_full_resource_into_buf(lte, res_buf, threadsafe);
313         if (ret)
314                 return -EIO;
315         return get_symlink_name(res_buf, wim_resource_size(lte),
316                                 buf, buf_len, inode->i_reparse_tag);
317 }
318
319 /*
320  * Sets @inode to be a symbolic link pointing to @target.
321  *
322  * A lookup table entry for the symbolic link data buffer is created and
323  * inserted into @lookup_table, unless there is an existing lookup table entry
324  * for the exact same data, in which its reference count is incremented.
325  *
326  * The lookup table entry is returned in @lte_ret.
327  *
328  * On failure @dentry and @lookup_table are not modified.
329  */
330 int
331 inode_set_symlink(struct wim_inode *inode,
332                   const char *target,
333                   struct wim_lookup_table *lookup_table,
334                   struct wim_lookup_table_entry **lte_ret)
335
336 {
337         int ret;
338         size_t symlink_buf_len;
339         struct wim_lookup_table_entry *lte = NULL, *existing_lte;
340         u8 symlink_buf_hash[SHA1_HASH_SIZE];
341         void *symlink_buf;
342
343         ret = make_symlink_reparse_data_buf(target, &symlink_buf_len,
344                                             &symlink_buf);
345         if (ret)
346                 return ret;
347
348         DEBUG("Made symlink reparse data buf (len = %zu, name len = %zu)",
349                         symlink_buf_len, symlink_buf_len);
350
351         sha1_buffer(symlink_buf, symlink_buf_len, symlink_buf_hash);
352
353         existing_lte = __lookup_resource(lookup_table, symlink_buf_hash);
354
355         if (existing_lte) {
356                 lte = existing_lte;
357                 FREE(symlink_buf);
358                 symlink_buf = NULL;
359         } else {
360                 DEBUG("Creating new lookup table entry for symlink buf");
361                 lte = new_lookup_table_entry();
362                 if (!lte) {
363                         ret = WIMLIB_ERR_NOMEM;
364                         goto out_free_symlink_buf;
365                 }
366                 lte->resource_location            = RESOURCE_IN_ATTACHED_BUFFER;
367                 lte->attached_buffer              = symlink_buf;
368                 lte->resource_entry.original_size = symlink_buf_len;
369                 copy_hash(lte->hash, symlink_buf_hash);
370         }
371
372         inode->i_lte = lte;
373         inode->i_resolved = 1;
374
375         DEBUG("Loaded symlink buf");
376
377         if (existing_lte)
378                 lte->refcnt++;
379         else
380                 lookup_table_insert(lookup_table, lte);
381         if (lte_ret)
382                 *lte_ret = lte;
383         return 0;
384 out_free_symlink_buf:
385         FREE(symlink_buf);
386         return ret;
387 }
388
389 static int
390 unix_get_ino_and_dev(const char *path, u64 *ino_ret, u64 *dev_ret)
391 {
392         struct stat stbuf;
393         if (stat(path, &stbuf)) {
394                 WARNING_WITH_ERRNO("Failed to stat \"%s\"", path);
395                 /* Treat as a link pointing outside the capture root (it
396                  * most likely is). */
397                 return WIMLIB_ERR_STAT;
398         } else {
399                 *ino_ret = stbuf.st_ino;
400                 *dev_ret = stbuf.st_dev;
401                 return 0;
402         }
403 }
404
405 #endif /* !defined(__WIN32__) */
406
407 #ifdef __WIN32__
408 #  include "win32.h"
409 #  define RP_PATH_SEPARATOR L'\\'
410 #  define os_get_ino_and_dev win32_get_file_and_vol_ids
411 #else
412 #  define RP_PATH_SEPARATOR '/'
413 #  define os_get_ino_and_dev unix_get_ino_and_dev
414 #endif
415
416 /* Fix up absolute symbolic link targets--- mostly shared between UNIX and
417  * Windows */
418 tchar *
419 fixup_symlink(tchar *dest, u64 capture_root_ino, u64 capture_root_dev)
420 {
421         tchar *p = dest;
422
423 #ifdef __WIN32__
424         /* Skip over drive letter */
425         if (*p != RP_PATH_SEPARATOR)
426                 p += 2;
427 #endif
428
429         DEBUG("Fixing symlink or junction \"%"TS"\"", dest);
430         for (;;) {
431                 tchar save;
432                 int ret;
433                 u64 ino;
434                 u64 dev;
435
436                 while (*p == RP_PATH_SEPARATOR)
437                         p++;
438
439                 save = *p;
440                 *p = T('\0');
441                 ret = os_get_ino_and_dev(dest, &ino, &dev);
442                 *p = save;
443
444                 if (ret) /* stat() failed before we got to the capture root---
445                             assume the link points outside it. */
446                         return NULL;
447
448                 if (ino == capture_root_ino && dev == capture_root_dev) {
449                         /* Link points inside capture root.  Return abbreviated
450                          * path. */
451                         if (*p == T('\0'))
452                                 *(p - 1) = RP_PATH_SEPARATOR;
453                         while (p - 1 >= dest && *(p - 1) == RP_PATH_SEPARATOR)
454                                 p--;
455                 #ifdef __WIN32__
456                         /* Add back drive letter */
457                         if (*dest != RP_PATH_SEPARATOR) {
458                                 *--p = *(dest + 1);
459                                 *--p = *dest;
460                         }
461                 #endif
462                         wimlib_assert(p >= dest);
463                         return p;
464                 }
465
466                 if (*p == T('\0')) {
467                         /* Link points outside capture root. */
468                         return NULL;
469                 }
470
471                 do {
472                         p++;
473                 } while (*p != RP_PATH_SEPARATOR && *p != T('\0'));
474         }
475 }
476