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);
47 static int extract_resource_to_ntfs_attr(WIMStruct *w, const struct resource_entry *entry,
50 u8 buf[min(entry->original_size, WIM_CHUNK_SIZE)];
51 u64 num_chunks = (entry->original_size + WIM_CHUNK_SIZE - 1) / WIM_CHUNK_SIZE;
52 u64 n = WIM_CHUNK_SIZE;
53 int res_ctype = wim_resource_compression_type(w, entry);
55 for (u64 i = 0; i < num_chunks; i++) {
56 DEBUG("Write chunk %u of %u", i + 1, num_chunks);
58 if (i == num_chunks - 1) {
59 n = entry->original_size % WIM_CHUNK_SIZE;
65 ret = read_resource(w->fp, entry->size, entry->original_size,
66 entry->offset, res_ctype, n, offset, buf);
70 if (ntfs_attr_pwrite(na, offset, n, buf) != n) {
71 ERROR("Failed to write to NTFS data stream");
72 return WIMLIB_ERR_WRITE;
79 static int write_ntfs_data_streams(ntfs_inode *ni, const struct dentry *dentry,
83 struct lookup_table_entry *lte;
87 DEBUG("Writing NTFS data streams for `%s'", dentry->full_path_utf8);
89 wimlib_assert(dentry->num_ads == 0);
91 lte = dentry_stream_lte(dentry, 0, w->lookup_table);
92 if (lte && lte->resource_entry.original_size != 0) {
94 na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
96 ERROR_WITH_ERRNO("Failed to open unnamed data stream of "
97 "extracted file `%s'",
98 dentry->full_path_utf8);
99 return WIMLIB_ERR_NTFS_3G;
101 ret = extract_resource_to_ntfs_attr(w, <e->resource_entry, na);
111 * Applies a WIM dentry to a NTFS filesystem.
113 * @dentry: The WIM dentry to apply
114 * @dir_ni: The NTFS inode for the parent directory
115 * @w: The WIMStruct for the WIM containing the image we are applying.
117 * @return: 0 on success; nonzero on failure.
119 static int __ntfs_apply_dentry(struct dentry *dentry, ntfs_inode *dir_ni,
126 print_dentry(dentry, w->lookup_table);
128 if (dentry_is_directory(dentry))
133 ni = ntfs_create(dir_ni, 0, (ntfschar*)dentry->file_name,
134 dentry->file_name_len / 2, type);
137 ERROR_WITH_ERRNO("Could not create NTFS object for `%s'",
138 dentry->full_path_utf8);
139 ret = WIMLIB_ERR_NTFS_3G;
143 if (!dentry_is_directory(dentry) &&
144 !(dentry->attributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
145 ret = write_ntfs_data_streams(ni, dentry, w);
150 DEBUG("Setting file attributes 0x%x on `%s'",
152 dentry->full_path_utf8);
154 if (!_ntfs_set_file_attributes(ni, dentry->attributes)) {
155 ERROR("Failed to set NTFS file attributes on `%s'",
156 dentry->full_path_utf8);
157 ret = WIMLIB_ERR_NTFS_3G;
161 if (dentry->security_id != -1) {
162 const struct wim_security_data *sd = wim_security_data(w);
163 wimlib_assert(dentry->security_id < sd->num_entries);
164 DEBUG("Applying security descriptor %d to `%s'",
165 dentry->security_id, dentry->full_path_utf8);
166 if (!_ntfs_set_file_security(ni->vol, ni, ~0,
167 sd->descriptors[dentry->security_id]))
169 ERROR_WITH_ERRNO("Failed to set security data on `%s'",
170 dentry->full_path_utf8);
171 ret = WIMLIB_ERR_NTFS_3G;
176 if (dentry->attributes & FILE_ATTR_REPARSE_POINT) {
177 struct lookup_table_entry *lte;
179 lte = dentry_first_lte(dentry, w->lookup_table);
181 ERROR("Could not find reparse data for `%s'",
182 dentry->full_path_utf8);
183 ret = WIMLIB_ERR_INVALID_DENTRY;
187 ret = ntfs_set_ntfs_reparse_data(ni, lte->symlink_buf,
188 lte->resource_entry.original_size,
191 ERROR_WITH_ERRNO("Failed to set NTFS reparse data on "
192 "`%s'", dentry->full_path_utf8);
193 ret = WIMLIB_ERR_NTFS_3G;
198 if (ntfs_inode_close_in_dir(ni, dir_ni) != 0) {
199 ERROR_WITH_ERRNO("Failed to close new inode");
200 ret = WIMLIB_ERR_NTFS_3G;
207 static int ntfs_apply_dentry(struct dentry *dentry, void *arg)
209 struct ntfs_apply_args *args = arg;
210 ntfs_volume *vol = args->vol;
211 int extract_flags = args->extract_flags;
212 WIMStruct *w = args->w;
216 DEBUG("Applying `%s'", dentry->full_path_utf8);
218 if (dentry_is_root(dentry))
221 if (extract_flags & WIMLIB_EXTRACT_FLAG_VERBOSE) {
222 wimlib_assert(dentry->full_path_utf8);
223 puts(dentry->full_path_utf8);
226 char *p = dentry->full_path_utf8 + dentry->full_path_utf8_len;
233 const char *dir_name = dentry->full_path_utf8;
235 dir_ni = ntfs_pathname_to_inode(vol, NULL, dir_name);
238 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
240 return WIMLIB_ERR_NTFS_3G;
242 DEBUG("Found NTFS inode for `%s'", dir_name);
244 ret = __ntfs_apply_dentry(dentry, dir_ni, w);
246 if (ntfs_inode_close(dir_ni) != 0) {
248 ret = WIMLIB_ERR_NTFS_3G;
249 ERROR_WITH_ERRNO("Failed to close directory inode");
255 static int do_ntfs_apply(WIMStruct *w, const char *device, int extract_flags)
260 vol = ntfs_mount(device, 0);
262 ERROR_WITH_ERRNO("Failed to mount NTFS volume `%s'", device);
263 return WIMLIB_ERR_NTFS_3G;
265 struct ntfs_apply_args args = {
267 .extract_flags = extract_flags,
270 ret = for_dentry_in_tree(wim_root_dentry(w), ntfs_apply_dentry,
272 if (ntfs_umount(vol, FALSE) != 0) {
273 ERROR_WITH_ERRNO("Failed to unmount NTFS volume");
275 ret = WIMLIB_ERR_NTFS_3G;
280 WIMLIBAPI int wimlib_apply_image_to_ntfs_volume(WIMStruct *w, int image,
281 const char *device, int flags)
286 return WIMLIB_ERR_INVALID_PARAM;
287 if (image == WIM_ALL_IMAGES) {
288 ERROR("Can only apply a single image when applying "
289 "directly to a NTFS volume");
290 return WIMLIB_ERR_INVALID_PARAM;
292 if (flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | WIMLIB_EXTRACT_FLAG_HARDLINK)) {
293 ERROR("Cannot specifcy symlink or hardlink flags when applying ");
294 ERROR("directly to a NTFS volume");
295 return WIMLIB_ERR_INVALID_PARAM;
297 ret = wimlib_select_image(w, image);
303 ERROR("We are not root, but NTFS-3g requires root privileges to set arbitrary");
304 ERROR("security data on the NTFS filesystem. Please run this program as root");
305 ERROR("if you want to extract a WIM image while preserving NTFS-specific");
306 ERROR("information.");
308 return WIMLIB_ERR_NOT_ROOT;
311 return do_ntfs_apply(w, device, flags);
314 #else /* WITH_NTFS_3G */
315 WIMLIBAPI int wimlib_apply_image_to_ntfs_volume(WIMStruct *w, int image,
316 const char *device, int flags)
318 ERROR("wimlib was compiled without support for NTFS-3g, so");
319 ERROR("we cannot apply a WIM image directly to a NTFS volume");
320 return WIMLIB_ERR_UNSUPPORTED;
322 #endif /* WITH_NTFS_3G */