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>
43 #include "wimlib/apply.h"
44 #include "wimlib/encoding.h"
45 #include "wimlib/error.h"
46 #include "wimlib/lookup_table.h"
47 #include "wimlib/ntfs_3g.h"
48 #include "wimlib/paths.h"
49 #include "wimlib/resource.h"
52 ntfs_3g_apply_ctx_get_volume(struct apply_ctx *ctx)
54 return (ntfs_volume*)ctx->private[0];
58 ntfs_3g_apply_ctx_set_volume(struct apply_ctx *ctx, ntfs_volume *vol)
60 ctx->private[0] = (intptr_t)vol;
64 ntfs_3g_apply_pathname_to_inode(const char *path, struct apply_ctx *ctx)
66 ntfs_volume *vol = ntfs_3g_apply_ctx_get_volume(ctx);
67 return ntfs_pathname_to_inode(vol, NULL, path);
70 struct ntfs_attr_extract_ctx {
76 ntfs_3g_extract_wim_chunk(const void *buf, size_t len, void *_ctx)
78 struct ntfs_attr_extract_ctx *ctx = _ctx;
80 if (ntfs_attr_pwrite(ctx->na, ctx->offset, len, buf) != len)
81 return WIMLIB_ERR_WRITE;
87 ntfs_3g_open_parent_inode(const char *path, ntfs_volume *vol)
92 p = strchr(path, '\0');
99 dir_ni = ntfs_pathname_to_inode(vol, NULL, path);
105 ntfs_3g_create(const char *path, struct apply_ctx *ctx, mode_t mode)
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_NTFS_3G;
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;
128 ni = ntfs_create(dir_ni, 0, name_utf16le,
129 name_utf16le_nbytes / 2, mode);
130 if (!ni || ntfs_inode_close_in_dir(ni, dir_ni))
131 ret = WIMLIB_ERR_NTFS_3G;
134 if (ntfs_inode_close(dir_ni))
135 ret = WIMLIB_ERR_NTFS_3G;
141 ntfs_3g_create_file(const char *path, struct apply_ctx *ctx)
143 return ntfs_3g_create(path, ctx, S_IFREG);
147 ntfs_3g_create_directory(const char *path, struct apply_ctx *ctx)
149 return ntfs_3g_create(path, ctx, S_IFDIR);
153 ntfs_3g_create_hardlink(const char *oldpath, const char *newpath,
154 struct apply_ctx *ctx)
157 ntfs_inode *dir_ni, *ni;
159 utf16lechar *name_utf16le;
160 size_t name_utf16le_nbytes;
163 vol = ntfs_3g_apply_ctx_get_volume(ctx);
165 ret = WIMLIB_ERR_NTFS_3G;
166 ni = ntfs_pathname_to_inode(vol, NULL, oldpath);
170 ret = WIMLIB_ERR_NTFS_3G;
171 dir_ni = ntfs_3g_open_parent_inode(newpath, vol);
175 name = path_basename(newpath);
176 ret = tstr_to_utf16le(name, strlen(name),
177 &name_utf16le, &name_utf16le_nbytes);
179 goto out_close_dir_ni;
182 if (ntfs_link(ni, dir_ni, name_utf16le, name_utf16le_nbytes / 2))
183 ret = WIMLIB_ERR_NTFS_3G;
186 if (ntfs_inode_close(dir_ni))
187 ret = WIMLIB_ERR_NTFS_3G;
189 if (ntfs_inode_close(ni))
190 ret = WIMLIB_ERR_NTFS_3G;
196 * Extract a stream (default or alternate data) to an attribute of a NTFS file.
199 ntfs_3g_extract_stream(const char *path, const utf16lechar *stream_name,
200 size_t stream_name_nchars,
201 struct wim_lookup_table_entry *lte, struct apply_ctx *ctx)
206 struct ntfs_attr_extract_ctx extract_ctx;
207 utf16lechar stream_name_copy[stream_name_nchars + 1];
209 memcpy(stream_name_copy, stream_name,
210 stream_name_nchars * sizeof(utf16lechar));
211 stream_name_copy[stream_name_nchars] = 0;
214 if (!stream_name_nchars && !lte)
217 /* Look up NTFS inode to which to extract the stream. */
218 ret = WIMLIB_ERR_NTFS_3G;
219 ni = ntfs_3g_apply_pathname_to_inode(path, ctx);
223 /* Add the stream if it's not the default (unnamed) stream. */
224 ret = WIMLIB_ERR_NTFS_3G;
225 if (stream_name_nchars)
226 if (ntfs_attr_add(ni, AT_DATA, stream_name_copy,
227 stream_name_nchars, NULL, 0))
230 /* If stream is empty, no need to open and extract it. */
235 /* Open the stream (NTFS attribute). */
236 ret = WIMLIB_ERR_NTFS_3G;
237 na = ntfs_attr_open(ni, AT_DATA, stream_name_copy, stream_name_nchars);
241 /* (Optional) Immediately resize attribute to size of stream. */
242 ret = WIMLIB_ERR_NTFS_3G;
243 if (ntfs_attr_truncate_solid(na, wim_resource_size(lte)))
246 /* Extract stream data to the NTFS attribute. */
248 extract_ctx.offset = 0;
249 ret = extract_wim_resource(lte, wim_resource_size(lte),
250 ntfs_3g_extract_wim_chunk, &extract_ctx);
251 /* Clean up and return. */
255 if (ntfs_inode_close(ni))
256 ret = WIMLIB_ERR_NTFS_3G;
264 ntfs_3g_extract_unnamed_stream(const char *path,
265 struct wim_lookup_table_entry *lte,
266 struct apply_ctx *ctx)
268 return ntfs_3g_extract_stream(path, NULL, 0, lte, ctx);
272 ntfs_3g_extract_named_stream(const char *path, const utf16lechar *stream_name,
273 size_t stream_name_nchars,
274 struct wim_lookup_table_entry *lte, struct apply_ctx *ctx)
276 return ntfs_3g_extract_stream(path, stream_name,
277 stream_name_nchars, lte, ctx);
281 ntfs_3g_set_file_attributes(const char *path, u32 attributes,
282 struct apply_ctx *ctx)
287 ni = ntfs_3g_apply_pathname_to_inode(path, ctx);
289 return WIMLIB_ERR_NTFS_3G;
290 if (ntfs_set_ntfs_attrib(ni, (const char*)&attributes, sizeof(u32), 0))
291 ret = WIMLIB_ERR_NTFS_3G;
292 if (ntfs_inode_close(ni))
293 ret = WIMLIB_ERR_NTFS_3G;
298 ntfs_3g_set_reparse_data(const char *path, const u8 *rpbuf, u16 rpbuflen,
299 struct apply_ctx *ctx)
304 ni = ntfs_3g_apply_pathname_to_inode(path, ctx);
306 return WIMLIB_ERR_NTFS_3G;
307 if (ntfs_set_ntfs_reparse_data(ni, rpbuf, rpbuflen, 0))
308 ret = WIMLIB_ERR_NTFS_3G;
309 if (ntfs_inode_close(ni))
310 ret = WIMLIB_ERR_NTFS_3G;
315 ntfs_3g_set_short_name(const char *path, const utf16lechar *short_name,
316 size_t short_name_nchars, struct apply_ctx *ctx)
318 ntfs_inode *ni, *dir_ni;
321 char *dosname = NULL;
322 size_t dosname_nbytes;
325 if (short_name_nchars == 0)
328 vol = ntfs_3g_apply_ctx_get_volume(ctx);
330 ret = WIMLIB_ERR_NTFS_3G;
331 dir_ni = ntfs_3g_open_parent_inode(path, vol);
335 ret = WIMLIB_ERR_NTFS_3G;
336 ni = ntfs_pathname_to_inode(vol, NULL, path);
338 goto out_close_dir_ni;
340 ret = utf16le_to_tstr(short_name, short_name_nchars * 2,
341 &dosname, &dosname_nbytes);
346 if (ntfs_set_ntfs_dos_name(ni, dir_ni, dosname,
348 ret = WIMLIB_ERR_NTFS_3G;
349 /* ntfs_set_ntfs_dos_name() always closes the inodes. */
353 if (ntfs_inode_close_in_dir(ni, dir_ni))
354 ret = WIMLIB_ERR_NTFS_3G;
356 if (ntfs_inode_close(dir_ni))
357 ret = WIMLIB_ERR_NTFS_3G;
363 ntfs_3g_set_security_descriptor(const char *path, const u8 *desc, size_t desc_size,
364 struct apply_ctx *ctx, bool strict)
368 struct SECURITY_CONTEXT sec_ctx;
371 vol = ntfs_3g_apply_ctx_get_volume(ctx);
373 ni = ntfs_pathname_to_inode(vol, NULL, path);
375 return WIMLIB_ERR_NTFS_3G;
377 memset(&sec_ctx, 0, sizeof(sec_ctx));
380 if (ntfs_set_ntfs_acl(&sec_ctx, ni, desc, desc_size, 0))
381 ret = WIMLIB_ERR_NTFS_3G;
382 if (ntfs_inode_close(ni))
383 ret = WIMLIB_ERR_NTFS_3G;
388 ntfs_3g_set_timestamps(const char *path, u64 creation_time,
389 u64 last_write_time, u64 last_access_time,
390 struct apply_ctx *ctx)
392 u64 ntfs_timestamps[3];
396 ni = ntfs_3g_apply_pathname_to_inode(path, ctx);
398 return WIMLIB_ERR_NTFS_3G;
400 /* Note: ntfs_inode_set_times() expects the times in native byte order,
401 * not little endian. */
402 ntfs_timestamps[0] = creation_time;
403 ntfs_timestamps[1] = last_write_time;
404 ntfs_timestamps[2] = last_access_time;
406 if (ntfs_inode_set_times(ni, (const char*)ntfs_timestamps,
407 sizeof(ntfs_timestamps), 0))
408 ret = WIMLIB_ERR_NTFS_3G;
409 if (ntfs_inode_close(ni))
410 ret = WIMLIB_ERR_NTFS_3G;
415 ntfs_3g_target_is_root(const char *target)
417 /* We always extract to the root of the NTFS volume. */
422 ntfs_3g_start_extract(const char *path, struct apply_ctx *ctx)
426 vol = ntfs_mount(ctx->target, 0);
428 ERROR_WITH_ERRNO("Failed to mount \"%"TS"\" with NTFS-3g", ctx->target);
429 return WIMLIB_ERR_NTFS_3G;
431 ntfs_3g_apply_ctx_set_volume(ctx, vol);
433 ctx->supported_features.archive_files = 1;
434 ctx->supported_features.hidden_files = 1;
435 ctx->supported_features.system_files = 1;
436 ctx->supported_features.compressed_files = 1;
437 ctx->supported_features.encrypted_files = 0;
438 ctx->supported_features.not_context_indexed_files = 1;
439 ctx->supported_features.sparse_files = 1;
440 ctx->supported_features.named_data_streams = 1;
441 ctx->supported_features.hard_links = 1;
442 ctx->supported_features.reparse_points = 1;
443 ctx->supported_features.security_descriptors = 1;
444 ctx->supported_features.short_names = 1;
449 ntfs_3g_finish_or_abort_extract(struct apply_ctx *ctx)
453 vol = ntfs_3g_apply_ctx_get_volume(ctx);
454 if (ntfs_umount(vol, FALSE)) {
455 ERROR_WITH_ERRNO("Failed to unmount \"%"TS"\" with NTFS-3g",
457 return WIMLIB_ERR_NTFS_3G;
463 libntfs3g_global_init(void)
465 ntfs_set_char_encoding(setlocale(LC_ALL, ""));
468 const struct apply_operations ntfs_3g_apply_ops = {
471 .target_is_root = ntfs_3g_target_is_root,
472 .start_extract = ntfs_3g_start_extract,
473 .create_file = ntfs_3g_create_file,
474 .create_directory = ntfs_3g_create_directory,
475 .create_hardlink = ntfs_3g_create_hardlink,
476 .extract_unnamed_stream = ntfs_3g_extract_unnamed_stream,
477 .extract_named_stream = ntfs_3g_extract_named_stream,
478 .set_file_attributes = ntfs_3g_set_file_attributes,
479 .set_reparse_data = ntfs_3g_set_reparse_data,
480 .set_short_name = ntfs_3g_set_short_name,
481 .set_security_descriptor = ntfs_3g_set_security_descriptor,
482 .set_timestamps = ntfs_3g_set_timestamps,
483 .abort_extract = ntfs_3g_finish_or_abort_extract,
484 .finish_extract = ntfs_3g_finish_or_abort_extract,
487 .path_prefix_nchars = 1,
488 .path_separator = '/',
491 .supports_case_sensitive_filenames = 1,
492 .root_directory_is_special = 1,
495 #endif /* WITH_NTFS_3G */