]> wimlib.net Git - wimlib/blob - src/ntfs-3g_apply.c
28589698b7e9e3a8cc082d15091b377c2f73052c
[wimlib] / src / ntfs-3g_apply.c
1 /*
2  * ntfs-3g_apply.c
3  *
4  * Apply a WIM image directly to a NTFS volume using libntfs-3g.  Restore as
5  * much information as possible, including security data, file attributes, DOS
6  * names, and alternate data streams.
7  */
8
9 /*
10  * Copyright (C) 2012, 2013 Eric Biggers
11  *
12  * This file is part of wimlib, a library for working with WIM files.
13  *
14  * wimlib is free software; you can redistribute it and/or modify it under the
15  * terms of the GNU General Public License as published by the Free
16  * Software Foundation; either version 3 of the License, or (at your option)
17  * any later version.
18  *
19  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
20  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
21  * A PARTICULAR PURPOSE. See the GNU General Public License for more
22  * details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with wimlib; if not, see http://www.gnu.org/licenses/.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 #  include "config.h"
30 #endif
31
32 #ifdef WITH_NTFS_3G
33
34 #include <errno.h>
35 #include <locale.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #ifdef HAVE_ALLOCA_H
39 #  include <alloca.h>
40 #endif
41
42 #include <ntfs-3g/attrib.h>
43 #include <ntfs-3g/reparse.h>
44 #include <ntfs-3g/security.h>
45
46 #include "wimlib/apply.h"
47 #include "wimlib/encoding.h"
48 #include "wimlib/error.h"
49 #include "wimlib/lookup_table.h"
50 #include "wimlib/ntfs_3g.h"
51 #include "wimlib/paths.h"
52 #include "wimlib/resource.h"
53
54 static ntfs_volume *
55 ntfs_3g_apply_ctx_get_volume(struct apply_ctx *ctx)
56 {
57         return (ntfs_volume*)ctx->private[0];
58 }
59
60 static void
61 ntfs_3g_apply_ctx_set_volume(struct apply_ctx *ctx, ntfs_volume *vol)
62 {
63         ctx->private[0] = (intptr_t)vol;
64 }
65
66 static ntfs_inode *
67 ntfs_3g_apply_pathname_to_inode(const char *path, struct apply_ctx *ctx)
68 {
69         ntfs_volume *vol = ntfs_3g_apply_ctx_get_volume(ctx);
70         return ntfs_pathname_to_inode(vol, NULL, path);
71 }
72
73 struct ntfs_attr_extract_ctx {
74         u64 offset;
75         ntfs_attr *na;
76 };
77
78 static int
79 ntfs_3g_extract_wim_chunk(const void *buf, size_t len, void *_ctx)
80 {
81         struct ntfs_attr_extract_ctx *ctx = _ctx;
82
83         if (ntfs_attr_pwrite(ctx->na, ctx->offset, len, buf) != len)
84                 return WIMLIB_ERR_WRITE;
85         ctx->offset += len;
86         return 0;
87 }
88
89 static ntfs_inode *
90 ntfs_3g_open_parent_inode(const char *path, ntfs_volume *vol)
91 {
92         char *p;
93         ntfs_inode *dir_ni;
94
95         p = strrchr(path, '/');
96         *p = '\0';
97         dir_ni = ntfs_pathname_to_inode(vol, NULL, path);
98         *p = '/';
99         return dir_ni;
100 }
101
102 static int
103 ntfs_3g_create(const char *path, struct apply_ctx *ctx, u64 *cookie_ret,
104                mode_t mode)
105 {
106         ntfs_volume *vol;
107         ntfs_inode *dir_ni, *ni;
108         const char *name;
109         utf16lechar *name_utf16le;
110         size_t name_utf16le_nbytes;
111         int ret;
112
113         vol = ntfs_3g_apply_ctx_get_volume(ctx);
114
115         ret = WIMLIB_ERR_OPEN;
116         dir_ni = ntfs_3g_open_parent_inode(path, vol);
117         if (!dir_ni)
118                 goto out;
119
120         name = path_basename(path);
121         ret = tstr_to_utf16le(name, strlen(name),
122                               &name_utf16le, &name_utf16le_nbytes);
123         if (ret)
124                 goto out_close_dir_ni;
125
126         ret = WIMLIB_ERR_OPEN;
127         ni = ntfs_create(dir_ni, 0, name_utf16le,
128                          name_utf16le_nbytes / 2, mode);
129         if (!ni)
130                 goto out_free_name_utf16le;
131         *cookie_ret = MK_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number));
132         if (ntfs_inode_close_in_dir(ni, dir_ni))
133                 goto out_free_name_utf16le;
134         ret = 0;
135 out_free_name_utf16le:
136         FREE(name_utf16le);
137 out_close_dir_ni:
138         if (ntfs_inode_close(dir_ni))
139                 ret = WIMLIB_ERR_WRITE;
140 out:
141         return ret;
142 }
143
144 static int
145 ntfs_3g_create_file(const char *path, struct apply_ctx *ctx,
146                     u64 *cookie_ret)
147 {
148         return ntfs_3g_create(path, ctx, cookie_ret, S_IFREG);
149 }
150
151 static int
152 ntfs_3g_create_directory(const char *path, struct apply_ctx *ctx,
153                          u64 *cookie_ret)
154 {
155         return ntfs_3g_create(path, ctx, cookie_ret, S_IFDIR);
156 }
157
158 static int
159 ntfs_3g_create_hardlink(const char *oldpath, const char *newpath,
160                         struct apply_ctx *ctx)
161 {
162         ntfs_volume *vol;
163         ntfs_inode *dir_ni, *ni;
164         const char *name;
165         utf16lechar *name_utf16le;
166         size_t name_utf16le_nbytes;
167         int ret;
168
169         vol = ntfs_3g_apply_ctx_get_volume(ctx);
170
171         ret = WIMLIB_ERR_OPEN;
172         ni = ntfs_pathname_to_inode(vol, NULL, oldpath);
173         if (!ni)
174                 goto out;
175
176         ret = WIMLIB_ERR_OPEN;
177         dir_ni = ntfs_3g_open_parent_inode(newpath, vol);
178         if (!dir_ni)
179                 goto out_close_ni;
180
181         name = path_basename(newpath);
182         ret = tstr_to_utf16le(name, strlen(name),
183                               &name_utf16le, &name_utf16le_nbytes);
184         if (ret)
185                 goto out_close_dir_ni;
186         ret = 0;
187         if (ntfs_link(ni, dir_ni, name_utf16le, name_utf16le_nbytes / 2))
188                 ret = WIMLIB_ERR_LINK;
189         FREE(name_utf16le);
190 out_close_dir_ni:
191         if (ntfs_inode_close(dir_ni))
192                 ret = WIMLIB_ERR_WRITE;
193 out_close_ni:
194         if (ntfs_inode_close(ni))
195                 ret = WIMLIB_ERR_WRITE;
196 out:
197         return ret;
198 }
199
200 /*
201  * Extract a stream (default or alternate data) to an attribute of a NTFS file.
202  */
203 static int
204 ntfs_3g_extract_stream(file_spec_t file, const utf16lechar *raw_stream_name,
205                        size_t stream_name_nchars,
206                        struct wim_lookup_table_entry *lte, struct apply_ctx *ctx)
207 {
208         ntfs_inode *ni;
209         ntfs_attr *na;
210         int ret;
211         struct ntfs_attr_extract_ctx extract_ctx;
212         utf16lechar *stream_name;
213
214         if (stream_name_nchars == 0) {
215                 stream_name = AT_UNNAMED;
216         } else {
217                 stream_name = alloca((stream_name_nchars + 1) * sizeof(utf16lechar));
218                 memcpy(stream_name, raw_stream_name,
219                        stream_name_nchars * sizeof(utf16lechar));
220                 stream_name[stream_name_nchars] = 0;
221         }
222
223         ret = 0;
224         if (!stream_name_nchars && !lte)
225                 goto out;
226
227         /* Open NTFS inode to which to extract the stream.  */
228         ret = WIMLIB_ERR_OPEN;
229         ni = ntfs_inode_open(ntfs_3g_apply_ctx_get_volume(ctx), file.cookie);
230         if (!ni)
231                 goto out;
232
233         /* Add the stream if it's not the default (unnamed) stream.  */
234         ret = WIMLIB_ERR_OPEN;
235         if (stream_name_nchars)
236                 if (ntfs_attr_add(ni, AT_DATA, stream_name,
237                                   stream_name_nchars, NULL, 0))
238                         goto out_close;
239
240         /* If stream is empty, no need to open and extract it.  */
241         ret = 0;
242         if (!lte)
243                 goto out_close;
244
245         /* Open the stream (NTFS attribute).  */
246         ret = WIMLIB_ERR_OPEN;
247         na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_nchars);
248         if (!na)
249                 goto out_close;
250
251         /* (Optional) Immediately resize attribute to size of stream.
252          *
253          * This dramatically speeds up extraction, as demonstrated with the
254          * following timing results:
255          *
256          * 18 mins. 27 sec. to apply Windows 7 image (with resize)
257          * 32 mins. 45 sec. to apply Windows 7 image (no resize)
258          *
259          * It probably would speed things up even more if we could get NTFS-3g
260          * to skip even more useless work (for example it fills resized
261          * attributes with 0's, then we just override it.)  */
262         ret = WIMLIB_ERR_WRITE;
263         if (ntfs_attr_truncate_solid(na, wim_resource_size(lte)))
264                 goto out_attr_close;
265
266         /* Extract stream data to the NTFS attribute.  */
267         extract_ctx.na = na;
268         extract_ctx.offset = 0;
269         ret = extract_wim_resource(lte, wim_resource_size(lte),
270                                    ntfs_3g_extract_wim_chunk, &extract_ctx);
271         /* Clean up and return.  */
272 out_attr_close:
273         ntfs_attr_close(na);
274 out_close:
275         if (ntfs_inode_close(ni))
276                 ret = WIMLIB_ERR_WRITE;
277 out:
278         if (ret && !errno)
279                 errno = -1;
280         return ret;
281 }
282
283 static int
284 ntfs_3g_extract_unnamed_stream(file_spec_t file,
285                                struct wim_lookup_table_entry *lte,
286                                struct apply_ctx *ctx)
287 {
288         return ntfs_3g_extract_stream(file, NULL, 0, lte, ctx);
289 }
290
291 static int
292 ntfs_3g_extract_named_stream(file_spec_t file, const utf16lechar *stream_name,
293                              size_t stream_name_nchars,
294                              struct wim_lookup_table_entry *lte, struct apply_ctx *ctx)
295 {
296         return ntfs_3g_extract_stream(file, stream_name,
297                                       stream_name_nchars, lte, ctx);
298 }
299
300 static int
301 ntfs_3g_set_file_attributes(const char *path, u32 attributes,
302                             struct apply_ctx *ctx, unsigned pass)
303 {
304         ntfs_inode *ni;
305         int ret = 0;
306
307         ni = ntfs_3g_apply_pathname_to_inode(path, ctx);
308         if (!ni)
309                 return WIMLIB_ERR_OPEN;
310         if (ntfs_set_ntfs_attrib(ni, (const char*)&attributes, sizeof(u32), 0))
311                 ret = WIMLIB_ERR_SET_ATTRIBUTES;
312         if (ntfs_inode_close(ni))
313                 ret = WIMLIB_ERR_WRITE;
314         return ret;
315 }
316
317 static int
318 ntfs_3g_set_reparse_data(const char *path, const u8 *rpbuf, u16 rpbuflen,
319                          struct apply_ctx *ctx)
320 {
321         ntfs_inode *ni;
322         int ret = 0;
323
324         ni = ntfs_3g_apply_pathname_to_inode(path, ctx);
325         if (!ni)
326                 return WIMLIB_ERR_OPEN;
327         if (ntfs_set_ntfs_reparse_data(ni, rpbuf, rpbuflen, 0))
328                 ret = WIMLIB_ERR_SET_REPARSE_DATA;
329         if (ntfs_inode_close(ni))
330                 ret = WIMLIB_ERR_WRITE;
331         return ret;
332 }
333
334 static int
335 ntfs_3g_set_short_name(const char *path, const utf16lechar *short_name,
336                        size_t short_name_nchars, struct apply_ctx *ctx)
337 {
338         ntfs_inode *ni, *dir_ni;
339         ntfs_volume *vol;
340         int ret;
341         char *dosname = NULL;
342         size_t dosname_nbytes;
343
344         ret = 0;
345         if (short_name_nchars == 0)
346                 goto out;
347
348         vol = ntfs_3g_apply_ctx_get_volume(ctx);
349
350         ret = WIMLIB_ERR_OPEN;
351         dir_ni = ntfs_3g_open_parent_inode(path, vol);
352         if (!dir_ni)
353                 goto out;
354
355         ret = WIMLIB_ERR_OPEN;
356         ni = ntfs_pathname_to_inode(vol, NULL, path);
357         if (!ni)
358                 goto out_close_dir_ni;
359
360         ret = utf16le_to_tstr(short_name, short_name_nchars * 2,
361                               &dosname, &dosname_nbytes);
362         if (ret)
363                 goto out_close_ni;
364
365         ret = 0;
366         if (ntfs_set_ntfs_dos_name(ni, dir_ni, dosname,
367                                    dosname_nbytes, 0))
368                 ret = WIMLIB_ERR_SET_SHORT_NAME;
369         /* ntfs_set_ntfs_dos_name() always closes the inodes.  */
370         FREE(dosname);
371         goto out;
372 out_close_ni:
373         if (ntfs_inode_close_in_dir(ni, dir_ni))
374                 ret = WIMLIB_ERR_WRITE;
375 out_close_dir_ni:
376         if (ntfs_inode_close(dir_ni))
377                 ret = WIMLIB_ERR_WRITE;
378 out:
379         return ret;
380 }
381
382 static int
383 ntfs_3g_set_security_descriptor(const char *path, const u8 *desc, size_t desc_size,
384                                 struct apply_ctx *ctx)
385 {
386         ntfs_volume *vol;
387         ntfs_inode *ni;
388         struct SECURITY_CONTEXT sec_ctx;
389         int ret = 0;
390
391         vol = ntfs_3g_apply_ctx_get_volume(ctx);
392
393         ni = ntfs_pathname_to_inode(vol, NULL, path);
394         if (!ni)
395                 return WIMLIB_ERR_OPEN;
396
397         memset(&sec_ctx, 0, sizeof(sec_ctx));
398         sec_ctx.vol = vol;
399
400         if (ntfs_set_ntfs_acl(&sec_ctx, ni, desc, desc_size, 0))
401                 ret = WIMLIB_ERR_SET_SECURITY;
402         if (ntfs_inode_close(ni))
403                 ret = WIMLIB_ERR_WRITE;
404         return ret;
405 }
406
407 static int
408 ntfs_3g_set_timestamps(const char *path, u64 creation_time,
409                        u64 last_write_time, u64 last_access_time,
410                        struct apply_ctx *ctx)
411 {
412         u64 ntfs_timestamps[3];
413         ntfs_inode *ni;
414         int ret = 0;
415
416         ni = ntfs_3g_apply_pathname_to_inode(path, ctx);
417         if (!ni)
418                 return WIMLIB_ERR_OPEN;
419
420         /* Note: ntfs_inode_set_times() expects the times in native byte order,
421          * not little endian. */
422         ntfs_timestamps[0] = creation_time;
423         ntfs_timestamps[1] = last_write_time;
424         ntfs_timestamps[2] = last_access_time;
425
426         if (ntfs_inode_set_times(ni, (const char*)ntfs_timestamps,
427                                  sizeof(ntfs_timestamps), 0))
428                 ret = WIMLIB_ERR_SET_TIMESTAMPS;
429         if (ntfs_inode_close(ni))
430                 ret = WIMLIB_ERR_WRITE;
431         return ret;
432 }
433
434 static bool
435 ntfs_3g_target_is_root(const char *target)
436 {
437         /* We always extract to the root of the NTFS volume.  */
438         return true;
439 }
440
441 static int
442 ntfs_3g_start_extract(const char *path, struct apply_ctx *ctx)
443 {
444         ntfs_volume *vol;
445
446         vol = ntfs_mount(ctx->target, 0);
447         if (!vol) {
448                 ERROR_WITH_ERRNO("Failed to mount \"%"TS"\" with NTFS-3g", ctx->target);
449                 return WIMLIB_ERR_OPEN;
450         }
451         ntfs_3g_apply_ctx_set_volume(ctx, vol);
452
453         ctx->supported_features.archive_files             = 1;
454         ctx->supported_features.hidden_files              = 1;
455         ctx->supported_features.system_files              = 1;
456         ctx->supported_features.compressed_files          = 1;
457         ctx->supported_features.encrypted_files           = 0;
458         ctx->supported_features.not_context_indexed_files = 1;
459         ctx->supported_features.sparse_files              = 1;
460         ctx->supported_features.named_data_streams        = 1;
461         ctx->supported_features.hard_links                = 1;
462         ctx->supported_features.reparse_points            = 1;
463         ctx->supported_features.security_descriptors      = 1;
464         ctx->supported_features.short_names               = 1;
465         return 0;
466 }
467
468 static int
469 ntfs_3g_finish_or_abort_extract(struct apply_ctx *ctx)
470 {
471         ntfs_volume *vol;
472
473         vol = ntfs_3g_apply_ctx_get_volume(ctx);
474         if (ntfs_umount(vol, FALSE)) {
475                 ERROR_WITH_ERRNO("Failed to unmount \"%"TS"\" with NTFS-3g",
476                                  ctx->target);
477                 return WIMLIB_ERR_WRITE;
478         }
479         return 0;
480 }
481
482 void
483 libntfs3g_global_init(void)
484 {
485         ntfs_set_char_encoding(setlocale(LC_ALL, ""));
486 }
487
488 const struct apply_operations ntfs_3g_apply_ops = {
489         .name = "NTFS-3g",
490
491         .target_is_root          = ntfs_3g_target_is_root,
492         .start_extract           = ntfs_3g_start_extract,
493         .create_file             = ntfs_3g_create_file,
494         .create_directory        = ntfs_3g_create_directory,
495         .create_hardlink         = ntfs_3g_create_hardlink,
496         .extract_unnamed_stream  = ntfs_3g_extract_unnamed_stream,
497         .extract_named_stream    = ntfs_3g_extract_named_stream,
498         .set_file_attributes     = ntfs_3g_set_file_attributes,
499         .set_reparse_data        = ntfs_3g_set_reparse_data,
500         .set_short_name          = ntfs_3g_set_short_name,
501         .set_security_descriptor = ntfs_3g_set_security_descriptor,
502         .set_timestamps          = ntfs_3g_set_timestamps,
503         .abort_extract           = ntfs_3g_finish_or_abort_extract,
504         .finish_extract          = ntfs_3g_finish_or_abort_extract,
505
506         .path_prefix = "/",
507         .path_prefix_nchars = 1,
508         .path_separator = '/',
509         .path_max = 32768,
510
511         /* By default, NTFS-3g creates names in the NTFS POSIX namespace, which
512          * is case-sensitive.  */
513         .supports_case_sensitive_filenames = 1,
514
515         /* The root directory of the NTFS volume should not be created
516          * explicitly.  */
517         .root_directory_is_special = 1,
518
519         /* NTFS-3g can open files by MFT reference.  */
520         .uses_cookies = 1,
521
522         /*
523          * With NTFS-3g, the extraction order of the names of a file that has a
524          * short name needs to be:
525          *
526          * 1. Create file using the long name that has an associated short name.
527          *    This long name is temporarily placed in the POSIX namespace.
528          * 2. Set the short name on the file.  This will either change the POSIX
529          *    name to Win32 and create a new DOS name, or replace the POSIX name
530          *    with a Win32+DOS name.
531          * 3. Create additional long names (links) of the file, which are placed
532          *    in the POSIX namespace.
533          *
534          * The reason for this is that two issues can come up when the
535          * extraction is done otherwise:
536          *
537          * - If a DOS name is set on a file in a directory with several long
538          *   names, it is ambiguous which long name to use (at least with the
539          *   exported ntfs_set_ntfs_dos_name() function).
540          * - NTFS-3g 2013.1.13 will no longer allow even setting the DOS name on
541          *   a file with multiple existing long names, even if those long names
542          *   are in different directories and the ntfs_set_ntfs_dos_name() call
543          *   is therefore unambiguous.  (This was apparently changed with the
544          *   FUSE interface in mind.)
545          */
546         .requires_short_name_reordering    = 1,
547 };
548
549 #endif /* WITH_NTFS_3G */