2 * Copyright (C) 2012 Eric Biggers
4 * This file is part of wimlib, a library for working with WIM files.
6 * wimlib is free software; you can redistribute it and/or modify it under the
7 * terms of the GNU Lesser General Public License as published by the Free
8 * Software Foundation; either version 2.1 of the License, or (at your option)
11 * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13 * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with wimlib; if not, see http://www.gnu.org/licenses/.
21 #include "wimlib_internal.h"
26 #include "lookup_table.h"
27 #include <ntfs-3g/layout.h>
28 #include <ntfs-3g/acls.h>
29 #include <ntfs-3g/attrib.h>
30 #include <ntfs-3g/misc.h>
31 #include <ntfs-3g/reparse.h>
32 #include <ntfs-3g/security.h>
33 #include <ntfs-3g/volume.h>
37 struct ntfs_apply_args {
43 extern int _ntfs_set_file_security(ntfs_volume *vol, ntfs_inode *ni,
44 u32 selection, const char *attr);
45 extern int _ntfs_set_file_attributes(ntfs_inode *ni, s32 attrib);
48 * Extracts a WIM resource to a NTFS attribute.
50 static int extract_resource_to_ntfs_attr(WIMStruct *w, const struct resource_entry *entry,
55 u8 buf[min(entry->original_size, WIM_CHUNK_SIZE)];
56 u64 num_chunks = (entry->original_size + WIM_CHUNK_SIZE - 1) / WIM_CHUNK_SIZE;
57 u64 n = WIM_CHUNK_SIZE;
58 int res_ctype = wim_resource_compression_type(w, entry);
60 for (u64 i = 0; i < num_chunks; i++) {
61 DEBUG("Write chunk %u of %u", i + 1, num_chunks);
63 if (i == num_chunks - 1) {
64 n = entry->original_size % WIM_CHUNK_SIZE;
70 ret = read_resource(w->fp, entry->size, entry->original_size,
71 entry->offset, res_ctype, n, offset, buf);
75 if (ntfs_attr_pwrite(na, offset, n, buf) != n) {
76 ERROR("Failed to write to NTFS data stream");
77 return WIMLIB_ERR_WRITE;
85 /* Writes the data streams to a NTFS file
87 * @ni: The NTFS inode for the file.
88 * @dentry: The directory entry in the WIM file.
89 * @w: The WIMStruct for the WIM containing the image we are applying.
91 * Returns 0 on success, nonzero on failure.
93 static int write_ntfs_data_streams(ntfs_inode *ni, const struct dentry *dentry,
97 struct lookup_table_entry *lte;
101 DEBUG("Writing NTFS data streams for `%s'", dentry->full_path_utf8);
103 wimlib_assert(dentry->num_ads == 0);
105 lte = dentry_stream_lte(dentry, 0, w->lookup_table);
106 if (lte && lte->resource_entry.original_size != 0) {
108 na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
110 ERROR_WITH_ERRNO("Failed to open unnamed data stream of "
111 "extracted file `%s'",
112 dentry->full_path_utf8);
113 return WIMLIB_ERR_NTFS_3G;
115 ret = extract_resource_to_ntfs_attr(w, <e->resource_entry, na);
125 * Makes a NTFS hard link
127 * It is named @from_dentry->file_name and is located under the directory
128 * specified by @dir_ni, and it is made to point to the previously extracted
129 * file located at @to_dentry->extracted_file.
131 * Return 0 on success, nonzero on failure.
133 static int wim_apply_hardlink_ntfs(const struct dentry *from_dentry,
134 const struct dentry *to_dentry,
140 DEBUG("Extracting NTFS hard link `%s' => `%s'",
141 from_dentry->full_path_utf8, to_dentry->extracted_file);
143 to_ni = ntfs_pathname_to_inode(dir_ni->vol, NULL,
144 to_dentry->extracted_file);
146 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
147 to_dentry->extracted_file);
148 return WIMLIB_ERR_NTFS_3G;
150 ret = ntfs_link(to_ni, dir_ni,
151 (ntfschar*)from_dentry->file_name,
152 from_dentry->file_name_len / 2);
154 ERROR_WITH_ERRNO("Could not create hard link `%s' => `%s'",
155 from_dentry->full_path_utf8,
156 to_dentry->extracted_file);
157 ret = WIMLIB_ERR_NTFS_3G;
159 if (ntfs_inode_close(to_ni) != 0) {
160 ERROR_WITH_ERRNO("Failed to close NTFS inode for `%s'",
161 to_dentry->extracted_file);
162 ret = WIMLIB_ERR_NTFS_3G;
168 * Applies a WIM dentry to a NTFS filesystem.
170 * @dentry: The WIM dentry to apply
171 * @dir_ni: The NTFS inode for the parent directory
172 * @w: The WIMStruct for the WIM containing the image we are applying.
174 * @return: 0 on success; nonzero on failure.
176 static int do_wim_apply_dentry_ntfs(struct dentry *dentry, ntfs_inode *dir_ni,
183 if (dentry_is_directory(dentry)) {
187 const struct list_head *head = &dentry->link_group_list;
188 if (head->next != head) {
189 /* This dentry is one of a hard link set of at least 2
190 * dentries. If one of the other dentries has already
191 * been extracted, make a hard link to the file
192 * corresponding to this already-extracted directory.
193 * Otherwise, extract the file, and set the
194 * dentry->extracted_file field so that other dentries
195 * in the hard link group can link to it. */
196 struct dentry *other;
197 list_for_each_entry(other, head, link_group_list) {
198 if (other->extracted_file) {
199 return wim_apply_hardlink_ntfs(
200 dentry, other, dir_ni);
204 FREE(dentry->extracted_file);
205 dentry->extracted_file = STRDUP(dentry->full_path_utf8);
206 if (!dentry->extracted_file) {
207 ERROR("Failed to allocate memory for filename");
208 return WIMLIB_ERR_NOMEM;
213 * Create a directory or file.
215 * Note: if it's a reparse point (such as a symbolic link), we still
216 * pass S_IFREG here, since we manually set the reparse data later.
218 ni = ntfs_create(dir_ni, 0, (ntfschar*)dentry->file_name,
219 dentry->file_name_len / 2, type);
222 ERROR_WITH_ERRNO("Could not create NTFS object for `%s'",
223 dentry->full_path_utf8);
224 ret = WIMLIB_ERR_NTFS_3G;
228 /* Write the data streams, unless this is a directory or reparse point
230 if (!dentry_is_directory(dentry) &&
231 !(dentry->attributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
232 ret = write_ntfs_data_streams(ni, dentry, w);
237 DEBUG("Setting NTFS file attributes on `%s' to %#"PRIx32,
238 dentry->full_path_utf8, dentry->attributes);
240 if (!_ntfs_set_file_attributes(ni, dentry->attributes)) {
241 ERROR("Failed to set NTFS file attributes on `%s'",
242 dentry->full_path_utf8);
243 ret = WIMLIB_ERR_NTFS_3G;
247 if (dentry->security_id != -1) {
248 const struct wim_security_data *sd = wim_security_data(w);
249 wimlib_assert(dentry->security_id < sd->num_entries);
250 DEBUG("Applying security descriptor %d to `%s'",
251 dentry->security_id, dentry->full_path_utf8);
252 if (!_ntfs_set_file_security(ni->vol, ni, ~0,
253 sd->descriptors[dentry->security_id]))
255 ERROR_WITH_ERRNO("Failed to set security data on `%s'",
256 dentry->full_path_utf8);
257 ret = WIMLIB_ERR_NTFS_3G;
262 if (dentry->attributes & FILE_ATTR_REPARSE_POINT) {
263 struct lookup_table_entry *lte;
265 lte = dentry_first_lte(dentry, w->lookup_table);
267 ERROR("Could not find reparse data for `%s'",
268 dentry->full_path_utf8);
269 ret = WIMLIB_ERR_INVALID_DENTRY;
273 char symlink_buf[wim_resource_size(lte)];
275 ret = read_full_wim_resource(lte, symlink_buf);
279 ret = ntfs_set_ntfs_reparse_data(ni, symlink_buf,
280 wim_resource_size(lte), 0);
282 ERROR_WITH_ERRNO("Failed to set NTFS reparse data on "
283 "`%s'", dentry->full_path_utf8);
284 ret = WIMLIB_ERR_NTFS_3G;
289 if (ntfs_inode_close_in_dir(ni, dir_ni) != 0) {
290 ERROR_WITH_ERRNO("Failed to close new inode");
291 ret = WIMLIB_ERR_NTFS_3G;
298 static int wim_apply_dentry_ntfs(struct dentry *dentry, void *arg)
300 struct ntfs_apply_args *args = arg;
301 ntfs_volume *vol = args->vol;
302 int extract_flags = args->extract_flags;
303 WIMStruct *w = args->w;
308 const char *dir_name;
310 wimlib_assert(dentry->full_path_utf8);
312 DEBUG("Applying dentry `%s' to NTFS", dentry->full_path_utf8);
314 if (dentry_is_root(dentry))
317 if (extract_flags & WIMLIB_EXTRACT_FLAG_VERBOSE)
318 puts(dentry->full_path_utf8);
320 p = dentry->full_path_utf8 + dentry->full_path_utf8_len;
327 dir_name = dentry->full_path_utf8;
329 dir_ni = ntfs_pathname_to_inode(vol, NULL, dir_name);
332 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
334 return WIMLIB_ERR_NTFS_3G;
336 DEBUG("Found NTFS inode for `%s'", dir_name);
338 ret = do_wim_apply_dentry_ntfs(dentry, dir_ni, w);
340 if (ntfs_inode_close(dir_ni) != 0) {
342 ret = WIMLIB_ERR_NTFS_3G;
343 ERROR_WITH_ERRNO("Failed to close directory inode");
349 static int do_wim_apply_image_ntfs(WIMStruct *w, const char *device, int extract_flags)
354 vol = ntfs_mount(device, 0);
356 ERROR_WITH_ERRNO("Failed to mount NTFS volume `%s'", device);
357 return WIMLIB_ERR_NTFS_3G;
359 struct ntfs_apply_args args = {
361 .extract_flags = extract_flags,
364 ret = for_dentry_in_tree(wim_root_dentry(w), wim_apply_dentry_ntfs,
366 if (ntfs_umount(vol, FALSE) != 0) {
367 ERROR_WITH_ERRNO("Failed to unmount NTFS volume");
369 ret = WIMLIB_ERR_NTFS_3G;
374 WIMLIBAPI int wimlib_apply_image_to_ntfs_volume(WIMStruct *w, int image,
375 const char *device, int flags)
380 return WIMLIB_ERR_INVALID_PARAM;
381 if (image == WIM_ALL_IMAGES) {
382 ERROR("Can only apply a single image when applying "
383 "directly to a NTFS volume");
384 return WIMLIB_ERR_INVALID_PARAM;
386 if (flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | WIMLIB_EXTRACT_FLAG_HARDLINK)) {
387 ERROR("Cannot specifcy symlink or hardlink flags when applying ");
388 ERROR("directly to a NTFS volume");
389 return WIMLIB_ERR_INVALID_PARAM;
391 ret = wimlib_select_image(w, image);
397 ERROR("We are not root, but NTFS-3g requires root privileges to set arbitrary");
398 ERROR("security data on the NTFS filesystem. Please run this program as root");
399 ERROR("if you want to extract a WIM image while preserving NTFS-specific");
400 ERROR("information.");
402 return WIMLIB_ERR_NOT_ROOT;
405 return do_wim_apply_image_ntfs(w, device, flags);
408 #else /* WITH_NTFS_3G */
409 WIMLIBAPI int wimlib_apply_image_to_ntfs_volume(WIMStruct *w, int image,
410 const char *device, int flags)
412 ERROR("wimlib was compiled without support for NTFS-3g, so");
413 ERROR("we cannot apply a WIM image directly to a NTFS volume");
414 return WIMLIB_ERR_UNSUPPORTED;
416 #endif /* WITH_NTFS_3G */