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,
53 u8 buf[min(entry->original_size, WIM_CHUNK_SIZE)];
54 u64 num_chunks = (entry->original_size + WIM_CHUNK_SIZE - 1) / WIM_CHUNK_SIZE;
55 u64 n = WIM_CHUNK_SIZE;
56 int res_ctype = wim_resource_compression_type(w, entry);
58 for (u64 i = 0; i < num_chunks; i++) {
59 DEBUG("Write chunk %u of %u", i + 1, num_chunks);
61 if (i == num_chunks - 1) {
62 n = entry->original_size % WIM_CHUNK_SIZE;
68 ret = read_resource(w->fp, entry->size, entry->original_size,
69 entry->offset, res_ctype, n, offset, buf);
73 if (ntfs_attr_pwrite(na, offset, n, buf) != n) {
74 ERROR("Failed to write to NTFS data stream");
75 return WIMLIB_ERR_WRITE;
82 /* Writes the data streams to a NTFS file
84 * @ni: The NTFS inode for the file.
85 * @dentry: The directory entry in the WIM file.
86 * @w: The WIMStruct for the WIM containing the image we are applying.
88 * Returns 0 on success, nonzero on failure.
90 static int write_ntfs_data_streams(ntfs_inode *ni, const struct dentry *dentry,
94 struct lookup_table_entry *lte;
98 DEBUG("Writing NTFS data streams for `%s'", dentry->full_path_utf8);
100 wimlib_assert(dentry->num_ads == 0);
102 lte = dentry_stream_lte(dentry, 0, w->lookup_table);
103 if (lte && lte->resource_entry.original_size != 0) {
105 na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
107 ERROR_WITH_ERRNO("Failed to open unnamed data stream of "
108 "extracted file `%s'",
109 dentry->full_path_utf8);
110 return WIMLIB_ERR_NTFS_3G;
112 ret = extract_resource_to_ntfs_attr(w, <e->resource_entry, na);
122 * Makes a NTFS hard link
124 * It is named @from_dentry->file_name and is located under the directory
125 * specified by @dir_ni, and it is made to point to the previously extracted
126 * file located at @to_dentry->extracted_file.
128 * Return 0 on success, nonzero on failure.
130 static int wim_apply_hardlink_ntfs(const struct dentry *from_dentry,
131 const struct dentry *to_dentry,
137 DEBUG("Extracting NTFS hard link `%s' => `%s'",
138 from_dentry->full_path_utf8, to_dentry->extracted_file);
140 to_ni = ntfs_pathname_to_inode(dir_ni->vol, NULL,
141 to_dentry->extracted_file);
143 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
144 to_dentry->extracted_file);
145 return WIMLIB_ERR_NTFS_3G;
147 ret = ntfs_link(to_ni, dir_ni,
148 (ntfschar*)from_dentry->file_name,
149 from_dentry->file_name_len / 2);
151 ERROR_WITH_ERRNO("Could not create hard link `%s' => `%s'",
152 from_dentry->full_path_utf8,
153 to_dentry->extracted_file);
154 ret = WIMLIB_ERR_NTFS_3G;
156 if (ntfs_inode_close(to_ni) != 0) {
157 ERROR_WITH_ERRNO("Failed to close NTFS inode for `%s'",
158 to_dentry->extracted_file);
159 ret = WIMLIB_ERR_NTFS_3G;
165 * Applies a WIM dentry to a NTFS filesystem.
167 * @dentry: The WIM dentry to apply
168 * @dir_ni: The NTFS inode for the parent directory
169 * @w: The WIMStruct for the WIM containing the image we are applying.
171 * @return: 0 on success; nonzero on failure.
173 static int do_wim_apply_dentry_ntfs(struct dentry *dentry, ntfs_inode *dir_ni,
180 if (dentry_is_directory(dentry)) {
184 const struct list_head *head = &dentry->link_group_list;
185 if (head->next != head) {
186 /* This dentry is one of a hard link set of at least 2
187 * dentries. If one of the other dentries has already
188 * been extracted, make a hard link to the file
189 * corresponding to this already-extracted directory.
190 * Otherwise, extract the file, and set the
191 * dentry->extracted_file field so that other dentries
192 * in the hard link group can link to it. */
193 struct dentry *other;
194 list_for_each_entry(other, head, link_group_list) {
195 if (other->extracted_file) {
196 return wim_apply_hardlink_ntfs(
197 dentry, other, dir_ni);
201 FREE(dentry->extracted_file);
202 dentry->extracted_file = STRDUP(dentry->full_path_utf8);
203 if (!dentry->extracted_file) {
204 ERROR("Failed to allocate memory for filename");
205 return WIMLIB_ERR_NOMEM;
210 * Create a directory or file.
212 * Note: if it's a reparse point (such as a symbolic link), we still
213 * pass S_IFREG here, since we manually set the reparse data later.
215 ni = ntfs_create(dir_ni, 0, (ntfschar*)dentry->file_name,
216 dentry->file_name_len / 2, type);
219 ERROR_WITH_ERRNO("Could not create NTFS object for `%s'",
220 dentry->full_path_utf8);
221 ret = WIMLIB_ERR_NTFS_3G;
225 /* Write the data streams, unless this is a directory or reparse point
227 if (!dentry_is_directory(dentry) &&
228 !(dentry->attributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
229 ret = write_ntfs_data_streams(ni, dentry, w);
234 DEBUG("Setting NTFS file attributes on `%s' to %#"PRIx32,
235 dentry->full_path_utf8, dentry->attributes);
237 if (!_ntfs_set_file_attributes(ni, dentry->attributes)) {
238 ERROR("Failed to set NTFS file attributes on `%s'",
239 dentry->full_path_utf8);
240 ret = WIMLIB_ERR_NTFS_3G;
244 if (dentry->security_id != -1) {
245 const struct wim_security_data *sd = wim_security_data(w);
246 wimlib_assert(dentry->security_id < sd->num_entries);
247 DEBUG("Applying security descriptor %d to `%s'",
248 dentry->security_id, dentry->full_path_utf8);
249 if (!_ntfs_set_file_security(ni->vol, ni, ~0,
250 sd->descriptors[dentry->security_id]))
252 ERROR_WITH_ERRNO("Failed to set security data on `%s'",
253 dentry->full_path_utf8);
254 ret = WIMLIB_ERR_NTFS_3G;
259 if (dentry->attributes & FILE_ATTR_REPARSE_POINT) {
260 struct lookup_table_entry *lte;
262 lte = dentry_first_lte(dentry, w->lookup_table);
264 ERROR("Could not find reparse data for `%s'",
265 dentry->full_path_utf8);
266 ret = WIMLIB_ERR_INVALID_DENTRY;
270 ret = ntfs_set_ntfs_reparse_data(ni, lte->symlink_buf,
271 lte->resource_entry.original_size,
274 ERROR_WITH_ERRNO("Failed to set NTFS reparse data on "
275 "`%s'", dentry->full_path_utf8);
276 ret = WIMLIB_ERR_NTFS_3G;
281 if (ntfs_inode_close_in_dir(ni, dir_ni) != 0) {
282 ERROR_WITH_ERRNO("Failed to close new inode");
283 ret = WIMLIB_ERR_NTFS_3G;
290 static int wim_apply_dentry_ntfs(struct dentry *dentry, void *arg)
292 struct ntfs_apply_args *args = arg;
293 ntfs_volume *vol = args->vol;
294 int extract_flags = args->extract_flags;
295 WIMStruct *w = args->w;
300 const char *dir_name;
302 wimlib_assert(dentry->full_path_utf8);
304 DEBUG("Applying dentry `%s' to NTFS", dentry->full_path_utf8);
306 if (dentry_is_root(dentry))
309 if (extract_flags & WIMLIB_EXTRACT_FLAG_VERBOSE)
310 puts(dentry->full_path_utf8);
312 p = dentry->full_path_utf8 + dentry->full_path_utf8_len;
319 dir_name = dentry->full_path_utf8;
321 dir_ni = ntfs_pathname_to_inode(vol, NULL, dir_name);
324 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
326 return WIMLIB_ERR_NTFS_3G;
328 DEBUG("Found NTFS inode for `%s'", dir_name);
330 ret = do_wim_apply_dentry_ntfs(dentry, dir_ni, w);
332 if (ntfs_inode_close(dir_ni) != 0) {
334 ret = WIMLIB_ERR_NTFS_3G;
335 ERROR_WITH_ERRNO("Failed to close directory inode");
341 static int do_wim_apply_image_ntfs(WIMStruct *w, const char *device, int extract_flags)
346 vol = ntfs_mount(device, 0);
348 ERROR_WITH_ERRNO("Failed to mount NTFS volume `%s'", device);
349 return WIMLIB_ERR_NTFS_3G;
351 struct ntfs_apply_args args = {
353 .extract_flags = extract_flags,
356 ret = for_dentry_in_tree(wim_root_dentry(w), wim_apply_dentry_ntfs,
358 if (ntfs_umount(vol, FALSE) != 0) {
359 ERROR_WITH_ERRNO("Failed to unmount NTFS volume");
361 ret = WIMLIB_ERR_NTFS_3G;
366 WIMLIBAPI int wimlib_apply_image_to_ntfs_volume(WIMStruct *w, int image,
367 const char *device, int flags)
372 return WIMLIB_ERR_INVALID_PARAM;
373 if (image == WIM_ALL_IMAGES) {
374 ERROR("Can only apply a single image when applying "
375 "directly to a NTFS volume");
376 return WIMLIB_ERR_INVALID_PARAM;
378 if (flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | WIMLIB_EXTRACT_FLAG_HARDLINK)) {
379 ERROR("Cannot specifcy symlink or hardlink flags when applying ");
380 ERROR("directly to a NTFS volume");
381 return WIMLIB_ERR_INVALID_PARAM;
383 ret = wimlib_select_image(w, image);
389 ERROR("We are not root, but NTFS-3g requires root privileges to set arbitrary");
390 ERROR("security data on the NTFS filesystem. Please run this program as root");
391 ERROR("if you want to extract a WIM image while preserving NTFS-specific");
392 ERROR("information.");
394 return WIMLIB_ERR_NOT_ROOT;
397 return do_wim_apply_image_ntfs(w, device, flags);
400 #else /* WITH_NTFS_3G */
401 WIMLIBAPI int wimlib_apply_image_to_ntfs_volume(WIMStruct *w, int image,
402 const char *device, int flags)
404 ERROR("wimlib was compiled without support for NTFS-3g, so");
405 ERROR("we cannot apply a WIM image directly to a NTFS volume");
406 return WIMLIB_ERR_UNSUPPORTED;
408 #endif /* WITH_NTFS_3G */