new_lookup_table_entry() fix
[wimlib] / src / ntfs-capture.c
1 /*
2  * ntfs-capture.c
3  *
4  * Capture a WIM image from a NTFS volume.  We capture everything we can,
5  * including security data and alternate data streams.  There should be no loss
6  * of information.
7  */
8
9 /*
10  * Copyright (C) 2012 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 Lesser General Public License as published by the Free
16  * Software Foundation; either version 2.1 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 Lesser General Public License for more
22  * details.
23  *
24  * You should have received a copy of the GNU Lesser General Public License
25  * along with wimlib; if not, see http://www.gnu.org/licenses/.
26  */
27
28 #include "config.h"
29 #include "wimlib_internal.h"
30
31
32 #ifdef WITH_NTFS_3G
33 #include "dentry.h"
34 #include "lookup_table.h"
35 #include "io.h"
36 #include <ntfs-3g/layout.h>
37 #include <ntfs-3g/acls.h>
38 #include <ntfs-3g/attrib.h>
39 #include <ntfs-3g/misc.h>
40 #include <ntfs-3g/reparse.h>
41 #include <ntfs-3g/security.h>
42 #include <ntfs-3g/volume.h>
43 #include <stdlib.h>
44 #include <unistd.h>
45
46 extern int ntfs_inode_get_security(ntfs_inode *ni, u32 selection, char *buf,
47                                    u32 buflen, u32 *psize);
48
49 extern int ntfs_inode_get_attributes(ntfs_inode *ni);
50
51 struct sd_tree {
52         u32 num_sds;
53         struct wim_security_data *sd;
54         struct sd_node *root;
55 };
56
57 struct sd_node {
58         int security_id;
59         u8 hash[SHA1_HASH_SIZE];
60         struct sd_node *left;
61         struct sd_node *right;
62 };
63
64 static void free_sd_tree(struct sd_node *root)
65 {
66         if (root) {
67                 free_sd_tree(root->left);
68                 free_sd_tree(root->right);
69                 FREE(root);
70         }
71 }
72
73 static void insert_sd_node(struct sd_node *new, struct sd_node *root)
74 {
75         int cmp = hashes_cmp(root->hash, new->hash);
76         if (cmp < 0) {
77                 if (root->left)
78                         insert_sd_node(new, root->left);
79                 else 
80                         root->left = new;
81         } else if (cmp > 0) {
82                 if (root->right)
83                         insert_sd_node(new, root->right);
84                 else 
85                         root->right = new;
86         } else {
87                 wimlib_assert(0);
88         }
89 }
90
91 static int lookup_sd(const u8 hash[SHA1_HASH_SIZE], struct sd_node *node)
92 {
93         int cmp;
94         if (!node)
95                 return -1;
96         cmp = hashes_cmp(hash, node->hash);
97         if (cmp < 0)
98                 return lookup_sd(hash, node->left);
99         else if (cmp > 0)
100                 return lookup_sd(hash, node->right);
101         else
102                 return node->security_id;
103 }
104
105 static int tree_add_sd(struct sd_tree *tree, const u8 *descriptor,
106                        size_t size)
107 {
108         u8 hash[SHA1_HASH_SIZE];
109         int security_id;
110         struct sd_node *new;
111         u8 **descriptors;
112         u64 *sizes;
113         u8 *descr_copy;
114         struct wim_security_data *sd = tree->sd;
115         sha1_buffer(descriptor, size, hash);
116
117         security_id = lookup_sd(hash, tree->root);
118         if (security_id >= 0)
119                 return security_id;
120
121         new = MALLOC(sizeof(struct sd_node));
122         if (!new)
123                 return -1;
124         descr_copy = MALLOC(size);
125         if (!descr_copy)
126                 goto out_free_node;
127         memcpy(descr_copy, descriptor, size);
128         new->security_id = tree->num_sds++;
129         new->left = NULL;
130         new->right = NULL;
131         copy_hash(new->hash, hash);
132
133         descriptors = REALLOC(sd->descriptors,
134                               (sd->num_entries + 1) * sizeof(sd->descriptors[0]));
135         if (!descriptors)
136                 goto out_free_descr;
137         sd->descriptors = descriptors;
138         sizes = REALLOC(sd->sizes,
139                         (sd->num_entries + 1) * sizeof(sd->sizes[0]));
140         if (!sizes)
141                 goto out_free_descr;
142         sd->sizes = sizes;
143         sd->descriptors[sd->num_entries] = descr_copy;
144         sd->sizes[sd->num_entries] = size;
145         sd->num_entries++;
146         sd->total_length += size + 8;
147
148         if (tree->root)
149                 insert_sd_node(tree->root, new);
150         else
151                 tree->root = new;
152         return new->security_id;
153 out_free_descr:
154         FREE(descr_copy);
155 out_free_node:
156         FREE(new);
157         return -1;
158 }
159
160 #if 0
161 static int build_sd_tree(struct wim_security_data *sd, struct sd_tree *tree)
162 {
163         int ret;
164         u32 orig_num_entries = sd->num_entries;
165         u32 orig_total_length = sd->total_length;
166
167         tree->num_sds = 0;
168         tree->sd = sd;
169         tree->root = NULL;
170
171         for (u32 i = 0; i < sd->num_entries; i++) {
172                 ret = tree_add_sd(tree, sd->descriptors[i], sd->sizes[i]);
173                 if (ret < 0)
174                         goto out_revert;
175         }
176         return 0;
177 out_revert:
178         sd->num_entries = orig_num_entries;
179         sd->total_length = orig_total_length;
180         free_sd_tree(tree->root);
181         return ret;
182 }
183 #endif
184
185 static int ntfs_attr_sha1sum(ntfs_inode *ni, ATTR_RECORD *ar,
186                              u8 md[SHA1_HASH_SIZE])
187 {
188         s64 pos = 0;
189         s64 bytes_remaining;
190         char buf[4096];
191         ntfs_attr *na;
192         SHA_CTX ctx;
193
194         na = ntfs_attr_open(ni, ar->type,
195                             (ntfschar*)((u8*)ar + le16_to_cpu(ar->name_offset)),
196                             ar->name_length);
197         if (!na) {
198                 ERROR_WITH_ERRNO("Failed to open NTFS attribute");
199                 return WIMLIB_ERR_NTFS_3G;
200         }
201
202         bytes_remaining = na->data_size;
203         sha1_init(&ctx);
204
205         while (bytes_remaining) {
206                 s64 to_read = min(bytes_remaining, sizeof(buf));
207                 if (ntfs_attr_pread(na, pos, to_read, buf) != to_read) {
208                         ERROR_WITH_ERRNO("Error reading NTFS attribute");
209                         return WIMLIB_ERR_NTFS_3G;
210                 }
211                 sha1_update(&ctx, buf, to_read);
212                 pos += to_read;
213                 bytes_remaining -= to_read;
214         }
215         sha1_final(md, &ctx);
216         ntfs_attr_close(na);
217         return 0;
218 }
219
220 static int __build_dentry_tree_ntfs(struct dentry *dentry, ntfs_inode *ni,
221                                     char path[], size_t path_len,
222                                     struct lookup_table *lookup_table,
223                                     struct sd_tree *tree)
224 {
225         u32 attributes = ntfs_inode_get_attributes(ni);
226         int mrec_flags = ni->mrec->flags;
227         u32 sd_size;
228         int ret = 0;
229
230         dentry->creation_time    = le64_to_cpu(ni->creation_time);
231         dentry->last_write_time  = le64_to_cpu(ni->last_data_change_time);
232         dentry->last_access_time = le64_to_cpu(ni->last_access_time);
233         dentry->security_id      = le32_to_cpu(ni->security_id);
234         dentry->attributes       = le32_to_cpu(attributes);
235         dentry->resolved = true;
236
237         if (mrec_flags & MFT_RECORD_IS_DIRECTORY) {
238                 if (attributes & FILE_ATTR_REPARSE_POINT) {
239                         /* Junction point */
240                 } else {
241                         /* Normal directory */
242                 }
243         } else {
244                 if (attributes & FILE_ATTR_REPARSE_POINT) {
245                         /* Symbolic link or other reparse point */
246                 } else {
247                         /* Normal file */
248                         ntfs_attr_search_ctx *actx;
249                         u8 attr_hash[SHA1_HASH_SIZE];
250                         struct lookup_table_entry *lte;
251
252                         actx = ntfs_attr_get_search_ctx(ni, NULL);
253                         if (!actx) {
254                                 ERROR_WITH_ERRNO("Cannot get attribute search "
255                                                  "context");
256                                 return WIMLIB_ERR_NTFS_3G;
257                         }
258                         while (!ntfs_attr_lookup(AT_DATA, NULL, 0,
259                                                  CASE_SENSITIVE, 0, NULL, 0, actx))
260                         {
261                                 ret = ntfs_attr_sha1sum(ni, actx->attr, attr_hash);
262                                 if (ret != 0)
263                                         return ret;
264                                 lte = __lookup_resource(lookup_table, attr_hash);
265                                 if (lte) {
266                                         lte->refcnt++;
267                                 } else {
268                                         /*char *file_on_disk = STRDUP(root_disk_path);*/
269                                         /*if (!file_on_disk) {*/
270                                                 /*ERROR("Failed to allocate memory for file path");*/
271                                                 /*return WIMLIB_ERR_NOMEM;*/
272                                         /*}*/
273                                         /*lte = new_lookup_table_entry();*/
274                                         /*if (!lte) {*/
275                                                 /*FREE(file_on_disk);*/
276                                                 /*return WIMLIB_ERR_NOMEM;*/
277                                         /*}*/
278                                         /*lte->file_on_disk = file_on_disk;*/
279                                         /*lte->resource_location = RESOURCE_IN_FILE_ON_DISK;*/
280                                         /*lte->resource_entry.original_size = root_stbuf.st_size;*/
281                                         /*lte->resource_entry.size = root_stbuf.st_size;*/
282                                         /*copy_hash(lte->hash, hash);*/
283                                         /*lookup_table_insert(lookup_table, lte);*/
284                                 }
285                                 dentry->lte = lte;
286                         }
287                 }
288         }
289         ret = ntfs_inode_get_security(ni,
290                                       OWNER_SECURITY_INFORMATION |
291                                       GROUP_SECURITY_INFORMATION |
292                                       DACL_SECURITY_INFORMATION  |
293                                       SACL_SECURITY_INFORMATION,
294                                       NULL, 0, &sd_size);
295         u8 sd[sd_size];
296         ret = ntfs_inode_get_security(ni,
297                                       OWNER_SECURITY_INFORMATION |
298                                       GROUP_SECURITY_INFORMATION |
299                                       DACL_SECURITY_INFORMATION  |
300                                       SACL_SECURITY_INFORMATION,
301                                       sd, sd_size, &sd_size);
302         dentry->security_id = tree_add_sd(tree, sd, sd_size);
303         return 0;
304 }
305
306 static int build_dentry_tree_ntfs(struct dentry *root_dentry,
307                                   const char *device,
308                                   struct lookup_table *lookup_table,
309                                   struct wim_security_data *sd,
310                                   int flags,
311                                   void *extra_arg)
312 {
313         ntfs_volume *vol;
314         ntfs_inode *root_ni;
315         int ret = 0;
316         struct sd_tree tree;
317         tree.sd = sd;
318         tree.root = NULL;
319         ntfs_volume **ntfs_vol_p = extra_arg;
320         
321         vol = ntfs_mount(device, MS_RDONLY);
322         if (!vol) {
323                 ERROR_WITH_ERRNO("Failed to mount NTFS volume `%s' read-only",
324                                  device);
325                 return WIMLIB_ERR_NTFS_3G;
326         }
327         root_ni = ntfs_inode_open(vol, FILE_root);
328         if (!root_ni) {
329                 ERROR_WITH_ERRNO("Failed to open root inode of NTFS volume "
330                                  "`%s'", device);
331                 ret = WIMLIB_ERR_NTFS_3G;
332                 goto out;
333         }
334         char path[4096];
335         path[0] = '/';
336         path[1] = '\0';
337         ret = __build_dentry_tree_ntfs(root_dentry, root_ni, path, 1,
338                                        lookup_table, &tree);
339
340 out:
341         if (ntfs_umount(vol, FALSE) != 0) {
342                 ERROR_WITH_ERRNO("Failed to unmount NTFS volume `%s'", device);
343                 if (ret == 0)
344                         ret = WIMLIB_ERR_NTFS_3G;
345         }
346         return ret;
347 }
348
349 WIMLIBAPI int wimlib_add_image_from_ntfs_volume(WIMStruct *w,
350                                                 const char *device,
351                                                 const char *name,
352                                                 const char *description,
353                                                 const char *flags_element,
354                                                 int flags)
355 {
356         if (flags & (WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE)) {
357                 ERROR("Cannot dereference files when capturing directly from NTFS");
358                 return WIMLIB_ERR_INVALID_PARAM;
359         }
360         return do_add_image(w, device, name, description, flags_element, flags,
361                             build_dentry_tree_ntfs,
362                             &w->ntfs_vol);
363 }
364
365 #else /* WITH_NTFS_3G */
366 WIMLIBAPI int wimlib_add_image_from_ntfs_volume(WIMStruct *w,
367                                                 const char *device,
368                                                 const char *name,
369                                                 const char *description,
370                                                 const char *flags_element,
371                                                 int flags)
372 {
373         ERROR("wimlib was compiled without support for NTFS-3g, so");
374         ERROR("we cannot capture a WIM image directly from a NTFS volume");
375         return WIMLIB_ERR_UNSUPPORTED;
376 }
377 #endif /* WITH_NTFS_3G */