4 * Apply a WIM image to a NTFS volume, restoring everything we can, including
5 * security data and alternate data streams. There should be no loss of
10 * Copyright (C) 2012 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 Lesser General Public License as published by the Free
16 * Software Foundation; either version 2.1 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 Lesser General Public License for more
24 * You should have received a copy of the GNU Lesser General Public License
25 * along with wimlib; if not, see http://www.gnu.org/licenses/.
29 #include "wimlib_internal.h"
34 #include "lookup_table.h"
35 #include <ntfs-3g/layout.h>
36 #include <ntfs-3g/acls.h>
37 #include <ntfs-3g/attrib.h>
38 #include <ntfs-3g/misc.h>
39 #include <ntfs-3g/reparse.h>
40 #include <ntfs-3g/security.h>
41 #include <ntfs-3g/volume.h>
45 struct ntfs_apply_args {
51 extern int _ntfs_set_file_security(ntfs_volume *vol, ntfs_inode *ni,
52 u32 selection, const char *attr);
53 extern int _ntfs_set_file_attributes(ntfs_inode *ni, s32 attrib);
56 * Extracts a WIM resource to a NTFS attribute.
58 static int extract_resource_to_ntfs_attr(WIMStruct *w, const struct resource_entry *entry,
63 u8 buf[min(entry->original_size, WIM_CHUNK_SIZE)];
64 u64 num_chunks = (entry->original_size + WIM_CHUNK_SIZE - 1) / WIM_CHUNK_SIZE;
65 u64 n = WIM_CHUNK_SIZE;
66 int res_ctype = wim_resource_compression_type(w, entry);
68 for (u64 i = 0; i < num_chunks; i++) {
69 DEBUG("Write chunk %u of %u", i + 1, num_chunks);
71 if (i == num_chunks - 1) {
72 n = entry->original_size % WIM_CHUNK_SIZE;
78 ret = read_resource(w->fp, entry->size, entry->original_size,
79 entry->offset, res_ctype, n, offset, buf);
83 if (ntfs_attr_pwrite(na, offset, n, buf) != n) {
84 ERROR("Failed to write to NTFS data stream");
85 return WIMLIB_ERR_WRITE;
93 /* Writes the data streams to a NTFS file
95 * @ni: The NTFS inode for the file.
96 * @dentry: The directory entry in the WIM file.
97 * @w: The WIMStruct for the WIM containing the image we are applying.
99 * Returns 0 on success, nonzero on failure.
101 static int write_ntfs_data_streams(ntfs_inode *ni, const struct dentry *dentry,
105 struct lookup_table_entry *lte;
109 DEBUG("Writing NTFS data streams for `%s'", dentry->full_path_utf8);
111 wimlib_assert(dentry->num_ads == 0);
113 lte = dentry_stream_lte(dentry, 0, w->lookup_table);
114 if (lte && lte->resource_entry.original_size != 0) {
116 na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
118 ERROR_WITH_ERRNO("Failed to open unnamed data stream of "
119 "extracted file `%s'",
120 dentry->full_path_utf8);
121 return WIMLIB_ERR_NTFS_3G;
123 ret = extract_resource_to_ntfs_attr(w, <e->resource_entry, na);
133 * Makes a NTFS hard link
135 * It is named @from_dentry->file_name and is located under the directory
136 * specified by @dir_ni, and it is made to point to the previously extracted
137 * file located at @to_dentry->extracted_file.
139 * Return 0 on success, nonzero on failure.
141 static int wim_apply_hardlink_ntfs(const struct dentry *from_dentry,
142 const struct dentry *to_dentry,
148 DEBUG("Extracting NTFS hard link `%s' => `%s'",
149 from_dentry->full_path_utf8, to_dentry->extracted_file);
151 to_ni = ntfs_pathname_to_inode(dir_ni->vol, NULL,
152 to_dentry->extracted_file);
154 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
155 to_dentry->extracted_file);
156 return WIMLIB_ERR_NTFS_3G;
158 ret = ntfs_link(to_ni, dir_ni,
159 (ntfschar*)from_dentry->file_name,
160 from_dentry->file_name_len / 2);
162 ERROR_WITH_ERRNO("Could not create hard link `%s' => `%s'",
163 from_dentry->full_path_utf8,
164 to_dentry->extracted_file);
165 ret = WIMLIB_ERR_NTFS_3G;
167 if (ntfs_inode_close(to_ni) != 0) {
168 ERROR_WITH_ERRNO("Failed to close NTFS inode for `%s'",
169 to_dentry->extracted_file);
170 ret = WIMLIB_ERR_NTFS_3G;
176 * Applies a WIM dentry to a NTFS filesystem.
178 * @dentry: The WIM dentry to apply
179 * @dir_ni: The NTFS inode for the parent directory
180 * @w: The WIMStruct for the WIM containing the image we are applying.
182 * @return: 0 on success; nonzero on failure.
184 static int do_wim_apply_dentry_ntfs(struct dentry *dentry, ntfs_inode *dir_ni,
191 if (dentry_is_directory(dentry)) {
195 const struct list_head *head = &dentry->link_group_list;
196 if (head->next != head) {
197 /* This dentry is one of a hard link set of at least 2
198 * dentries. If one of the other dentries has already
199 * been extracted, make a hard link to the file
200 * corresponding to this already-extracted directory.
201 * Otherwise, extract the file, and set the
202 * dentry->extracted_file field so that other dentries
203 * in the hard link group can link to it. */
204 struct dentry *other;
205 list_for_each_entry(other, head, link_group_list) {
206 if (other->extracted_file) {
207 return wim_apply_hardlink_ntfs(
208 dentry, other, dir_ni);
212 FREE(dentry->extracted_file);
213 dentry->extracted_file = STRDUP(dentry->full_path_utf8);
214 if (!dentry->extracted_file) {
215 ERROR("Failed to allocate memory for filename");
216 return WIMLIB_ERR_NOMEM;
221 * Create a directory or file.
223 * Note: if it's a reparse point (such as a symbolic link), we still
224 * pass S_IFREG here, since we manually set the reparse data later.
226 ni = ntfs_create(dir_ni, 0, (ntfschar*)dentry->file_name,
227 dentry->file_name_len / 2, type);
230 ERROR_WITH_ERRNO("Could not create NTFS object for `%s'",
231 dentry->full_path_utf8);
232 ret = WIMLIB_ERR_NTFS_3G;
236 /* Write the data streams, unless this is a directory or reparse point
238 if (!dentry_is_directory(dentry) &&
239 !(dentry->attributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
240 ret = write_ntfs_data_streams(ni, dentry, w);
245 DEBUG("Setting NTFS file attributes on `%s' to %#"PRIx32,
246 dentry->full_path_utf8, dentry->attributes);
248 if (!_ntfs_set_file_attributes(ni, dentry->attributes)) {
249 ERROR("Failed to set NTFS file attributes on `%s'",
250 dentry->full_path_utf8);
251 ret = WIMLIB_ERR_NTFS_3G;
255 if (dentry->security_id != -1) {
256 const struct wim_security_data *sd = wim_security_data(w);
257 wimlib_assert(dentry->security_id < sd->num_entries);
258 DEBUG("Applying security descriptor %d to `%s'",
259 dentry->security_id, dentry->full_path_utf8);
260 if (!_ntfs_set_file_security(ni->vol, ni, ~0,
261 sd->descriptors[dentry->security_id]))
263 ERROR_WITH_ERRNO("Failed to set security data on `%s'",
264 dentry->full_path_utf8);
265 ret = WIMLIB_ERR_NTFS_3G;
270 if (dentry->attributes & FILE_ATTR_REPARSE_POINT) {
271 struct lookup_table_entry *lte;
273 lte = dentry_first_lte(dentry, w->lookup_table);
275 ERROR("Could not find reparse data for `%s'",
276 dentry->full_path_utf8);
277 ret = WIMLIB_ERR_INVALID_DENTRY;
281 char symlink_buf[wim_resource_size(lte)];
283 ret = read_full_wim_resource(lte, symlink_buf);
287 ret = ntfs_set_ntfs_reparse_data(ni, symlink_buf,
288 wim_resource_size(lte), 0);
290 ERROR_WITH_ERRNO("Failed to set NTFS reparse data on "
291 "`%s'", dentry->full_path_utf8);
292 ret = WIMLIB_ERR_NTFS_3G;
297 if (ntfs_inode_close_in_dir(ni, dir_ni) != 0) {
298 ERROR_WITH_ERRNO("Failed to close new inode");
299 ret = WIMLIB_ERR_NTFS_3G;
306 static int wim_apply_dentry_ntfs(struct dentry *dentry, void *arg)
308 struct ntfs_apply_args *args = arg;
309 ntfs_volume *vol = args->vol;
310 int extract_flags = args->extract_flags;
311 WIMStruct *w = args->w;
316 const char *dir_name;
318 wimlib_assert(dentry->full_path_utf8);
320 DEBUG("Applying dentry `%s' to NTFS", dentry->full_path_utf8);
322 if (dentry_is_root(dentry))
325 if (extract_flags & WIMLIB_EXTRACT_FLAG_VERBOSE)
326 puts(dentry->full_path_utf8);
328 p = dentry->full_path_utf8 + dentry->full_path_utf8_len;
335 dir_name = dentry->full_path_utf8;
337 dir_ni = ntfs_pathname_to_inode(vol, NULL, dir_name);
340 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
342 return WIMLIB_ERR_NTFS_3G;
344 DEBUG("Found NTFS inode for `%s'", dir_name);
346 ret = do_wim_apply_dentry_ntfs(dentry, dir_ni, w);
348 if (ntfs_inode_close(dir_ni) != 0) {
350 ret = WIMLIB_ERR_NTFS_3G;
351 ERROR_WITH_ERRNO("Failed to close directory inode");
357 static int do_wim_apply_image_ntfs(WIMStruct *w, const char *device, int extract_flags)
362 vol = ntfs_mount(device, 0);
364 ERROR_WITH_ERRNO("Failed to mount NTFS volume `%s'", device);
365 return WIMLIB_ERR_NTFS_3G;
367 struct ntfs_apply_args args = {
369 .extract_flags = extract_flags,
372 ret = for_dentry_in_tree(wim_root_dentry(w), wim_apply_dentry_ntfs,
374 if (ntfs_umount(vol, FALSE) != 0) {
375 ERROR_WITH_ERRNO("Failed to unmount NTFS volume");
377 ret = WIMLIB_ERR_NTFS_3G;
384 * API entry point for applying a WIM image to a NTFS volume.
386 * Please note that this is a NTFS *volume* and not a directory. The intention
387 * is that the volume contain an empty filesystem, and the WIM image contain a
388 * full filesystem to be applied to the volume.
390 WIMLIBAPI int wimlib_apply_image_to_ntfs_volume(WIMStruct *w, int image,
391 const char *device, int flags)
396 return WIMLIB_ERR_INVALID_PARAM;
397 if (image == WIM_ALL_IMAGES) {
398 ERROR("Can only apply a single image when applying "
399 "directly to a NTFS volume");
400 return WIMLIB_ERR_INVALID_PARAM;
402 if (flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | WIMLIB_EXTRACT_FLAG_HARDLINK)) {
403 ERROR("Cannot specify symlink or hardlink flags when applying ");
404 ERROR("directly to a NTFS volume");
405 return WIMLIB_ERR_INVALID_PARAM;
407 ret = wimlib_select_image(w, image);
413 ERROR("We are not root, but NTFS-3g requires root privileges to set arbitrary");
414 ERROR("security data on the NTFS filesystem. Please run this program as root");
415 ERROR("if you want to extract a WIM image while preserving NTFS-specific");
416 ERROR("information.");
418 return WIMLIB_ERR_NOT_ROOT;
421 return do_wim_apply_image_ntfs(w, device, flags);
424 #else /* WITH_NTFS_3G */
425 WIMLIBAPI int wimlib_apply_image_to_ntfs_volume(WIMStruct *w, int image,
426 const char *device, int flags)
428 ERROR("wimlib was compiled without support for NTFS-3g, so");
429 ERROR("we cannot apply a WIM image directly to a NTFS volume");
430 return WIMLIB_ERR_UNSUPPORTED;
432 #endif /* WITH_NTFS_3G */