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.
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"
55 ntfs_3g_apply_ctx_get_volume(struct apply_ctx *ctx)
57 return (ntfs_volume*)ctx->private[0];
61 ntfs_3g_apply_ctx_set_volume(struct apply_ctx *ctx, ntfs_volume *vol)
63 ctx->private[0] = (intptr_t)vol;
67 ntfs_3g_apply_pathname_to_inode(const char *path, struct apply_ctx *ctx)
69 ntfs_volume *vol = ntfs_3g_apply_ctx_get_volume(ctx);
70 return ntfs_pathname_to_inode(vol, NULL, path);
73 struct ntfs_attr_extract_ctx {
79 ntfs_3g_extract_wim_chunk(const void *buf, size_t len, void *_ctx)
81 struct ntfs_attr_extract_ctx *ctx = _ctx;
83 if (ntfs_attr_pwrite(ctx->na, ctx->offset, len, buf) != len)
84 return WIMLIB_ERR_WRITE;
90 ntfs_3g_open_parent_inode(const char *path, ntfs_volume *vol)
95 p = strrchr(path, '/');
97 dir_ni = ntfs_pathname_to_inode(vol, NULL, path);
103 ntfs_3g_create(const char *path, struct apply_ctx *ctx, u64 *cookie_ret,
107 ntfs_inode *dir_ni, *ni;
109 utf16lechar *name_utf16le;
110 size_t name_utf16le_nbytes;
113 vol = ntfs_3g_apply_ctx_get_volume(ctx);
115 ret = WIMLIB_ERR_OPEN;
116 dir_ni = ntfs_3g_open_parent_inode(path, vol);
120 name = path_basename(path);
121 ret = tstr_to_utf16le(name, strlen(name),
122 &name_utf16le, &name_utf16le_nbytes);
124 goto out_close_dir_ni;
126 ret = WIMLIB_ERR_OPEN;
127 ni = ntfs_create(dir_ni, 0, name_utf16le,
128 name_utf16le_nbytes / 2, mode);
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;
135 out_free_name_utf16le:
138 if (ntfs_inode_close(dir_ni))
139 ret = WIMLIB_ERR_WRITE;
145 ntfs_3g_create_file(const char *path, struct apply_ctx *ctx,
148 return ntfs_3g_create(path, ctx, cookie_ret, S_IFREG);
152 ntfs_3g_create_directory(const char *path, struct apply_ctx *ctx,
155 return ntfs_3g_create(path, ctx, cookie_ret, S_IFDIR);
159 ntfs_3g_create_hardlink(const char *oldpath, const char *newpath,
160 struct apply_ctx *ctx)
163 ntfs_inode *dir_ni, *ni;
165 utf16lechar *name_utf16le;
166 size_t name_utf16le_nbytes;
169 vol = ntfs_3g_apply_ctx_get_volume(ctx);
171 ret = WIMLIB_ERR_OPEN;
172 ni = ntfs_pathname_to_inode(vol, NULL, oldpath);
176 ret = WIMLIB_ERR_OPEN;
177 dir_ni = ntfs_3g_open_parent_inode(newpath, vol);
181 name = path_basename(newpath);
182 ret = tstr_to_utf16le(name, strlen(name),
183 &name_utf16le, &name_utf16le_nbytes);
185 goto out_close_dir_ni;
187 if (ntfs_link(ni, dir_ni, name_utf16le, name_utf16le_nbytes / 2))
188 ret = WIMLIB_ERR_LINK;
191 if (ntfs_inode_close(dir_ni))
192 ret = WIMLIB_ERR_WRITE;
194 if (ntfs_inode_close(ni))
195 ret = WIMLIB_ERR_WRITE;
201 * Extract a stream (default or alternate data) to an attribute of a NTFS file.
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)
211 struct ntfs_attr_extract_ctx extract_ctx;
212 utf16lechar *stream_name;
214 if (stream_name_nchars == 0) {
215 stream_name = AT_UNNAMED;
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;
224 if (!stream_name_nchars && !lte)
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);
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))
240 /* If stream is empty, no need to open and extract it. */
245 /* Open the stream (NTFS attribute). */
246 ret = WIMLIB_ERR_OPEN;
247 na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_nchars);
251 /* (Optional) Immediately resize attribute to size of stream.
253 * This dramatically speeds up extraction, as demonstrated with the
254 * following timing results:
256 * 18 mins. 27 sec. to apply Windows 7 image (with resize)
257 * 32 mins. 45 sec. to apply Windows 7 image (no resize)
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)))
266 /* Extract stream data to the NTFS attribute. */
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. */
275 if (ntfs_inode_close(ni))
276 ret = WIMLIB_ERR_WRITE;
284 ntfs_3g_extract_unnamed_stream(file_spec_t file,
285 struct wim_lookup_table_entry *lte,
286 struct apply_ctx *ctx)
288 return ntfs_3g_extract_stream(file, NULL, 0, lte, ctx);
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)
296 return ntfs_3g_extract_stream(file, stream_name,
297 stream_name_nchars, lte, ctx);
301 ntfs_3g_set_file_attributes(const char *path, u32 attributes,
302 struct apply_ctx *ctx, unsigned pass)
307 ni = ntfs_3g_apply_pathname_to_inode(path, ctx);
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;
318 ntfs_3g_set_reparse_data(const char *path, const u8 *rpbuf, u16 rpbuflen,
319 struct apply_ctx *ctx)
324 ni = ntfs_3g_apply_pathname_to_inode(path, ctx);
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;
335 ntfs_3g_set_short_name(const char *path, const utf16lechar *short_name,
336 size_t short_name_nchars, struct apply_ctx *ctx)
338 ntfs_inode *ni, *dir_ni;
341 char *dosname = NULL;
342 size_t dosname_nbytes;
345 if (short_name_nchars == 0)
348 vol = ntfs_3g_apply_ctx_get_volume(ctx);
350 ret = WIMLIB_ERR_OPEN;
351 dir_ni = ntfs_3g_open_parent_inode(path, vol);
355 ret = WIMLIB_ERR_OPEN;
356 ni = ntfs_pathname_to_inode(vol, NULL, path);
358 goto out_close_dir_ni;
360 ret = utf16le_to_tstr(short_name, short_name_nchars * 2,
361 &dosname, &dosname_nbytes);
366 if (ntfs_set_ntfs_dos_name(ni, dir_ni, dosname,
368 ret = WIMLIB_ERR_SET_SHORT_NAME;
369 /* ntfs_set_ntfs_dos_name() always closes the inodes. */
373 if (ntfs_inode_close_in_dir(ni, dir_ni))
374 ret = WIMLIB_ERR_WRITE;
376 if (ntfs_inode_close(dir_ni))
377 ret = WIMLIB_ERR_WRITE;
383 ntfs_3g_set_security_descriptor(const char *path, const u8 *desc, size_t desc_size,
384 struct apply_ctx *ctx)
388 struct SECURITY_CONTEXT sec_ctx;
391 vol = ntfs_3g_apply_ctx_get_volume(ctx);
393 ni = ntfs_pathname_to_inode(vol, NULL, path);
395 return WIMLIB_ERR_OPEN;
397 memset(&sec_ctx, 0, sizeof(sec_ctx));
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;
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)
412 u64 ntfs_timestamps[3];
416 ni = ntfs_3g_apply_pathname_to_inode(path, ctx);
418 return WIMLIB_ERR_OPEN;
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;
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;
435 ntfs_3g_target_is_root(const char *target)
437 /* We always extract to the root of the NTFS volume. */
442 ntfs_3g_start_extract(const char *path, struct apply_ctx *ctx)
446 vol = ntfs_mount(ctx->target, 0);
448 ERROR_WITH_ERRNO("Failed to mount \"%"TS"\" with NTFS-3g", ctx->target);
449 return WIMLIB_ERR_OPEN;
451 ntfs_3g_apply_ctx_set_volume(ctx, vol);
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;
469 ntfs_3g_finish_or_abort_extract(struct apply_ctx *ctx)
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",
477 return WIMLIB_ERR_WRITE;
483 libntfs3g_global_init(void)
485 ntfs_set_char_encoding(setlocale(LC_ALL, ""));
488 const struct apply_operations ntfs_3g_apply_ops = {
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,
507 .path_prefix_nchars = 1,
508 .path_separator = '/',
511 .supports_case_sensitive_filenames = 1,
512 .root_directory_is_special = 1,
516 #endif /* WITH_NTFS_3G */