]> wimlib.net Git - wimlib/blob - src/ntfs-3g_apply.c
Merge branch with pipable WIM support
[wimlib] / src / ntfs-3g_apply.c
1 /*
2  * ntfs-3g_apply.c
3  *
4  * Apply a WIM image directly to a NTFS volume using libntfs-3g.  Restore as
5  * much information as possible, including security data, file attributes, DOS
6  * names, and alternate data streams.
7  */
8
9 /*
10  * Copyright (C) 2012, 2013 Eric Biggers
11  *
12  * This file is part of wimlib, a library for working with WIM files.
13  *
14  * wimlib is free software; you can redistribute it and/or modify it under the
15  * terms of the GNU General Public License as published by the Free
16  * Software Foundation; either version 3 of the License, or (at your option)
17  * any later version.
18  *
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 General Public License for more
22  * details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with wimlib; if not, see http://www.gnu.org/licenses/.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 #  include "config.h"
30 #endif
31
32 #ifdef WITH_NTFS_3G
33
34 #include <locale.h>
35 #include <string.h>
36 #include <time.h> /* NTFS-3g headers are missing <time.h> include */
37
38 #include <ntfs-3g/attrib.h>
39 #include <ntfs-3g/reparse.h>
40 #include <ntfs-3g/security.h>
41 #include <errno.h>
42
43 #include "wimlib/apply.h"
44 #include "wimlib/encoding.h"
45 #include "wimlib/error.h"
46 #include "wimlib/lookup_table.h"
47 #include "wimlib/ntfs_3g.h"
48 #include "wimlib/paths.h"
49 #include "wimlib/resource.h"
50
51 static ntfs_volume *
52 ntfs_3g_apply_ctx_get_volume(struct apply_ctx *ctx)
53 {
54         return (ntfs_volume*)ctx->private[0];
55 }
56
57 static void
58 ntfs_3g_apply_ctx_set_volume(struct apply_ctx *ctx, ntfs_volume *vol)
59 {
60         ctx->private[0] = (intptr_t)vol;
61 }
62
63 static ntfs_inode *
64 ntfs_3g_apply_pathname_to_inode(const char *path, struct apply_ctx *ctx)
65 {
66         ntfs_volume *vol = ntfs_3g_apply_ctx_get_volume(ctx);
67         return ntfs_pathname_to_inode(vol, NULL, path);
68 }
69
70 struct ntfs_attr_extract_ctx {
71         u64 offset;
72         ntfs_attr *na;
73 };
74
75 static int
76 ntfs_3g_extract_wim_chunk(const void *buf, size_t len, void *_ctx)
77 {
78         struct ntfs_attr_extract_ctx *ctx = _ctx;
79
80         if (ntfs_attr_pwrite(ctx->na, ctx->offset, len, buf) != len)
81                 return WIMLIB_ERR_WRITE;
82         ctx->offset += len;
83         return 0;
84 }
85
86 static ntfs_inode *
87 ntfs_3g_open_parent_inode(const char *path, ntfs_volume *vol)
88 {
89         char orig, *p;
90         ntfs_inode *dir_ni;
91
92         p = strchr(path, '\0');
93         do {
94                 p--;
95         } while (*p != '/');
96
97         orig = *p;
98         *p = '\0';
99         dir_ni = ntfs_pathname_to_inode(vol, NULL, path);
100         *p = orig;
101         return dir_ni;
102 }
103
104 static int
105 ntfs_3g_create(const char *path, struct apply_ctx *ctx, mode_t mode)
106 {
107         ntfs_volume *vol;
108         ntfs_inode *dir_ni, *ni;
109         const char *name;
110         utf16lechar *name_utf16le;
111         size_t name_utf16le_nbytes;
112         int ret;
113
114         vol = ntfs_3g_apply_ctx_get_volume(ctx);
115
116         ret = WIMLIB_ERR_NTFS_3G;
117         dir_ni = ntfs_3g_open_parent_inode(path, vol);
118         if (!dir_ni)
119                 goto out;
120
121         name = path_basename(path);
122         ret = tstr_to_utf16le(name, strlen(name),
123                               &name_utf16le, &name_utf16le_nbytes);
124         if (ret)
125                 goto out_close_dir_ni;
126
127         ret = 0;
128         ni = ntfs_create(dir_ni, 0, name_utf16le,
129                          name_utf16le_nbytes / 2, mode);
130         if (!ni || ntfs_inode_close_in_dir(ni, dir_ni))
131                 ret = WIMLIB_ERR_NTFS_3G;
132         FREE(name_utf16le);
133 out_close_dir_ni:
134         if (ntfs_inode_close(dir_ni))
135                 ret = WIMLIB_ERR_NTFS_3G;
136 out:
137         return ret;
138 }
139
140 static int
141 ntfs_3g_create_file(const char *path, struct apply_ctx *ctx)
142 {
143         return ntfs_3g_create(path, ctx, S_IFREG);
144 }
145
146 static int
147 ntfs_3g_create_directory(const char *path, struct apply_ctx *ctx)
148 {
149         return ntfs_3g_create(path, ctx, S_IFDIR);
150 }
151
152 static int
153 ntfs_3g_create_hardlink(const char *oldpath, const char *newpath,
154                         struct apply_ctx *ctx)
155 {
156         ntfs_volume *vol;
157         ntfs_inode *dir_ni, *ni;
158         const char *name;
159         utf16lechar *name_utf16le;
160         size_t name_utf16le_nbytes;
161         int ret;
162
163         vol = ntfs_3g_apply_ctx_get_volume(ctx);
164
165         ret = WIMLIB_ERR_NTFS_3G;
166         ni = ntfs_pathname_to_inode(vol, NULL, oldpath);
167         if (!ni)
168                 goto out;
169
170         ret = WIMLIB_ERR_NTFS_3G;
171         dir_ni = ntfs_3g_open_parent_inode(newpath, vol);
172         if (!dir_ni)
173                 goto out_close_ni;
174
175         name = path_basename(newpath);
176         ret = tstr_to_utf16le(name, strlen(name),
177                               &name_utf16le, &name_utf16le_nbytes);
178         if (ret)
179                 goto out_close_dir_ni;
180
181         ret = 0;
182         if (ntfs_link(ni, dir_ni, name_utf16le, name_utf16le_nbytes / 2))
183                 ret = WIMLIB_ERR_NTFS_3G;
184         FREE(name_utf16le);
185 out_close_dir_ni:
186         if (ntfs_inode_close(dir_ni))
187                 ret = WIMLIB_ERR_NTFS_3G;
188 out_close_ni:
189         if (ntfs_inode_close(ni))
190                 ret = WIMLIB_ERR_NTFS_3G;
191 out:
192         return ret;
193 }
194
195 /*
196  * Extract a stream (default or alternate data) to an attribute of a NTFS file.
197  */
198 static int
199 ntfs_3g_extract_stream(const char *path, const utf16lechar *stream_name,
200                        size_t stream_name_nchars,
201                        struct wim_lookup_table_entry *lte, struct apply_ctx *ctx)
202 {
203         ntfs_inode *ni;
204         ntfs_attr *na;
205         int ret;
206         struct ntfs_attr_extract_ctx extract_ctx;
207         utf16lechar stream_name_copy[stream_name_nchars + 1];
208
209         memcpy(stream_name_copy, stream_name,
210                stream_name_nchars * sizeof(utf16lechar));
211         stream_name_copy[stream_name_nchars] = 0;
212
213         ret = 0;
214         if (!stream_name_nchars && !lte)
215                 goto out;
216
217         /* Look up NTFS inode to which to extract the stream.  */
218         ret = WIMLIB_ERR_NTFS_3G;
219         ni = ntfs_3g_apply_pathname_to_inode(path, ctx);
220         if (!ni)
221                 goto out;
222
223         /* Add the stream if it's not the default (unnamed) stream.  */
224         ret = WIMLIB_ERR_NTFS_3G;
225         if (stream_name_nchars)
226                 if (ntfs_attr_add(ni, AT_DATA, stream_name_copy,
227                                   stream_name_nchars, NULL, 0))
228                         goto out_close;
229
230         /* If stream is empty, no need to open and extract it.  */
231         ret = 0;
232         if (!lte)
233                 goto out_close;
234
235         /* Open the stream (NTFS attribute).  */
236         ret = WIMLIB_ERR_NTFS_3G;
237         na = ntfs_attr_open(ni, AT_DATA, stream_name_copy, stream_name_nchars);
238         if (!na)
239                 goto out_close;
240
241         /* (Optional) Immediately resize attribute to size of stream.  */
242         ret = WIMLIB_ERR_NTFS_3G;
243         if (ntfs_attr_truncate_solid(na, wim_resource_size(lte)))
244                 goto out_attr_close;
245
246         /* Extract stream data to the NTFS attribute.  */
247         extract_ctx.na = na;
248         extract_ctx.offset = 0;
249         ret = extract_wim_resource(lte, wim_resource_size(lte),
250                                    ntfs_3g_extract_wim_chunk, &extract_ctx);
251         /* Clean up and return.  */
252 out_attr_close:
253         ntfs_attr_close(na);
254 out_close:
255         if (ntfs_inode_close(ni))
256                 ret = WIMLIB_ERR_NTFS_3G;
257 out:
258         if (ret && !errno)
259                 errno = -1;
260         return ret;
261 }
262
263 static int
264 ntfs_3g_extract_unnamed_stream(const char *path,
265                                struct wim_lookup_table_entry *lte,
266                                struct apply_ctx *ctx)
267 {
268         return ntfs_3g_extract_stream(path, NULL, 0, lte, ctx);
269 }
270
271 static int
272 ntfs_3g_extract_named_stream(const char *path, const utf16lechar *stream_name,
273                              size_t stream_name_nchars,
274                              struct wim_lookup_table_entry *lte, struct apply_ctx *ctx)
275 {
276         return ntfs_3g_extract_stream(path, stream_name,
277                                       stream_name_nchars, lte, ctx);
278 }
279
280 static int
281 ntfs_3g_set_file_attributes(const char *path, u32 attributes,
282                             struct apply_ctx *ctx)
283 {
284         ntfs_inode *ni;
285         int ret = 0;
286
287         ni = ntfs_3g_apply_pathname_to_inode(path, ctx);
288         if (!ni)
289                 return WIMLIB_ERR_NTFS_3G;
290         if (ntfs_set_ntfs_attrib(ni, (const char*)&attributes, sizeof(u32), 0))
291                 ret = WIMLIB_ERR_NTFS_3G;
292         if (ntfs_inode_close(ni))
293                 ret = WIMLIB_ERR_NTFS_3G;
294         return ret;
295 }
296
297 static int
298 ntfs_3g_set_reparse_data(const char *path, const u8 *rpbuf, u16 rpbuflen,
299                          struct apply_ctx *ctx)
300 {
301         ntfs_inode *ni;
302         int ret = 0;
303
304         ni = ntfs_3g_apply_pathname_to_inode(path, ctx);
305         if (!ni)
306                 return WIMLIB_ERR_NTFS_3G;
307         if (ntfs_set_ntfs_reparse_data(ni, rpbuf, rpbuflen, 0))
308                 ret = WIMLIB_ERR_NTFS_3G;
309         if (ntfs_inode_close(ni))
310                 ret = WIMLIB_ERR_NTFS_3G;
311         return ret;
312 }
313
314 static int
315 ntfs_3g_set_short_name(const char *path, const utf16lechar *short_name,
316                        size_t short_name_nchars, struct apply_ctx *ctx)
317 {
318         ntfs_inode *ni, *dir_ni;
319         ntfs_volume *vol;
320         int ret;
321         char *dosname = NULL;
322         size_t dosname_nbytes;
323
324         ret = 0;
325         if (short_name_nchars == 0)
326                 goto out;
327
328         vol = ntfs_3g_apply_ctx_get_volume(ctx);
329
330         ret = WIMLIB_ERR_NTFS_3G;
331         dir_ni = ntfs_3g_open_parent_inode(path, vol);
332         if (!dir_ni)
333                 goto out;
334
335         ret = WIMLIB_ERR_NTFS_3G;
336         ni = ntfs_pathname_to_inode(vol, NULL, path);
337         if (!ni)
338                 goto out_close_dir_ni;
339
340         ret = utf16le_to_tstr(short_name, short_name_nchars * 2,
341                               &dosname, &dosname_nbytes);
342         if (ret)
343                 goto out_close_ni;
344
345         ret = 0;
346         if (ntfs_set_ntfs_dos_name(ni, dir_ni, dosname,
347                                    dosname_nbytes, 0))
348                 ret = WIMLIB_ERR_NTFS_3G;
349         /* ntfs_set_ntfs_dos_name() always closes the inodes.  */
350         FREE(dosname);
351         goto out;
352 out_close_ni:
353         if (ntfs_inode_close_in_dir(ni, dir_ni))
354                 ret = WIMLIB_ERR_NTFS_3G;
355 out_close_dir_ni:
356         if (ntfs_inode_close(dir_ni))
357                 ret = WIMLIB_ERR_NTFS_3G;
358 out:
359         return ret;
360 }
361
362 static int
363 ntfs_3g_set_security_descriptor(const char *path, const u8 *desc, size_t desc_size,
364                                 struct apply_ctx *ctx, bool strict)
365 {
366         ntfs_volume *vol;
367         ntfs_inode *ni;
368         struct SECURITY_CONTEXT sec_ctx;
369         int ret = 0;
370
371         vol = ntfs_3g_apply_ctx_get_volume(ctx);
372
373         ni = ntfs_pathname_to_inode(vol, NULL, path);
374         if (!ni)
375                 return WIMLIB_ERR_NTFS_3G;
376
377         memset(&sec_ctx, 0, sizeof(sec_ctx));
378         sec_ctx.vol = vol;
379
380         if (ntfs_set_ntfs_acl(&sec_ctx, ni, desc, desc_size, 0))
381                 ret = WIMLIB_ERR_NTFS_3G;
382         if (ntfs_inode_close(ni))
383                 ret = WIMLIB_ERR_NTFS_3G;
384         return ret;
385 }
386
387 static int
388 ntfs_3g_set_timestamps(const char *path, u64 creation_time,
389                        u64 last_write_time, u64 last_access_time,
390                        struct apply_ctx *ctx)
391 {
392         u64 ntfs_timestamps[3];
393         ntfs_inode *ni;
394         int ret = 0;
395
396         ni = ntfs_3g_apply_pathname_to_inode(path, ctx);
397         if (!ni)
398                 return WIMLIB_ERR_NTFS_3G;
399
400         /* Note: ntfs_inode_set_times() expects the times in native byte order,
401          * not little endian. */
402         ntfs_timestamps[0] = creation_time;
403         ntfs_timestamps[1] = last_write_time;
404         ntfs_timestamps[2] = last_access_time;
405
406         if (ntfs_inode_set_times(ni, (const char*)ntfs_timestamps,
407                                  sizeof(ntfs_timestamps), 0))
408                 ret = WIMLIB_ERR_NTFS_3G;
409         if (ntfs_inode_close(ni))
410                 ret = WIMLIB_ERR_NTFS_3G;
411         return ret;
412 }
413
414 static bool
415 ntfs_3g_target_is_root(const char *target)
416 {
417         /* We always extract to the root of the NTFS volume.  */
418         return true;
419 }
420
421 static int
422 ntfs_3g_start_extract(const char *path, struct apply_ctx *ctx)
423 {
424         ntfs_volume *vol;
425
426         vol = ntfs_mount(ctx->target, 0);
427         if (!vol) {
428                 ERROR_WITH_ERRNO("Failed to mount \"%"TS"\" with NTFS-3g", ctx->target);
429                 return WIMLIB_ERR_NTFS_3G;
430         }
431         ntfs_3g_apply_ctx_set_volume(ctx, vol);
432
433         ctx->supported_features.archive_files             = 1;
434         ctx->supported_features.hidden_files              = 1;
435         ctx->supported_features.system_files              = 1;
436         ctx->supported_features.compressed_files          = 1;
437         ctx->supported_features.encrypted_files           = 0;
438         ctx->supported_features.not_context_indexed_files = 1;
439         ctx->supported_features.sparse_files              = 1;
440         ctx->supported_features.named_data_streams        = 1;
441         ctx->supported_features.hard_links                = 1;
442         ctx->supported_features.reparse_points            = 1;
443         ctx->supported_features.security_descriptors      = 1;
444         ctx->supported_features.short_names               = 1;
445         return 0;
446 }
447
448 static int
449 ntfs_3g_finish_or_abort_extract(struct apply_ctx *ctx)
450 {
451         ntfs_volume *vol;
452
453         vol = ntfs_3g_apply_ctx_get_volume(ctx);
454         if (ntfs_umount(vol, FALSE)) {
455                 ERROR_WITH_ERRNO("Failed to unmount \"%"TS"\" with NTFS-3g",
456                                  ctx->target);
457                 return WIMLIB_ERR_NTFS_3G;
458         }
459         return 0;
460 }
461
462 void
463 libntfs3g_global_init(void)
464 {
465         ntfs_set_char_encoding(setlocale(LC_ALL, ""));
466 }
467
468 const struct apply_operations ntfs_3g_apply_ops = {
469         .name = "NTFS-3g",
470
471         .target_is_root          = ntfs_3g_target_is_root,
472         .start_extract           = ntfs_3g_start_extract,
473         .create_file             = ntfs_3g_create_file,
474         .create_directory        = ntfs_3g_create_directory,
475         .create_hardlink         = ntfs_3g_create_hardlink,
476         .extract_unnamed_stream  = ntfs_3g_extract_unnamed_stream,
477         .extract_named_stream    = ntfs_3g_extract_named_stream,
478         .set_file_attributes     = ntfs_3g_set_file_attributes,
479         .set_reparse_data        = ntfs_3g_set_reparse_data,
480         .set_short_name          = ntfs_3g_set_short_name,
481         .set_security_descriptor = ntfs_3g_set_security_descriptor,
482         .set_timestamps          = ntfs_3g_set_timestamps,
483         .abort_extract           = ntfs_3g_finish_or_abort_extract,
484         .finish_extract          = ntfs_3g_finish_or_abort_extract,
485
486         .path_prefix = "/",
487         .path_prefix_nchars = 1,
488         .path_separator = '/',
489         .path_max = 32768,
490
491         .supports_case_sensitive_filenames = 1,
492         .root_directory_is_special = 1,
493 };
494
495 #endif /* WITH_NTFS_3G */