]> wimlib.net Git - wimlib/blob - src/ntfs-apply.c
write_ntfs_data_streams(): Reserve space for streams
[wimlib] / src / ntfs-apply.c
1 /*
2  * ntfs-apply.c
3  *
4  * Apply a WIM image to a NTFS volume.  We restore everything we can, including
5  * security data and alternate data streams.
6  */
7
8 /*
9  * Copyright (C) 2012 Eric Biggers
10  *
11  * This file is part of wimlib, a library for working with WIM files.
12  *
13  * wimlib is free software; you can redistribute it and/or modify it under the
14  * terms of the GNU General Public License as published by the Free
15  * Software Foundation; either version 3 of the License, or (at your option)
16  * any later version.
17  *
18  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
19  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
20  * A PARTICULAR PURPOSE. See the GNU General Public License for more
21  * details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with wimlib; if not, see http://www.gnu.org/licenses/.
25  */
26
27
28 #include "config.h"
29
30
31 #include <ntfs-3g/endians.h>
32 #include <ntfs-3g/types.h>
33
34 #include "wimlib_internal.h"
35 #include "dentry.h"
36 #include "lookup_table.h"
37 #include "buffer_io.h"
38 #include <ntfs-3g/layout.h>
39 #include <ntfs-3g/acls.h>
40 #include <ntfs-3g/attrib.h>
41 #include <ntfs-3g/security.h> /* security.h before xattrs.h */
42 #include <ntfs-3g/xattrs.h>
43 #include <ntfs-3g/reparse.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46
47 static int extract_wim_chunk_to_ntfs_attr(const u8 *buf, size_t len,
48                                           u64 offset, void *arg)
49 {
50         ntfs_attr *na = arg;
51         if (ntfs_attr_pwrite(na, offset, len, buf) == len) {
52                 return 0;
53         } else {
54                 ERROR_WITH_ERRNO("Error extracting WIM resource to NTFS attribute");
55                 return WIMLIB_ERR_WRITE;
56         }
57 }
58
59 /*
60  * Extracts a WIM resource to a NTFS attribute.
61  */
62 static int
63 extract_wim_resource_to_ntfs_attr(const struct lookup_table_entry *lte,
64                                   ntfs_attr *na)
65 {
66         return extract_wim_resource(lte, wim_resource_size(lte),
67                                     extract_wim_chunk_to_ntfs_attr, na);
68 }
69
70 /* Writes the data streams to a NTFS file
71  *
72  * @ni:      The NTFS inode for the file.
73  * @inode:   The WIM dentry that has an inode containing the streams.
74  *
75  * Returns 0 on success, nonzero on failure.
76  */
77 static int write_ntfs_data_streams(ntfs_inode *ni, const struct dentry *dentry,
78                                    struct apply_args *args)
79 {
80         int ret = 0;
81         unsigned stream_idx = 0;
82         ntfschar *stream_name = AT_UNNAMED;
83         u32 stream_name_len = 0;
84         const struct inode *inode = dentry->d_inode;
85
86         DEBUG("Writing %u NTFS data stream%s for `%s'",
87               inode->num_ads + 1,
88               (inode->num_ads == 0 ? "" : "s"),
89               dentry->full_path_utf8);
90
91         while (1) {
92                 struct lookup_table_entry *lte;
93
94                 lte = inode_stream_lte_resolved(inode, stream_idx);
95
96                 if (stream_name_len) {
97                         /* Create an empty named stream. */
98                         ret = ntfs_attr_add(ni, AT_DATA, stream_name,
99                                             stream_name_len, NULL, 0);
100                         if (ret != 0) {
101                                 ERROR_WITH_ERRNO("Failed to create name data "
102                                                  "stream for extracted file "
103                                                  "`%s'",
104                                                  dentry->full_path_utf8);
105                                 ret = WIMLIB_ERR_NTFS_3G;
106                                 break;
107
108                         }
109                 }
110                 /* If there's no lookup table entry, it's an empty stream.
111                  * Otherwise, we must open the attribute and extract the data.
112                  * */
113                 if (lte) {
114                         ntfs_attr *na;
115
116                         na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
117                         if (!na) {
118                                 ERROR_WITH_ERRNO("Failed to open a data stream of "
119                                                  "extracted file `%s'",
120                                                  dentry->full_path_utf8);
121                                 ret = WIMLIB_ERR_NTFS_3G;
122                                 break;
123                         }
124                         ret = ntfs_attr_truncate_solid(na, wim_resource_size(lte));
125                         if (ret != 0) {
126                                 ntfs_attr_close(na);
127                                 break;
128                         }
129
130                         ret = extract_wim_resource_to_ntfs_attr(lte, na);
131                         ntfs_attr_close(na);
132                         if (ret != 0)
133                                 break;
134                         args->progress.extract.completed_bytes += wim_resource_size(lte);
135                 }
136                 if (stream_idx == inode->num_ads)
137                         break;
138                 stream_name = (ntfschar*)inode->ads_entries[stream_idx].stream_name;
139                 stream_name_len = inode->ads_entries[stream_idx].stream_name_len / 2;
140                 stream_idx++;
141         }
142         return ret;
143 }
144
145 /*
146  * Makes a NTFS hard link
147  *
148  * It is named @from_dentry->file_name and is located under the directory
149  * specified by @dir_ni, and it is made to point to the previously extracted
150  * file located at @inode->extracted_file.
151  *
152  * Return 0 on success, nonzero on failure.
153  */
154 static int apply_hardlink_ntfs(const struct dentry *from_dentry,
155                                const struct inode *inode,
156                                ntfs_inode *dir_ni,
157                                ntfs_inode **to_ni_ret)
158 {
159         int ret;
160         char *p;
161         char orig;
162         const char *dir_name;
163
164         ntfs_inode *to_ni;
165         ntfs_volume *vol;
166
167         wimlib_assert(dentry_is_regular_file(from_dentry)
168                         && inode_is_regular_file(inode));
169
170         if (ntfs_inode_close(dir_ni) != 0) {
171                 ERROR_WITH_ERRNO("Error closing directory");
172                 return WIMLIB_ERR_NTFS_3G;
173         }
174
175         vol = dir_ni->vol;
176
177         DEBUG("Extracting NTFS hard link `%s' => `%s'",
178               from_dentry->full_path_utf8, inode->extracted_file);
179
180         to_ni = ntfs_pathname_to_inode(vol, NULL, inode->extracted_file);
181         if (!to_ni) {
182                 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
183                                  inode->extracted_file);
184                 return WIMLIB_ERR_NTFS_3G;
185         }
186         p = from_dentry->full_path_utf8 + from_dentry->full_path_utf8_len;
187         do {
188                 p--;
189         } while (*p != '/');
190
191         orig = *p;
192         *p = '\0';
193         dir_name = from_dentry->full_path_utf8;
194
195         dir_ni = ntfs_pathname_to_inode(vol, NULL, dir_name);
196         if (!dir_ni) {
197                 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
198                                  from_dentry->full_path_utf8);
199                 *p = orig;
200                 return WIMLIB_ERR_NTFS_3G;
201         }
202         *p = orig;
203
204         ret = ntfs_link(to_ni, dir_ni,
205                         (ntfschar*)from_dentry->file_name,
206                         from_dentry->file_name_len / 2);
207         if (ret != 0) {
208                 ERROR_WITH_ERRNO("Could not create hard link `%s' => `%s'",
209                                  from_dentry->full_path_utf8,
210                                  inode->extracted_file);
211                 ret = WIMLIB_ERR_NTFS_3G;
212         }
213         *to_ni_ret = to_ni;
214         return ret;
215 }
216
217 static int
218 apply_file_attributes_and_security_data(ntfs_inode *ni,
219                                         ntfs_inode *dir_ni,
220                                         const struct dentry *dentry,
221                                         const WIMStruct *w)
222 {
223         DEBUG("Setting NTFS file attributes on `%s' to %#"PRIx32,
224               dentry->full_path_utf8, dentry->d_inode->attributes);
225         int ret;
226         struct SECURITY_CONTEXT ctx;
227         u32 attributes_le32;
228         attributes_le32 = cpu_to_le32(dentry->d_inode->attributes);
229         memset(&ctx, 0, sizeof(ctx));
230         ctx.vol = ni->vol;
231         ret = ntfs_xattr_system_setxattr(&ctx, XATTR_NTFS_ATTRIB,
232                                          ni, dir_ni,
233                                          (const char*)&attributes_le32,
234                                          sizeof(u32), 0);
235         if (ret != 0) {
236                 ERROR("Failed to set NTFS file attributes on `%s'",
237                        dentry->full_path_utf8);
238                 return WIMLIB_ERR_NTFS_3G;
239         }
240         if (dentry->d_inode->security_id != -1) {
241                 const struct wim_security_data *sd;
242                 const char *descriptor;
243
244                 sd = wim_const_security_data(w);
245                 wimlib_assert(dentry->d_inode->security_id < sd->num_entries);
246                 descriptor = (const char *)sd->descriptors[dentry->d_inode->security_id];
247                 DEBUG("Applying security descriptor %d to `%s'",
248                       dentry->d_inode->security_id, dentry->full_path_utf8);
249
250                 ret = ntfs_xattr_system_setxattr(&ctx, XATTR_NTFS_ACL,
251                                                  ni, dir_ni, descriptor,
252                                                  sd->sizes[dentry->d_inode->security_id], 0);
253
254                 if (ret != 0) {
255                         ERROR_WITH_ERRNO("Failed to set security data on `%s'",
256                                         dentry->full_path_utf8);
257                         return WIMLIB_ERR_NTFS_3G;
258                 }
259         }
260         return 0;
261 }
262
263 static int apply_reparse_data(ntfs_inode *ni, const struct dentry *dentry,
264                               struct apply_args *args)
265 {
266         struct lookup_table_entry *lte;
267         int ret = 0;
268
269         lte = inode_unnamed_lte_resolved(dentry->d_inode);
270
271         DEBUG("Applying reparse data to `%s'", dentry->full_path_utf8);
272
273         if (!lte) {
274                 ERROR("Could not find reparse data for `%s'",
275                       dentry->full_path_utf8);
276                 return WIMLIB_ERR_INVALID_DENTRY;
277         }
278
279         if (wim_resource_size(lte) >= 0xffff) {
280                 ERROR("Reparse data of `%s' is too long (%"PRIu64" bytes)",
281                       dentry->full_path_utf8, wim_resource_size(lte));
282                 return WIMLIB_ERR_INVALID_DENTRY;
283         }
284
285         u8 reparse_data_buf[8 + wim_resource_size(lte)];
286         u8 *p = reparse_data_buf;
287         p = put_u32(p, dentry->d_inode->reparse_tag); /* ReparseTag */
288         p = put_u16(p, wim_resource_size(lte)); /* ReparseDataLength */
289         p = put_u16(p, 0); /* Reserved */
290
291         ret = read_full_wim_resource(lte, p, 0);
292         if (ret != 0)
293                 return ret;
294
295         ret = ntfs_set_ntfs_reparse_data(ni, (char*)reparse_data_buf,
296                                          wim_resource_size(lte) + 8, 0);
297         if (ret != 0) {
298                 ERROR_WITH_ERRNO("Failed to set NTFS reparse data on `%s'",
299                                  dentry->full_path_utf8);
300                 return WIMLIB_ERR_NTFS_3G;
301         }
302         args->progress.extract.completed_bytes += wim_resource_size(lte);
303         return 0;
304 }
305
306 static int do_apply_dentry_ntfs(struct dentry *dentry, ntfs_inode *dir_ni,
307                                 struct apply_args *args);
308
309 /*
310  * If @dentry is part of a hard link group, search for hard-linked dentries in
311  * the same directory that have a nonempty DOS (short) filename.  There should
312  * be exactly 0 or 1 such dentries.  If there is 1, extract that dentry first,
313  * so that the DOS name is correctly associated with the corresponding long name
314  * in the Win32 namespace, and not any of the additional names in the POSIX
315  * namespace created from hard links.
316  */
317 static int preapply_dentry_with_dos_name(struct dentry *dentry,
318                                          ntfs_inode **dir_ni_p,
319                                          struct apply_args *args)
320 {
321         struct dentry *other;
322         struct dentry *dentry_with_dos_name;
323
324         dentry_with_dos_name = NULL;
325         inode_for_each_dentry(other, dentry->d_inode) {
326                 if (other != dentry && (dentry->parent == other->parent)
327                     && other->short_name_len)
328                 {
329                         if (dentry_with_dos_name) {
330                                 ERROR("Found multiple DOS names for file `%s' "
331                                       "in the same directory",
332                                       dentry_with_dos_name->full_path_utf8);
333                                 return WIMLIB_ERR_INVALID_DENTRY;
334                         }
335                         dentry_with_dos_name = other;
336                 }
337         }
338         /* If there's a dentry with a DOS name, extract it first */
339         if (dentry_with_dos_name && !dentry_with_dos_name->is_extracted) {
340                 char *p;
341                 const char *dir_name;
342                 char orig;
343                 int ret;
344                 ntfs_volume *vol = (*dir_ni_p)->vol;
345
346                 DEBUG("pre-applying DOS name `%s'",
347                       dentry_with_dos_name->full_path_utf8);
348                 ret = do_apply_dentry_ntfs(dentry_with_dos_name,
349                                            *dir_ni_p, args);
350                 if (ret != 0)
351                         return ret;
352                 p = dentry->full_path_utf8 + dentry->full_path_utf8_len;
353                 do {
354                         p--;
355                 } while (*p != '/');
356
357                 orig = *p;
358                 *p = '\0';
359                 dir_name = dentry->full_path_utf8;
360
361                 *dir_ni_p = ntfs_pathname_to_inode(vol, NULL, dir_name);
362                 *p = orig;
363                 if (!*dir_ni_p) {
364                         ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
365                                          dir_name);
366                         return WIMLIB_ERR_NTFS_3G;
367                 }
368         }
369         return 0;
370 }
371
372 /*
373  * Applies a WIM dentry to a NTFS filesystem.
374  *
375  * @dentry:  The WIM dentry to apply
376  * @dir_ni:  The NTFS inode for the parent directory
377  *
378  * @return:  0 on success; nonzero on failure.
379  */
380 static int do_apply_dentry_ntfs(struct dentry *dentry, ntfs_inode *dir_ni,
381                                 struct apply_args *args)
382 {
383         int ret = 0;
384         mode_t type;
385         ntfs_inode *ni = NULL;
386         bool is_hardlink = false;
387         ntfs_volume *vol = dir_ni->vol;
388         struct inode *inode = dentry->d_inode;
389         dentry->is_extracted = true;
390
391         if (inode->attributes & FILE_ATTRIBUTE_DIRECTORY) {
392                 type = S_IFDIR;
393         } else {
394                 /* Apply hard-linked directory in same directory with DOS name
395                  * (if there is one) before this dentry */
396                 if (dentry->short_name_len == 0) {
397                         ret = preapply_dentry_with_dos_name(dentry,
398                                                             &dir_ni, args);
399                         if (ret != 0)
400                                 return ret;
401                 }
402
403                 type = S_IFREG;
404
405                 if (inode->link_count > 1) {
406                         /* Already extracted another dentry in the hard link
407                          * group.  We can make a hard link instead of extracting
408                          * the file data. */
409                         if (inode->extracted_file) {
410                                 ret = apply_hardlink_ntfs(dentry, inode,
411                                                           dir_ni, &ni);
412                                 is_hardlink = true;
413                                 if (ret)
414                                         goto out_close_dir_ni;
415                                 else
416                                         goto out_set_dos_name;
417                         }
418                         /* Can't make a hard link; extract the file itself */
419                         FREE(inode->extracted_file);
420                         inode->extracted_file = STRDUP(dentry->full_path_utf8);
421                         if (!inode->extracted_file) {
422                                 ret = WIMLIB_ERR_NOMEM;
423                                 goto out_close_dir_ni;
424                         }
425                 }
426         }
427
428         /*
429          * Create a directory or file.
430          *
431          * Note: For symbolic links that are not directory junctions, pass
432          * S_IFREG here, since we manually set the reparse data later.
433          */
434         ni = ntfs_create(dir_ni, 0, (ntfschar*)dentry->file_name,
435                          dentry->file_name_len / 2, type);
436
437         if (!ni) {
438                 ERROR_WITH_ERRNO("Could not create NTFS object for `%s'",
439                                  dentry->full_path_utf8);
440                 ret = WIMLIB_ERR_NTFS_3G;
441                 goto out_close_dir_ni;
442         }
443
444         /* Write the data streams, unless this is a directory or reparse point
445          * */
446         if (!(inode->attributes & (FILE_ATTRIBUTE_REPARSE_POINT |
447                                    FILE_ATTRIBUTE_DIRECTORY))) {
448                 ret = write_ntfs_data_streams(ni, dentry, args);
449                 if (ret != 0)
450                         goto out_close_dir_ni;
451         }
452
453
454         ret = apply_file_attributes_and_security_data(ni, dir_ni,
455                                                       dentry, args->w);
456         if (ret != 0)
457                 goto out_close_dir_ni;
458
459         if (inode->attributes & FILE_ATTR_REPARSE_POINT) {
460                 ret = apply_reparse_data(ni, dentry, args);
461                 if (ret != 0)
462                         goto out_close_dir_ni;
463         }
464
465 out_set_dos_name:
466         /* Set DOS (short) name if given */
467         if (dentry->short_name_len != 0) {
468
469                 char *short_name_utf8;
470                 size_t short_name_utf8_len;
471                 ret = utf16_to_utf8(dentry->short_name,
472                                     dentry->short_name_len,
473                                     &short_name_utf8,
474                                     &short_name_utf8_len);
475                 if (ret != 0)
476                         goto out_close_dir_ni;
477
478                 if (is_hardlink) {
479                         char *p;
480                         char orig;
481                         const char *dir_name;
482
483                         /* ntfs_set_ntfs_dos_name() closes the inodes in the
484                          * wrong order if we have applied a hard link.   Close
485                          * them ourselves, then re-open then. */
486                         if (ntfs_inode_close(dir_ni) != 0) {
487                                 if (ret == 0)
488                                         ret = WIMLIB_ERR_NTFS_3G;
489                                 ERROR_WITH_ERRNO("Failed to close directory inode");
490                         }
491                         if (ntfs_inode_close(ni) != 0) {
492                                 if (ret == 0)
493                                         ret = WIMLIB_ERR_NTFS_3G;
494                                 ERROR_WITH_ERRNO("Failed to close hard link target inode");
495                         }
496                         p = dentry->full_path_utf8 + dentry->full_path_utf8_len;
497                         do {
498                                 p--;
499                         } while (*p != '/');
500
501                         orig = *p;
502                         *p = '\0';
503                         dir_name = dentry->full_path_utf8;
504
505                         dir_ni = ntfs_pathname_to_inode(vol, NULL, dir_name);
506                         *p = orig;
507                         if (!dir_ni) {
508                                 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
509                                                  dir_name);
510                                 return WIMLIB_ERR_NTFS_3G;
511                         }
512                         ni = ntfs_pathname_to_inode(vol, dir_ni,
513                                                     dentry->file_name_utf8);
514                         if (!ni) {
515                                 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
516                                                  dentry->full_path_utf8);
517                                 ntfs_inode_close(dir_ni);
518                                 return WIMLIB_ERR_NTFS_3G;
519                         }
520                 }
521
522                 DEBUG("Setting short (DOS) name of `%s' to %s",
523                       dentry->full_path_utf8, short_name_utf8);
524
525                 ret = ntfs_set_ntfs_dos_name(ni, dir_ni, short_name_utf8,
526                                              short_name_utf8_len, 0);
527                 FREE(short_name_utf8);
528                 if (ret != 0) {
529                         ERROR_WITH_ERRNO("Could not set DOS (short) name for `%s'",
530                                          dentry->full_path_utf8);
531                         ret = WIMLIB_ERR_NTFS_3G;
532                 }
533                 /* inodes have been closed by ntfs_set_ntfs_dos_name(). */
534                 return ret;
535         }
536
537 out_close_dir_ni:
538         if (ni && dir_ni) {
539                 if (ntfs_inode_close_in_dir(ni, dir_ni) != 0) {
540                         ni = NULL;
541                         if (ret == 0)
542                                 ret = WIMLIB_ERR_NTFS_3G;
543                         ERROR_WITH_ERRNO("Failed to close inode for `%s'",
544                                          dentry->full_path_utf8);
545                 }
546         }
547         if (ntfs_inode_close(dir_ni) != 0) {
548                 if (ret == 0)
549                         ret = WIMLIB_ERR_NTFS_3G;
550                 ERROR_WITH_ERRNO("Failed to close directory inode");
551         }
552         if (ni && ntfs_inode_close(ni) != 0) {
553                 if (ret == 0)
554                         ret = WIMLIB_ERR_NTFS_3G;
555                 ERROR_WITH_ERRNO("Failed to close inode for `%s'",
556                                  dentry->full_path_utf8);
557         }
558         return ret;
559 }
560
561 static int apply_root_dentry_ntfs(const struct dentry *dentry,
562                                   ntfs_volume *vol, const WIMStruct *w)
563 {
564         ntfs_inode *ni;
565         int ret = 0;
566
567         wimlib_assert(dentry_is_directory(dentry));
568         ni = ntfs_pathname_to_inode(vol, NULL, "/");
569         if (!ni) {
570                 ERROR_WITH_ERRNO("Could not find root NTFS inode");
571                 return WIMLIB_ERR_NTFS_3G;
572         }
573         ret = apply_file_attributes_and_security_data(ni, ni, dentry, w);
574         if (ntfs_inode_close(ni) != 0) {
575                 ERROR_WITH_ERRNO("Failed to close NTFS inode for root "
576                                  "directory");
577                 ret = WIMLIB_ERR_NTFS_3G;
578         }
579         return ret;
580 }
581
582 /* Applies a WIM dentry to the NTFS volume */
583 int apply_dentry_ntfs(struct dentry *dentry, void *arg)
584 {
585         struct apply_args *args = arg;
586         ntfs_volume *vol             = args->vol;
587         int extract_flags            = args->extract_flags;
588         WIMStruct *w                 = args->w;
589         ntfs_inode *dir_ni;
590         char *p;
591         char orig;
592         const char *dir_name;
593
594         if (dentry->is_extracted)
595                 return 0;
596
597         if (extract_flags & WIMLIB_EXTRACT_FLAG_NO_STREAMS)
598                 if (inode_unnamed_lte_resolved(dentry->d_inode))
599                         return 0;
600
601         DEBUG("Applying dentry `%s' to NTFS", dentry->full_path_utf8);
602
603         if ((extract_flags & WIMLIB_EXTRACT_FLAG_VERBOSE) &&
604              args->progress_func)
605         {
606                 args->progress.extract.cur_path = dentry->full_path_utf8;
607                 args->progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DENTRY,
608                                     &args->progress);
609         }
610
611         if (dentry_is_root(dentry))
612                 return apply_root_dentry_ntfs(dentry, vol, w);
613
614         p = dentry->full_path_utf8 + dentry->full_path_utf8_len;
615         do {
616                 p--;
617         } while (*p != '/');
618
619         orig = *p;
620         *p = '\0';
621         dir_name = dentry->full_path_utf8;
622
623         dir_ni = ntfs_pathname_to_inode(vol, NULL, dir_name);
624         *p = orig;
625         if (!dir_ni) {
626                 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
627                                  dir_name);
628                 return WIMLIB_ERR_NTFS_3G;
629         }
630         return do_apply_dentry_ntfs(dentry, dir_ni, arg);
631 }
632
633 int apply_dentry_timestamps_ntfs(struct dentry *dentry, void *arg)
634 {
635         struct apply_args *args = arg;
636         ntfs_volume *vol = args->vol;
637         u8 *p;
638         u8 buf[24];
639         ntfs_inode *ni;
640         int ret = 0;
641
642         DEBUG("Setting timestamps on `%s'", dentry->full_path_utf8);
643
644         ni = ntfs_pathname_to_inode(vol, NULL, dentry->full_path_utf8);
645         if (!ni) {
646                 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
647                                  dentry->full_path_utf8);
648                 return WIMLIB_ERR_NTFS_3G;
649         }
650
651         p = buf;
652         p = put_u64(p, dentry->d_inode->creation_time);
653         p = put_u64(p, dentry->d_inode->last_write_time);
654         p = put_u64(p, dentry->d_inode->last_access_time);
655         ret = ntfs_inode_set_times(ni, (const char*)buf, 3 * sizeof(u64), 0);
656         if (ret != 0) {
657                 ERROR_WITH_ERRNO("Failed to set NTFS timestamps on `%s'",
658                                  dentry->full_path_utf8);
659                 ret = WIMLIB_ERR_NTFS_3G;
660         }
661
662         if (ntfs_inode_close(ni) != 0) {
663                 if (ret == 0)
664                         ret = WIMLIB_ERR_NTFS_3G;
665                 ERROR_WITH_ERRNO("Failed to close NTFS inode for `%s'",
666                                  dentry->full_path_utf8);
667         }
668         return ret;
669 }