4 * Apply a WIM image directly to an NTFS volume using libntfs-3g. Restore as
5 * much information as possible, including security data, file attributes, DOS
6 * names, and alternate data streams.
10 * Copyright (C) 2012, 2013 Eric Biggers
12 * This file is part of wimlib, a library for working with WIM files.
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)
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
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/.
42 #include <ntfs-3g/attrib.h>
43 #include <ntfs-3g/reparse.h>
44 #include <ntfs-3g/security.h>
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 #include "wimlib/security_descriptor.h"
56 ntfs_3g_apply_ctx_get_volume(struct apply_ctx *ctx)
58 return (ntfs_volume*)ctx->private[0];
62 ntfs_3g_apply_ctx_set_volume(struct apply_ctx *ctx, ntfs_volume *vol)
64 ctx->private[0] = (intptr_t)vol;
68 ntfs_3g_apply_pathname_to_inode(const char *path, struct apply_ctx *ctx)
70 ntfs_volume *vol = ntfs_3g_apply_ctx_get_volume(ctx);
71 return ntfs_pathname_to_inode(vol, NULL, path);
74 struct ntfs_attr_extract_ctx {
80 ntfs_3g_extract_wim_chunk(const void *buf, size_t len, void *_ctx)
82 struct ntfs_attr_extract_ctx *ctx = _ctx;
84 if (ntfs_attr_pwrite(ctx->na, ctx->offset, len, buf) != len)
85 return WIMLIB_ERR_WRITE;
91 ntfs_3g_open_parent_inode(const char *path, ntfs_volume *vol)
96 p = strrchr(path, '/');
98 dir_ni = ntfs_pathname_to_inode(vol, NULL, path);
104 ntfs_3g_create(const char *path, struct apply_ctx *ctx, u64 *cookie_ret,
108 ntfs_inode *dir_ni, *ni;
110 utf16lechar *name_utf16le;
111 size_t name_utf16le_nbytes;
114 vol = ntfs_3g_apply_ctx_get_volume(ctx);
116 ret = WIMLIB_ERR_OPEN;
117 dir_ni = ntfs_3g_open_parent_inode(path, vol);
121 name = path_basename(path);
122 ret = tstr_to_utf16le(name, strlen(name),
123 &name_utf16le, &name_utf16le_nbytes);
125 goto out_close_dir_ni;
127 ret = WIMLIB_ERR_OPEN;
128 ni = ntfs_create(dir_ni, 0, name_utf16le,
129 name_utf16le_nbytes / 2, mode);
131 goto out_free_name_utf16le;
132 *cookie_ret = MK_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number));
133 if (ntfs_inode_close_in_dir(ni, dir_ni))
134 goto out_free_name_utf16le;
136 out_free_name_utf16le:
139 if (ntfs_inode_close(dir_ni))
140 ret = WIMLIB_ERR_WRITE;
146 ntfs_3g_create_file(const char *path, struct apply_ctx *ctx,
149 return ntfs_3g_create(path, ctx, cookie_ret, S_IFREG);
153 ntfs_3g_create_directory(const char *path, struct apply_ctx *ctx,
156 return ntfs_3g_create(path, ctx, cookie_ret, S_IFDIR);
160 ntfs_3g_create_hardlink(const char *oldpath, const char *newpath,
161 struct apply_ctx *ctx)
164 ntfs_inode *dir_ni, *ni;
166 utf16lechar *name_utf16le;
167 size_t name_utf16le_nbytes;
170 vol = ntfs_3g_apply_ctx_get_volume(ctx);
172 ret = WIMLIB_ERR_OPEN;
173 ni = ntfs_pathname_to_inode(vol, NULL, oldpath);
177 ret = WIMLIB_ERR_OPEN;
178 dir_ni = ntfs_3g_open_parent_inode(newpath, vol);
182 name = path_basename(newpath);
183 ret = tstr_to_utf16le(name, strlen(name),
184 &name_utf16le, &name_utf16le_nbytes);
186 goto out_close_dir_ni;
188 if (ntfs_link(ni, dir_ni, name_utf16le, name_utf16le_nbytes / 2))
189 ret = WIMLIB_ERR_LINK;
192 if (ntfs_inode_close(dir_ni))
193 ret = WIMLIB_ERR_WRITE;
195 if (ntfs_inode_close(ni))
196 ret = WIMLIB_ERR_WRITE;
202 * Extract a stream (default or alternate data) to an attribute of an NTFS file.
205 ntfs_3g_extract_stream(file_spec_t file, const utf16lechar *raw_stream_name,
206 size_t stream_name_nchars,
207 struct wim_lookup_table_entry *lte, struct apply_ctx *ctx)
212 struct ntfs_attr_extract_ctx extract_ctx;
213 utf16lechar *stream_name;
215 if (stream_name_nchars == 0) {
216 stream_name = AT_UNNAMED;
218 stream_name = alloca((stream_name_nchars + 1) * sizeof(utf16lechar));
219 memcpy(stream_name, raw_stream_name,
220 stream_name_nchars * sizeof(utf16lechar));
221 stream_name[stream_name_nchars] = 0;
225 if (!stream_name_nchars && !lte)
228 /* Open NTFS inode to which to extract the stream. */
229 ret = WIMLIB_ERR_OPEN;
230 ni = ntfs_inode_open(ntfs_3g_apply_ctx_get_volume(ctx), file.cookie);
234 /* Add the stream if it's not the default (unnamed) stream. */
235 ret = WIMLIB_ERR_OPEN;
236 if (stream_name_nchars)
237 if (ntfs_attr_add(ni, AT_DATA, stream_name,
238 stream_name_nchars, NULL, 0))
241 /* If stream is empty, no need to open and extract it. */
246 /* Open the stream (NTFS attribute). */
247 ret = WIMLIB_ERR_OPEN;
248 na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_nchars);
252 /* (Optional) Immediately resize attribute to size of stream.
254 * This dramatically speeds up extraction, as demonstrated with the
255 * following timing results:
257 * 18 mins. 27 sec. to apply Windows 7 image (with resize)
258 * 32 mins. 45 sec. to apply Windows 7 image (no resize)
260 * It probably would speed things up even more if we could get NTFS-3g
261 * to skip even more useless work (for example it fills resized
262 * attributes with 0's, then we just override it.) */
263 ret = WIMLIB_ERR_WRITE;
264 if (ntfs_attr_truncate_solid(na, lte->size))
267 /* Extract stream data to the NTFS attribute. */
269 extract_ctx.offset = 0;
270 ret = extract_stream(lte, lte->size,
271 ntfs_3g_extract_wim_chunk, &extract_ctx);
272 /* Clean up and return. */
276 if (ntfs_inode_close(ni))
277 ret = WIMLIB_ERR_WRITE;
285 ntfs_3g_extract_unnamed_stream(file_spec_t file,
286 struct wim_lookup_table_entry *lte,
287 struct apply_ctx *ctx,
288 struct wim_dentry *_ignore)
290 return ntfs_3g_extract_stream(file, NULL, 0, lte, ctx);
294 ntfs_3g_extract_named_stream(file_spec_t file, const utf16lechar *stream_name,
295 size_t stream_name_nchars,
296 struct wim_lookup_table_entry *lte, struct apply_ctx *ctx)
298 return ntfs_3g_extract_stream(file, stream_name,
299 stream_name_nchars, lte, ctx);
303 ntfs_3g_set_file_attributes(const char *path, u32 attributes,
304 struct apply_ctx *ctx, unsigned pass)
309 ni = ntfs_3g_apply_pathname_to_inode(path, ctx);
311 return WIMLIB_ERR_OPEN;
312 if (ntfs_set_ntfs_attrib(ni, (const char*)&attributes, sizeof(u32), 0))
313 ret = WIMLIB_ERR_SET_ATTRIBUTES;
314 if (ntfs_inode_close(ni))
315 ret = WIMLIB_ERR_WRITE;
320 ntfs_3g_set_reparse_data(const char *path, const u8 *rpbuf, u16 rpbuflen,
321 struct apply_ctx *ctx)
326 ni = ntfs_3g_apply_pathname_to_inode(path, ctx);
328 return WIMLIB_ERR_OPEN;
329 if (ntfs_set_ntfs_reparse_data(ni, rpbuf, rpbuflen, 0))
330 ret = WIMLIB_ERR_SET_REPARSE_DATA;
331 if (ntfs_inode_close(ni))
332 ret = WIMLIB_ERR_WRITE;
337 ntfs_3g_set_short_name(const char *path, const utf16lechar *short_name,
338 size_t short_name_nchars, struct apply_ctx *ctx)
340 ntfs_inode *ni, *dir_ni;
343 char *dosname = NULL;
344 size_t dosname_nbytes;
347 if (short_name_nchars == 0)
350 vol = ntfs_3g_apply_ctx_get_volume(ctx);
352 ret = WIMLIB_ERR_OPEN;
353 dir_ni = ntfs_3g_open_parent_inode(path, vol);
357 ret = WIMLIB_ERR_OPEN;
358 ni = ntfs_pathname_to_inode(vol, NULL, path);
360 goto out_close_dir_ni;
362 ret = utf16le_to_tstr(short_name, short_name_nchars * 2,
363 &dosname, &dosname_nbytes);
368 if (ntfs_set_ntfs_dos_name(ni, dir_ni, dosname,
370 ret = WIMLIB_ERR_SET_SHORT_NAME;
371 /* ntfs_set_ntfs_dos_name() always closes the inodes. */
375 if (ntfs_inode_close_in_dir(ni, dir_ni))
376 ret = WIMLIB_ERR_WRITE;
378 if (ntfs_inode_close(dir_ni))
379 ret = WIMLIB_ERR_WRITE;
385 sid_size(const wimlib_SID *sid)
387 return offsetof(wimlib_SID, sub_authority) +
388 sizeof(le32) * sid->sub_authority_count;
392 * sd_fixup - Fix up a Windows NT security descriptor for libntfs-3g.
394 * libntfs-3g validates security descriptors before setting them, but old
395 * versions contain bugs causing it to reject unusual but valid security
398 * - Versions before 2013.1.13 reject security descriptors ending with an empty
399 * SACL (System Access Control List). This bug can be worked around either by
400 * moving the empty SACL earlier in the security descriptor or by removing the
401 * SACL entirely. The latter work-around is valid because an empty SACL is
402 * equivalent to a "null", or non-existent, SACL.
403 * - Versions up to and including 2013.1.13 reject security descriptors ending
404 * with an empty DACL (Discretionary Access Control List). This is very
405 * similar to the SACL bug and should be fixed in the next release after
406 * 2013.1.13. However, removing the DACL is not a valid workaround because
407 * this changes the meaning of the security descriptor--- an empty DACL allows
408 * no access, whereas a "null" DACL allows all access.
410 * If the security descriptor was fixed, this function returns an allocated
411 * buffer containing the fixed security descriptor, and its size is updated.
412 * Otherwise (or if no memory is available) the original descriptor is returned.
415 sd_fixup(const u8 *_desc, size_t *size_p)
417 u32 owner_offset, group_offset, dacl_offset, sacl_offset;
418 bool owner_valid, group_valid;
419 size_t size = *size_p;
420 const wimlib_SECURITY_DESCRIPTOR_RELATIVE *desc =
421 (const wimlib_SECURITY_DESCRIPTOR_RELATIVE*)_desc;
422 wimlib_SECURITY_DESCRIPTOR_RELATIVE *desc_new;
423 const wimlib_SID *owner, *group, *sid;
425 /* Don't attempt to fix clearly invalid security descriptors. */
426 if (size < sizeof(wimlib_SECURITY_DESCRIPTOR_RELATIVE))
429 if (le16_to_cpu(desc->control) & wimlib_SE_DACL_PRESENT)
430 dacl_offset = le32_to_cpu(desc->dacl_offset);
434 if (le16_to_cpu(desc->control) & wimlib_SE_SACL_PRESENT)
435 sacl_offset = le32_to_cpu(desc->sacl_offset);
439 /* Check if the security descriptor will be affected by one of the bugs.
440 * If not, do nothing and return.
442 * Note: HAVE_NTFS_MNT_RDONLY is defined if libntfs-3g is
443 * version 2013.1.13 or later. */
445 #if !defined(HAVE_NTFS_MNT_RDONLY)
446 (sacl_offset != 0 && sacl_offset == size - sizeof(wimlib_ACL)) ||
448 (dacl_offset != 0 && dacl_offset == size - sizeof(wimlib_ACL))))
451 owner_offset = le32_to_cpu(desc->owner_offset);
452 group_offset = le32_to_cpu(desc->group_offset);
453 owner = (const wimlib_SID*)((const u8*)desc + owner_offset);
454 group = (const wimlib_SID*)((const u8*)desc + group_offset);
456 /* We'll try to move the owner or group SID to the end of the security
457 * descriptor to avoid the bug. This is only possible if at least one
459 owner_valid = (owner_offset != 0) &&
460 (owner_offset % 4 == 0) &&
461 (owner_offset <= size - sizeof(SID)) &&
462 (owner_offset + sid_size(owner) <= size) &&
463 (owner_offset >= sizeof(wimlib_SECURITY_DESCRIPTOR_RELATIVE));
464 group_valid = (group_offset != 0) &&
465 (group_offset % 4 == 0) &&
466 (group_offset <= size - sizeof(SID)) &&
467 (group_offset + sid_size(group) <= size) &&
468 (group_offset >= sizeof(wimlib_SECURITY_DESCRIPTOR_RELATIVE));
471 } else if (group_valid) {
477 desc_new = MALLOC(size + sid_size(sid));
478 if (desc_new == NULL)
481 memcpy(desc_new, desc, size);
483 desc_new->owner_offset = cpu_to_le32(size);
484 else if (group_valid)
485 desc_new->group_offset = cpu_to_le32(size);
486 memcpy((u8*)desc_new + size, sid, sid_size(sid));
487 *size_p = size + sid_size(sid);
488 return (u8*)desc_new;
492 ntfs_3g_set_security_descriptor(const char *path, const u8 *desc, size_t desc_size,
493 struct apply_ctx *ctx)
497 struct SECURITY_CONTEXT sec_ctx;
501 vol = ntfs_3g_apply_ctx_get_volume(ctx);
503 ni = ntfs_pathname_to_inode(vol, NULL, path);
505 return WIMLIB_ERR_OPEN;
507 memset(&sec_ctx, 0, sizeof(sec_ctx));
510 desc_fixed = sd_fixup(desc, &desc_size);
514 if (ntfs_set_ntfs_acl(&sec_ctx, ni, desc_fixed, desc_size, 0))
515 ret = WIMLIB_ERR_SET_SECURITY;
517 if (desc_fixed != desc)
520 if (ntfs_inode_close(ni))
521 ret = WIMLIB_ERR_WRITE;
527 ntfs_3g_set_timestamps(const char *path, u64 creation_time,
528 u64 last_write_time, u64 last_access_time,
529 struct apply_ctx *ctx)
531 u64 ntfs_timestamps[3];
535 ni = ntfs_3g_apply_pathname_to_inode(path, ctx);
537 return WIMLIB_ERR_OPEN;
539 /* Note: ntfs_inode_set_times() expects the times in native byte order,
540 * not little endian. */
541 ntfs_timestamps[0] = creation_time;
542 ntfs_timestamps[1] = last_write_time;
543 ntfs_timestamps[2] = last_access_time;
545 if (ntfs_inode_set_times(ni, (const char*)ntfs_timestamps,
546 sizeof(ntfs_timestamps), 0))
547 ret = WIMLIB_ERR_SET_TIMESTAMPS;
548 if (ntfs_inode_close(ni))
549 ret = WIMLIB_ERR_WRITE;
554 ntfs_3g_target_is_root(const char *target)
556 /* We always extract to the root of the NTFS volume. */
561 ntfs_3g_start_extract(const char *path, struct apply_ctx *ctx)
565 vol = ntfs_mount(ctx->target, 0);
567 ERROR_WITH_ERRNO("Failed to mount \"%"TS"\" with NTFS-3g", ctx->target);
568 return WIMLIB_ERR_OPEN;
570 ntfs_3g_apply_ctx_set_volume(ctx, vol);
572 ctx->supported_features.archive_files = 1;
573 ctx->supported_features.hidden_files = 1;
574 ctx->supported_features.system_files = 1;
575 ctx->supported_features.compressed_files = 1;
576 ctx->supported_features.encrypted_files = 0;
577 ctx->supported_features.not_context_indexed_files = 1;
578 ctx->supported_features.sparse_files = 1;
579 ctx->supported_features.named_data_streams = 1;
580 ctx->supported_features.hard_links = 1;
581 ctx->supported_features.reparse_points = 1;
582 ctx->supported_features.security_descriptors = 1;
583 ctx->supported_features.short_names = 1;
588 ntfs_3g_finish_or_abort_extract(struct apply_ctx *ctx)
592 vol = ntfs_3g_apply_ctx_get_volume(ctx);
593 if (ntfs_umount(vol, FALSE)) {
594 ERROR_WITH_ERRNO("Failed to unmount \"%"TS"\" with NTFS-3g",
596 return WIMLIB_ERR_WRITE;
602 libntfs3g_global_init(void)
604 ntfs_set_char_encoding(setlocale(LC_ALL, ""));
607 const struct apply_operations ntfs_3g_apply_ops = {
610 .target_is_root = ntfs_3g_target_is_root,
611 .start_extract = ntfs_3g_start_extract,
612 .create_file = ntfs_3g_create_file,
613 .create_directory = ntfs_3g_create_directory,
614 .create_hardlink = ntfs_3g_create_hardlink,
615 .extract_unnamed_stream = ntfs_3g_extract_unnamed_stream,
616 .extract_named_stream = ntfs_3g_extract_named_stream,
617 .set_file_attributes = ntfs_3g_set_file_attributes,
618 .set_reparse_data = ntfs_3g_set_reparse_data,
619 .set_short_name = ntfs_3g_set_short_name,
620 .set_security_descriptor = ntfs_3g_set_security_descriptor,
621 .set_timestamps = ntfs_3g_set_timestamps,
622 .abort_extract = ntfs_3g_finish_or_abort_extract,
623 .finish_extract = ntfs_3g_finish_or_abort_extract,
626 .path_prefix_nchars = 1,
627 .path_separator = '/',
630 /* By default, NTFS-3g creates names in the NTFS POSIX namespace, which
631 * is case-sensitive. */
632 .supports_case_sensitive_filenames = 1,
634 /* The root directory of the NTFS volume should not be created
636 .root_directory_is_special = 1,
638 /* NTFS-3g can open files by MFT reference. */
642 * With NTFS-3g, the extraction order of the names of a file that has a
643 * short name needs to be:
645 * 1. Create file using the long name that has an associated short name.
646 * This long name is temporarily placed in the POSIX namespace.
647 * 2. Set the short name on the file. This will either change the POSIX
648 * name to Win32 and create a new DOS name, or replace the POSIX name
649 * with a Win32+DOS name.
650 * 3. Create additional long names (links) of the file, which are placed
651 * in the POSIX namespace.
653 * The reason for this is that two issues can come up when the
654 * extraction is done otherwise:
656 * - If a DOS name is set on a file in a directory with several long
657 * names, it is ambiguous which long name to use (at least with the
658 * exported ntfs_set_ntfs_dos_name() function).
659 * - NTFS-3g 2013.1.13 will no longer allow even setting the DOS name on
660 * a file with multiple existing long names, even if those long names
661 * are in different directories and the ntfs_set_ntfs_dos_name() call
662 * is therefore unambiguous. (This was apparently changed with the
663 * FUSE interface in mind.)
665 .requires_short_name_reordering = 1,
668 #endif /* WITH_NTFS_3G */