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/.
36 #include <time.h> /* NTFS-3g headers are missing <time.h> include */
38 #include <ntfs-3g/attrib.h>
39 #include <ntfs-3g/reparse.h>
40 #include <ntfs-3g/security.h>
49 #include "wimlib/apply.h"
50 #include "wimlib/encoding.h"
51 #include "wimlib/error.h"
52 #include "wimlib/lookup_table.h"
53 #include "wimlib/ntfs_3g.h"
54 #include "wimlib/paths.h"
55 #include "wimlib/resource.h"
58 ntfs_3g_apply_ctx_get_volume(struct apply_ctx *ctx)
60 return (ntfs_volume*)ctx->private[0];
64 ntfs_3g_apply_ctx_set_volume(struct apply_ctx *ctx, ntfs_volume *vol)
66 ctx->private[0] = (intptr_t)vol;
70 ntfs_3g_apply_pathname_to_inode(const char *path, struct apply_ctx *ctx)
72 ntfs_volume *vol = ntfs_3g_apply_ctx_get_volume(ctx);
73 return ntfs_pathname_to_inode(vol, NULL, path);
76 struct ntfs_attr_extract_ctx {
82 ntfs_3g_extract_wim_chunk(const void *buf, size_t len, void *_ctx)
84 struct ntfs_attr_extract_ctx *ctx = _ctx;
86 if (ntfs_attr_pwrite(ctx->na, ctx->offset, len, buf) != len)
87 return WIMLIB_ERR_WRITE;
93 ntfs_3g_open_parent_inode(const char *path, ntfs_volume *vol)
98 p = strrchr(path, '/');
100 dir_ni = ntfs_pathname_to_inode(vol, NULL, path);
106 ntfs_3g_create(const char *path, struct apply_ctx *ctx, u64 *cookie_ret,
110 ntfs_inode *dir_ni, *ni;
112 utf16lechar *name_utf16le;
113 size_t name_utf16le_nbytes;
116 vol = ntfs_3g_apply_ctx_get_volume(ctx);
118 ret = WIMLIB_ERR_NTFS_3G;
119 dir_ni = ntfs_3g_open_parent_inode(path, vol);
123 name = path_basename(path);
124 ret = tstr_to_utf16le(name, strlen(name),
125 &name_utf16le, &name_utf16le_nbytes);
127 goto out_close_dir_ni;
129 ret = WIMLIB_ERR_NTFS_3G;
130 ni = ntfs_create(dir_ni, 0, name_utf16le,
131 name_utf16le_nbytes / 2, mode);
133 goto out_free_name_utf16le;
134 *cookie_ret = MK_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number));
135 if (ntfs_inode_close_in_dir(ni, dir_ni))
136 goto out_free_name_utf16le;
138 out_free_name_utf16le:
141 if (ntfs_inode_close(dir_ni))
142 ret = WIMLIB_ERR_NTFS_3G;
148 ntfs_3g_create_file(const char *path, struct apply_ctx *ctx,
151 return ntfs_3g_create(path, ctx, cookie_ret, S_IFREG);
155 ntfs_3g_create_directory(const char *path, struct apply_ctx *ctx,
158 return ntfs_3g_create(path, ctx, cookie_ret, S_IFDIR);
162 ntfs_3g_create_hardlink(const char *oldpath, const char *newpath,
163 struct apply_ctx *ctx)
166 ntfs_inode *dir_ni, *ni;
168 utf16lechar *name_utf16le;
169 size_t name_utf16le_nbytes;
172 vol = ntfs_3g_apply_ctx_get_volume(ctx);
174 ret = WIMLIB_ERR_NTFS_3G;
175 ni = ntfs_pathname_to_inode(vol, NULL, oldpath);
179 ret = WIMLIB_ERR_NTFS_3G;
180 dir_ni = ntfs_3g_open_parent_inode(newpath, vol);
184 name = path_basename(newpath);
185 ret = tstr_to_utf16le(name, strlen(name),
186 &name_utf16le, &name_utf16le_nbytes);
188 goto out_close_dir_ni;
190 if (ntfs_link(ni, dir_ni, name_utf16le, name_utf16le_nbytes / 2))
191 ret = WIMLIB_ERR_NTFS_3G;
194 if (ntfs_inode_close(dir_ni))
195 ret = WIMLIB_ERR_NTFS_3G;
197 if (ntfs_inode_close(ni))
198 ret = WIMLIB_ERR_NTFS_3G;
204 * Extract a stream (default or alternate data) to an attribute of a NTFS file.
207 ntfs_3g_extract_stream(file_spec_t file, const utf16lechar *raw_stream_name,
208 size_t stream_name_nchars,
209 struct wim_lookup_table_entry *lte, struct apply_ctx *ctx)
214 struct ntfs_attr_extract_ctx extract_ctx;
215 utf16lechar *stream_name;
217 if (stream_name_nchars == 0) {
218 stream_name = AT_UNNAMED;
220 stream_name = alloca((stream_name_nchars + 1) * sizeof(utf16lechar));
221 memcpy(stream_name, raw_stream_name,
222 stream_name_nchars * sizeof(utf16lechar));
223 stream_name[stream_name_nchars] = 0;
227 if (!stream_name_nchars && !lte)
230 /* Open NTFS inode to which to extract the stream. */
231 ret = WIMLIB_ERR_NTFS_3G;
232 ni = ntfs_inode_open(ntfs_3g_apply_ctx_get_volume(ctx), file.cookie);
236 /* Add the stream if it's not the default (unnamed) stream. */
237 ret = WIMLIB_ERR_NTFS_3G;
238 if (stream_name_nchars)
239 if (ntfs_attr_add(ni, AT_DATA, stream_name,
240 stream_name_nchars, NULL, 0))
243 /* If stream is empty, no need to open and extract it. */
248 /* Open the stream (NTFS attribute). */
249 ret = WIMLIB_ERR_NTFS_3G;
250 na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_nchars);
254 /* (Optional) Immediately resize attribute to size of stream. */
255 ret = WIMLIB_ERR_NTFS_3G;
256 if (ntfs_attr_truncate_solid(na, wim_resource_size(lte)))
259 /* Extract stream data to the NTFS attribute. */
261 extract_ctx.offset = 0;
262 ret = extract_wim_resource(lte, wim_resource_size(lte),
263 ntfs_3g_extract_wim_chunk, &extract_ctx);
264 /* Clean up and return. */
268 if (ntfs_inode_close(ni))
269 ret = WIMLIB_ERR_NTFS_3G;
277 ntfs_3g_extract_unnamed_stream(file_spec_t file,
278 struct wim_lookup_table_entry *lte,
279 struct apply_ctx *ctx)
281 return ntfs_3g_extract_stream(file, NULL, 0, lte, ctx);
285 ntfs_3g_extract_named_stream(file_spec_t file, const utf16lechar *stream_name,
286 size_t stream_name_nchars,
287 struct wim_lookup_table_entry *lte, struct apply_ctx *ctx)
289 return ntfs_3g_extract_stream(file, stream_name,
290 stream_name_nchars, lte, ctx);
294 ntfs_3g_set_file_attributes(const char *path, u32 attributes,
295 struct apply_ctx *ctx)
300 ni = ntfs_3g_apply_pathname_to_inode(path, ctx);
302 return WIMLIB_ERR_NTFS_3G;
303 if (ntfs_set_ntfs_attrib(ni, (const char*)&attributes, sizeof(u32), 0))
304 ret = WIMLIB_ERR_NTFS_3G;
305 if (ntfs_inode_close(ni))
306 ret = WIMLIB_ERR_NTFS_3G;
311 ntfs_3g_set_reparse_data(const char *path, const u8 *rpbuf, u16 rpbuflen,
312 struct apply_ctx *ctx)
317 ni = ntfs_3g_apply_pathname_to_inode(path, ctx);
319 return WIMLIB_ERR_NTFS_3G;
320 if (ntfs_set_ntfs_reparse_data(ni, rpbuf, rpbuflen, 0))
321 ret = WIMLIB_ERR_NTFS_3G;
322 if (ntfs_inode_close(ni))
323 ret = WIMLIB_ERR_NTFS_3G;
328 ntfs_3g_set_short_name(const char *path, const utf16lechar *short_name,
329 size_t short_name_nchars, struct apply_ctx *ctx)
331 ntfs_inode *ni, *dir_ni;
334 char *dosname = NULL;
335 size_t dosname_nbytes;
338 if (short_name_nchars == 0)
341 vol = ntfs_3g_apply_ctx_get_volume(ctx);
343 ret = WIMLIB_ERR_NTFS_3G;
344 dir_ni = ntfs_3g_open_parent_inode(path, vol);
348 ret = WIMLIB_ERR_NTFS_3G;
349 ni = ntfs_pathname_to_inode(vol, NULL, path);
351 goto out_close_dir_ni;
353 ret = utf16le_to_tstr(short_name, short_name_nchars * 2,
354 &dosname, &dosname_nbytes);
359 if (ntfs_set_ntfs_dos_name(ni, dir_ni, dosname,
361 ret = WIMLIB_ERR_NTFS_3G;
362 /* ntfs_set_ntfs_dos_name() always closes the inodes. */
366 if (ntfs_inode_close_in_dir(ni, dir_ni))
367 ret = WIMLIB_ERR_NTFS_3G;
369 if (ntfs_inode_close(dir_ni))
370 ret = WIMLIB_ERR_NTFS_3G;
376 ntfs_3g_set_security_descriptor(const char *path, const u8 *desc, size_t desc_size,
377 struct apply_ctx *ctx)
381 struct SECURITY_CONTEXT sec_ctx;
384 vol = ntfs_3g_apply_ctx_get_volume(ctx);
386 ni = ntfs_pathname_to_inode(vol, NULL, path);
388 return WIMLIB_ERR_NTFS_3G;
390 memset(&sec_ctx, 0, sizeof(sec_ctx));
393 if (ntfs_set_ntfs_acl(&sec_ctx, ni, desc, desc_size, 0))
394 ret = WIMLIB_ERR_NTFS_3G;
395 if (ntfs_inode_close(ni))
396 ret = WIMLIB_ERR_NTFS_3G;
401 ntfs_3g_set_timestamps(const char *path, u64 creation_time,
402 u64 last_write_time, u64 last_access_time,
403 struct apply_ctx *ctx)
405 u64 ntfs_timestamps[3];
409 ni = ntfs_3g_apply_pathname_to_inode(path, ctx);
411 return WIMLIB_ERR_NTFS_3G;
413 /* Note: ntfs_inode_set_times() expects the times in native byte order,
414 * not little endian. */
415 ntfs_timestamps[0] = creation_time;
416 ntfs_timestamps[1] = last_write_time;
417 ntfs_timestamps[2] = last_access_time;
419 if (ntfs_inode_set_times(ni, (const char*)ntfs_timestamps,
420 sizeof(ntfs_timestamps), 0))
421 ret = WIMLIB_ERR_NTFS_3G;
422 if (ntfs_inode_close(ni))
423 ret = WIMLIB_ERR_NTFS_3G;
428 ntfs_3g_target_is_root(const char *target)
430 /* We always extract to the root of the NTFS volume. */
435 ntfs_3g_start_extract(const char *path, struct apply_ctx *ctx)
439 vol = ntfs_mount(ctx->target, 0);
441 ERROR_WITH_ERRNO("Failed to mount \"%"TS"\" with NTFS-3g", ctx->target);
442 return WIMLIB_ERR_NTFS_3G;
444 ntfs_3g_apply_ctx_set_volume(ctx, vol);
446 ctx->supported_features.archive_files = 1;
447 ctx->supported_features.hidden_files = 1;
448 ctx->supported_features.system_files = 1;
449 ctx->supported_features.compressed_files = 1;
450 ctx->supported_features.encrypted_files = 0;
451 ctx->supported_features.not_context_indexed_files = 1;
452 ctx->supported_features.sparse_files = 1;
453 ctx->supported_features.named_data_streams = 1;
454 ctx->supported_features.hard_links = 1;
455 ctx->supported_features.reparse_points = 1;
456 ctx->supported_features.security_descriptors = 1;
457 ctx->supported_features.short_names = 1;
462 ntfs_3g_finish_or_abort_extract(struct apply_ctx *ctx)
466 vol = ntfs_3g_apply_ctx_get_volume(ctx);
467 if (ntfs_umount(vol, FALSE)) {
468 ERROR_WITH_ERRNO("Failed to unmount \"%"TS"\" with NTFS-3g",
470 return WIMLIB_ERR_NTFS_3G;
476 libntfs3g_global_init(void)
478 ntfs_set_char_encoding(setlocale(LC_ALL, ""));
481 const struct apply_operations ntfs_3g_apply_ops = {
484 .target_is_root = ntfs_3g_target_is_root,
485 .start_extract = ntfs_3g_start_extract,
486 .create_file = ntfs_3g_create_file,
487 .create_directory = ntfs_3g_create_directory,
488 .create_hardlink = ntfs_3g_create_hardlink,
489 .extract_unnamed_stream = ntfs_3g_extract_unnamed_stream,
490 .extract_named_stream = ntfs_3g_extract_named_stream,
491 .set_file_attributes = ntfs_3g_set_file_attributes,
492 .set_reparse_data = ntfs_3g_set_reparse_data,
493 .set_short_name = ntfs_3g_set_short_name,
494 .set_security_descriptor = ntfs_3g_set_security_descriptor,
495 .set_timestamps = ntfs_3g_set_timestamps,
496 .abort_extract = ntfs_3g_finish_or_abort_extract,
497 .finish_extract = ntfs_3g_finish_or_abort_extract,
500 .path_prefix_nchars = 1,
501 .path_separator = '/',
504 .supports_case_sensitive_filenames = 1,
505 .root_directory_is_special = 1,
509 #endif /* WITH_NTFS_3G */