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