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, mode_t mode)
109 ntfs_inode *dir_ni, *ni;
111 utf16lechar *name_utf16le;
112 size_t name_utf16le_nbytes;
115 vol = ntfs_3g_apply_ctx_get_volume(ctx);
117 ret = WIMLIB_ERR_NTFS_3G;
118 dir_ni = ntfs_3g_open_parent_inode(path, vol);
122 name = path_basename(path);
123 ret = tstr_to_utf16le(name, strlen(name),
124 &name_utf16le, &name_utf16le_nbytes);
126 goto out_close_dir_ni;
129 ni = ntfs_create(dir_ni, 0, name_utf16le,
130 name_utf16le_nbytes / 2, mode);
131 if (!ni || ntfs_inode_close_in_dir(ni, dir_ni))
132 ret = WIMLIB_ERR_NTFS_3G;
135 if (ntfs_inode_close(dir_ni))
136 ret = WIMLIB_ERR_NTFS_3G;
142 ntfs_3g_create_file(const char *path, struct apply_ctx *ctx)
144 return ntfs_3g_create(path, ctx, S_IFREG);
148 ntfs_3g_create_directory(const char *path, struct apply_ctx *ctx)
150 return ntfs_3g_create(path, ctx, S_IFDIR);
154 ntfs_3g_create_hardlink(const char *oldpath, const char *newpath,
155 struct apply_ctx *ctx)
158 ntfs_inode *dir_ni, *ni;
160 utf16lechar *name_utf16le;
161 size_t name_utf16le_nbytes;
164 vol = ntfs_3g_apply_ctx_get_volume(ctx);
166 ret = WIMLIB_ERR_NTFS_3G;
167 ni = ntfs_pathname_to_inode(vol, NULL, oldpath);
171 ret = WIMLIB_ERR_NTFS_3G;
172 dir_ni = ntfs_3g_open_parent_inode(newpath, vol);
176 name = path_basename(newpath);
177 ret = tstr_to_utf16le(name, strlen(name),
178 &name_utf16le, &name_utf16le_nbytes);
180 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 *raw_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;
209 if (stream_name_nchars == 0) {
210 stream_name = AT_UNNAMED;
212 stream_name = alloca((stream_name_nchars + 1) * sizeof(utf16lechar));
213 memcpy(stream_name, raw_stream_name,
214 stream_name_nchars * sizeof(utf16lechar));
215 stream_name[stream_name_nchars] = 0;
219 if (!stream_name_nchars && !lte)
222 /* Look up NTFS inode to which to extract the stream. */
223 ret = WIMLIB_ERR_NTFS_3G;
224 ni = ntfs_3g_apply_pathname_to_inode(path, ctx);
228 /* Add the stream if it's not the default (unnamed) stream. */
229 ret = WIMLIB_ERR_NTFS_3G;
230 if (stream_name_nchars)
231 if (ntfs_attr_add(ni, AT_DATA, stream_name,
232 stream_name_nchars, NULL, 0))
235 /* If stream is empty, no need to open and extract it. */
240 /* Open the stream (NTFS attribute). */
241 ret = WIMLIB_ERR_NTFS_3G;
242 na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_nchars);
246 /* (Optional) Immediately resize attribute to size of stream. */
247 ret = WIMLIB_ERR_NTFS_3G;
248 if (ntfs_attr_truncate_solid(na, wim_resource_size(lte)))
251 /* Extract stream data to the NTFS attribute. */
253 extract_ctx.offset = 0;
254 ret = extract_wim_resource(lte, wim_resource_size(lte),
255 ntfs_3g_extract_wim_chunk, &extract_ctx);
256 /* Clean up and return. */
260 if (ntfs_inode_close(ni))
261 ret = WIMLIB_ERR_NTFS_3G;
269 ntfs_3g_extract_unnamed_stream(const char *path,
270 struct wim_lookup_table_entry *lte,
271 struct apply_ctx *ctx)
273 return ntfs_3g_extract_stream(path, NULL, 0, lte, ctx);
277 ntfs_3g_extract_named_stream(const char *path, const utf16lechar *stream_name,
278 size_t stream_name_nchars,
279 struct wim_lookup_table_entry *lte, struct apply_ctx *ctx)
281 return ntfs_3g_extract_stream(path, stream_name,
282 stream_name_nchars, lte, ctx);
286 ntfs_3g_set_file_attributes(const char *path, u32 attributes,
287 struct apply_ctx *ctx)
292 ni = ntfs_3g_apply_pathname_to_inode(path, ctx);
294 return WIMLIB_ERR_NTFS_3G;
295 if (ntfs_set_ntfs_attrib(ni, (const char*)&attributes, sizeof(u32), 0))
296 ret = WIMLIB_ERR_NTFS_3G;
297 if (ntfs_inode_close(ni))
298 ret = WIMLIB_ERR_NTFS_3G;
303 ntfs_3g_set_reparse_data(const char *path, const u8 *rpbuf, u16 rpbuflen,
304 struct apply_ctx *ctx)
309 ni = ntfs_3g_apply_pathname_to_inode(path, ctx);
311 return WIMLIB_ERR_NTFS_3G;
312 if (ntfs_set_ntfs_reparse_data(ni, rpbuf, rpbuflen, 0))
313 ret = WIMLIB_ERR_NTFS_3G;
314 if (ntfs_inode_close(ni))
315 ret = WIMLIB_ERR_NTFS_3G;
320 ntfs_3g_set_short_name(const char *path, const utf16lechar *short_name,
321 size_t short_name_nchars, struct apply_ctx *ctx)
323 ntfs_inode *ni, *dir_ni;
326 char *dosname = NULL;
327 size_t dosname_nbytes;
330 if (short_name_nchars == 0)
333 vol = ntfs_3g_apply_ctx_get_volume(ctx);
335 ret = WIMLIB_ERR_NTFS_3G;
336 dir_ni = ntfs_3g_open_parent_inode(path, vol);
340 ret = WIMLIB_ERR_NTFS_3G;
341 ni = ntfs_pathname_to_inode(vol, NULL, path);
343 goto out_close_dir_ni;
345 ret = utf16le_to_tstr(short_name, short_name_nchars * 2,
346 &dosname, &dosname_nbytes);
351 if (ntfs_set_ntfs_dos_name(ni, dir_ni, dosname,
353 ret = WIMLIB_ERR_NTFS_3G;
354 /* ntfs_set_ntfs_dos_name() always closes the inodes. */
358 if (ntfs_inode_close_in_dir(ni, dir_ni))
359 ret = WIMLIB_ERR_NTFS_3G;
361 if (ntfs_inode_close(dir_ni))
362 ret = WIMLIB_ERR_NTFS_3G;
368 ntfs_3g_set_security_descriptor(const char *path, const u8 *desc, size_t desc_size,
369 struct apply_ctx *ctx, bool strict)
373 struct SECURITY_CONTEXT sec_ctx;
376 vol = ntfs_3g_apply_ctx_get_volume(ctx);
378 ni = ntfs_pathname_to_inode(vol, NULL, path);
380 return WIMLIB_ERR_NTFS_3G;
382 memset(&sec_ctx, 0, sizeof(sec_ctx));
385 if (ntfs_set_ntfs_acl(&sec_ctx, ni, desc, desc_size, 0))
386 ret = WIMLIB_ERR_NTFS_3G;
387 if (ntfs_inode_close(ni))
388 ret = WIMLIB_ERR_NTFS_3G;
393 ntfs_3g_set_timestamps(const char *path, u64 creation_time,
394 u64 last_write_time, u64 last_access_time,
395 struct apply_ctx *ctx)
397 u64 ntfs_timestamps[3];
401 ni = ntfs_3g_apply_pathname_to_inode(path, ctx);
403 return WIMLIB_ERR_NTFS_3G;
405 /* Note: ntfs_inode_set_times() expects the times in native byte order,
406 * not little endian. */
407 ntfs_timestamps[0] = creation_time;
408 ntfs_timestamps[1] = last_write_time;
409 ntfs_timestamps[2] = last_access_time;
411 if (ntfs_inode_set_times(ni, (const char*)ntfs_timestamps,
412 sizeof(ntfs_timestamps), 0))
413 ret = WIMLIB_ERR_NTFS_3G;
414 if (ntfs_inode_close(ni))
415 ret = WIMLIB_ERR_NTFS_3G;
420 ntfs_3g_target_is_root(const char *target)
422 /* We always extract to the root of the NTFS volume. */
427 ntfs_3g_start_extract(const char *path, struct apply_ctx *ctx)
431 vol = ntfs_mount(ctx->target, 0);
433 ERROR_WITH_ERRNO("Failed to mount \"%"TS"\" with NTFS-3g", ctx->target);
434 return WIMLIB_ERR_NTFS_3G;
436 ntfs_3g_apply_ctx_set_volume(ctx, vol);
438 ctx->supported_features.archive_files = 1;
439 ctx->supported_features.hidden_files = 1;
440 ctx->supported_features.system_files = 1;
441 ctx->supported_features.compressed_files = 1;
442 ctx->supported_features.encrypted_files = 0;
443 ctx->supported_features.not_context_indexed_files = 1;
444 ctx->supported_features.sparse_files = 1;
445 ctx->supported_features.named_data_streams = 1;
446 ctx->supported_features.hard_links = 1;
447 ctx->supported_features.reparse_points = 1;
448 ctx->supported_features.security_descriptors = 1;
449 ctx->supported_features.short_names = 1;
454 ntfs_3g_finish_or_abort_extract(struct apply_ctx *ctx)
458 vol = ntfs_3g_apply_ctx_get_volume(ctx);
459 if (ntfs_umount(vol, FALSE)) {
460 ERROR_WITH_ERRNO("Failed to unmount \"%"TS"\" with NTFS-3g",
462 return WIMLIB_ERR_NTFS_3G;
468 libntfs3g_global_init(void)
470 ntfs_set_char_encoding(setlocale(LC_ALL, ""));
473 const struct apply_operations ntfs_3g_apply_ops = {
476 .target_is_root = ntfs_3g_target_is_root,
477 .start_extract = ntfs_3g_start_extract,
478 .create_file = ntfs_3g_create_file,
479 .create_directory = ntfs_3g_create_directory,
480 .create_hardlink = ntfs_3g_create_hardlink,
481 .extract_unnamed_stream = ntfs_3g_extract_unnamed_stream,
482 .extract_named_stream = ntfs_3g_extract_named_stream,
483 .set_file_attributes = ntfs_3g_set_file_attributes,
484 .set_reparse_data = ntfs_3g_set_reparse_data,
485 .set_short_name = ntfs_3g_set_short_name,
486 .set_security_descriptor = ntfs_3g_set_security_descriptor,
487 .set_timestamps = ntfs_3g_set_timestamps,
488 .abort_extract = ntfs_3g_finish_or_abort_extract,
489 .finish_extract = ntfs_3g_finish_or_abort_extract,
492 .path_prefix_nchars = 1,
493 .path_separator = '/',
496 .supports_case_sensitive_filenames = 1,
497 .root_directory_is_special = 1,
500 #endif /* WITH_NTFS_3G */