]> wimlib.net Git - wimlib/blob - src/ntfs.c
f0fb2276262253e8c93971a1b8cb3f196546dd19
[wimlib] / src / ntfs.c
1 /*
2  * Copyright (C) 2012 Eric Biggers
3  *
4  * This file is part of wimlib, a library for working with WIM files.
5  *
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)
9  * any later version.
10  *
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
14  * details.
15  *
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/.
18  */
19
20 #include "config.h"
21 #include "wimlib_internal.h"
22
23
24 #ifdef WITH_NTFS_3G
25 #include "dentry.h"
26 #include "lookup_table.h"
27 #include <ntfs-3g/attrib.h>
28 #include <ntfs-3g/misc.h>
29 #include <ntfs-3g/reparse.h>
30 #include <ntfs-3g/security.h>
31 #include <ntfs-3g/volume.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34
35 struct ntfs_apply_args {
36         struct SECURITY_API *scapi;
37         int flags;
38         WIMStruct *w;
39 };
40
41 /*
42  *              Initializations before calling ntfs_get_file_security()
43  *      ntfs_set_file_security() and ntfs_read_directory()
44  *
45  *      Returns an (obscured) struct SECURITY_API* needed for further calls
46  *              NULL if device is mounted (EBUSY)
47  */
48
49 static struct SECURITY_API *_ntfs_initialize_file_security(const char *device,
50                                                            unsigned long flags)
51 {
52         ntfs_volume *vol;
53         unsigned long mntflag;
54         int mnt;
55         struct SECURITY_API *scapi;
56         struct SECURITY_CONTEXT *scx;
57
58         scapi = (struct SECURITY_API*)NULL;
59         mnt = ntfs_check_if_mounted(device, &mntflag);
60         if (!mnt && !(mntflag & NTFS_MF_MOUNTED)) {
61                 vol = ntfs_mount(device, flags);
62                 if (vol) {
63                         scapi = (struct SECURITY_API*)
64                                 ntfs_malloc(sizeof(struct SECURITY_API));
65                         if (!ntfs_volume_get_free_space(vol)
66                             && scapi) {
67                                 scapi->magic = MAGIC_API;
68                                 scapi->seccache = (struct PERMISSIONS_CACHE*)NULL;
69                                 scx = &scapi->security;
70                                 scx->vol = vol;
71                                 scx->uid = 0;
72                                 scx->gid = 0;
73                                 scx->pseccache = &scapi->seccache;
74                                 scx->vol->secure_flags = (1 << SECURITY_DEFAULT) |
75                                                         (1 << SECURITY_RAW);
76                                 ntfs_open_secure(vol);
77                                 ntfs_build_mapping(scx,(const char*)NULL,TRUE);
78                         } else {
79                                 if (scapi)
80                                         free(scapi);
81                                 else
82                                         errno = ENOMEM;
83                                 mnt = ntfs_umount(vol,FALSE);
84                                 scapi = (struct SECURITY_API*)NULL;
85                         }
86                 }
87         } else
88                 errno = EBUSY;
89         return (scapi);
90 }
91
92 static int extract_resource_to_ntfs_attr(WIMStruct *w, const struct resource_entry *entry, 
93                                          ntfs_attr *na)
94 {
95         u8 buf[min(entry->original_size, WIM_CHUNK_SIZE)];
96         u64 num_chunks = (entry->original_size + WIM_CHUNK_SIZE - 1) / WIM_CHUNK_SIZE;
97         u64 n = WIM_CHUNK_SIZE;
98         int res_ctype = wim_resource_compression_type(w, entry);
99         u64 offset = 0;
100         for (u64 i = 0; i < num_chunks; i++) {
101                 DEBUG("Write chunk %u of %u", i + 1, num_chunks);
102                 int ret;
103                 if (i == num_chunks - 1) {
104                         n = entry->original_size % WIM_CHUNK_SIZE;
105                         if (n == 0) {
106                                 n = WIM_CHUNK_SIZE;
107                         }
108                 }
109
110                 ret = read_resource(w->fp, entry->size, entry->original_size,
111                                     entry->offset, res_ctype, n, offset, buf);
112                 if (ret != 0)
113                         return ret;
114
115                 if (ntfs_attr_pwrite(na, offset, n, buf) != n) {
116                         ERROR("Failed to write to NTFS data stream");
117                         return WIMLIB_ERR_WRITE;
118                 }
119                 offset += n;
120         }
121         return 0;
122 }
123
124 static int write_ntfs_data_streams(ntfs_inode *ni, const struct dentry *dentry,
125                                    WIMStruct *w)
126 {
127         ntfs_attr *na;
128         struct lookup_table_entry *lte;
129         int ret;
130
131
132         if (dentry_is_directory(dentry)) {
133                 wimlib_assert(!dentry_first_lte(dentry, w->lookup_table));
134                 return 0;
135         }
136
137         DEBUG("Writing NTFS data streams for `%s'", dentry->full_path_utf8);
138
139         wimlib_assert(dentry->num_ads == 0);
140
141         lte = dentry_stream_lte(dentry, 0, w->lookup_table);
142         if (lte && lte->resource_entry.original_size != 0) {
143
144                 na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
145                 if (!na) {
146                         ERROR_WITH_ERRNO("Failed to open unnamed data stream of "
147                                          "extracted file `%s'",
148                                          dentry->full_path_utf8);
149                         return WIMLIB_ERR_NTFS_3G;
150                 }
151                 ret = extract_resource_to_ntfs_attr(w, &lte->resource_entry, na);
152                 if (ret != 0)
153                         return ret;
154                 ntfs_attr_close(na);
155         }
156
157         return 0;
158 }
159
160 static int ntfs_apply_dentry(struct dentry *dentry, void *arg)
161 {
162         struct ntfs_apply_args *args = arg;
163         struct SECURITY_API *scapi   = args->scapi;
164         ntfs_volume *vol             = scapi->security.vol;
165         int flags                    = args->flags;
166         WIMStruct *w                 = args->w;
167         int ret = 0;
168         ntfs_inode *dir_ni, *ni;
169         le32 secid;
170
171         DEBUG("Applying `%s'", dentry->full_path_utf8);
172
173         if (dentry_is_root(dentry))
174                 return 0;
175
176         if (flags & WIMLIB_EXTRACT_FLAG_VERBOSE) {
177                 wimlib_assert(dentry->full_path_utf8);
178                 puts(dentry->full_path_utf8);
179         }
180
181         char *p = dentry->full_path_utf8 + dentry->full_path_utf8_len;
182         do {
183                 p--;
184         } while (*p != '/');
185
186         char orig = *p;
187         *p = '\0';
188         const char *dir_name;
189         if (p == dentry->full_path_utf8)
190                 dir_name = "/"; 
191         else
192                 dir_name = dentry->full_path_utf8;
193
194         dir_ni = ntfs_pathname_to_inode(vol, NULL, dir_name);
195         if (!dir_ni) {
196                 ret = WIMLIB_ERR_NTFS_3G;
197                 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
198                                  dir_name);
199                 goto out;
200         }
201         DEBUG("Found NTFS inode for `%s'", dir_name);
202         *p = orig;
203
204         ret = 0;
205         secid = 0;
206         if (dentry_is_directory(dentry)) {
207                 ni = ntfs_create(dir_ni, 0, (ntfschar*)dentry->file_name,
208                                  dentry->file_name_len / 2, S_IFDIR);
209         } else {
210                 ni = ntfs_create(dir_ni, 0, (ntfschar*)dentry->file_name,
211                                  dentry->file_name_len / 2, S_IFREG);
212         }
213         if (!ni) {
214                 *p = orig;
215                 ERROR_WITH_ERRNO("Could not create NTFS object for `%s'",
216                                  dentry->full_path_utf8);
217                 ret = WIMLIB_ERR_NTFS_3G;
218                 goto out;
219         }
220
221         ret = write_ntfs_data_streams(ni, dentry, w);
222         if (ret != 0)
223                 goto out;
224         if (ntfs_inode_close_in_dir(ni, dir_ni) != 0) {
225                 ERROR_WITH_ERRNO("Failed to close new inode");
226                 ret = WIMLIB_ERR_NTFS_3G;
227                 goto out;
228         } else {
229                 DEBUG("Closed inode `%s'", dentry->full_path_utf8);
230         }
231         if (ntfs_inode_close(dir_ni) != 0) {
232                 ret = WIMLIB_ERR_NTFS_3G;
233                 ERROR_WITH_ERRNO("Failed to close directory inode");
234                 goto out;
235         } else {
236                 DEBUG("Closed parent inode");
237         }
238         if (dentry->security_id != -1) {
239                 const struct wim_security_data *sd = wim_security_data(w);
240                 wimlib_assert(dentry->security_id < sd->num_entries);
241                 DEBUG("Applying security descriptor %d to `%s'",
242                       dentry->security_id, dentry->full_path_utf8);
243                 ret = ntfs_set_file_security(scapi, dentry->full_path_utf8,
244                                              ~0,
245                                              sd->descriptors[dentry->security_id]);
246                 if (ret != 0) {
247                         ERROR_WITH_ERRNO("Failed to set security data on `%s'",
248                                         dentry->full_path_utf8);
249                         ret = WIMLIB_ERR_NTFS_3G;
250                         goto out;
251                 }
252         }
253         DEBUG("Setting file attributes 0x%x on `%s'",
254                dentry->attributes, dentry->full_path_utf8);
255         if (!ntfs_set_file_attributes(scapi, dentry->full_path_utf8,
256                                       dentry->attributes)) {
257                 ERROR_WITH_ERRNO("Failed to set NTFS file attributes on `%s'",
258                                  dentry->full_path_utf8);
259                 ret = WIMLIB_ERR_NTFS_3G;
260                 goto out;
261         }
262         if (dentry->attributes & FILE_ATTR_REPARSE_POINT) {
263                 struct lookup_table_entry *lte;
264                 ntfs_inode *ni;
265                 lte = dentry_first_lte(dentry, w->lookup_table);
266                 if (!lte) {
267                         ERROR("Could not find reparse data for `%s'",
268                               dentry->full_path_utf8);
269                         ret = WIMLIB_ERR_INVALID_DENTRY;
270                         goto out;
271                 }
272                 
273                 if (!(ni = ntfs_pathname_to_inode(vol, NULL, dentry->full_path_utf8))
274                      || (ret = ntfs_set_ntfs_reparse_data(ni, lte->symlink_buf,
275                                                              lte->resource_entry.original_size,
276                                                              0)) != 0)
277                 {
278                         ERROR_WITH_ERRNO("Failed to set NTFS reparse data on "
279                                          "`%s'", dentry->full_path_utf8);
280                         ret = WIMLIB_ERR_NTFS_3G;
281                         goto out;
282                 }
283         }
284 out:
285         return ret;
286 }
287
288 static int do_ntfs_apply(WIMStruct *w, const char *device, int flags)
289 {
290         struct SECURITY_API *scapi;
291         int ret;
292         
293         scapi = _ntfs_initialize_file_security(device, 0);
294         if (!scapi) {
295                 ERROR_WITH_ERRNO("Failed to initialize NTFS file security API "
296                                  "on NTFS volume `%s'", device);
297                 return WIMLIB_ERR_NTFS_3G;
298         }
299         struct ntfs_apply_args args = {
300                 .scapi = scapi,
301                 .flags = flags,
302                 .w     = w,
303         };
304         ret = for_dentry_in_tree(wim_root_dentry(w), ntfs_apply_dentry,
305                                  &args);
306 out:
307         if (!ntfs_leave_file_security(scapi)) {
308                 ERROR_WITH_ERRNO("Failed to leave file security");
309                 ret = WIMLIB_ERR_NTFS_3G;
310         }
311         return ret;
312 }
313
314 WIMLIBAPI int wimlib_apply_image_to_ntfs_volume(WIMStruct *w, int image,
315                                                 const char *device, int flags)
316 {
317         int ret;
318
319         if (!device)
320                 return WIMLIB_ERR_INVALID_PARAM;
321         if (image == WIM_ALL_IMAGES) {
322                 ERROR("Can only apply a single image when applying "
323                       "directly to a NTFS volume");
324                 return WIMLIB_ERR_INVALID_PARAM;
325         }
326         if (flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | WIMLIB_EXTRACT_FLAG_HARDLINK)) {
327                 ERROR("Cannot specifcy symlink or hardlink flags when applying ");
328                 ERROR("directly to a NTFS volume");
329                 return WIMLIB_ERR_INVALID_PARAM;
330         }
331         ret = wimlib_select_image(w, image);
332         if (ret != 0)
333                 return ret;
334
335         /*if (getuid() != 0) {*/
336                 /*ERROR("We are not root, but NTFS-3g requires root privileges to set arbitrary");*/
337                 /*ERROR("security data on the NTFS filesystem.  Please run this program as root");*/
338                 /*ERROR("if you want to extract a WIM image while preserving NTFS-specific");*/
339                 /*ERROR("information.");*/
340
341                 /*return WIMLIB_ERR_NOT_ROOT;*/
342         /*}*/
343         return do_ntfs_apply(w, device, flags);
344 }
345
346 #else /* WITH_NTFS_3G */
347 WIMLIBAPI int wimlib_apply_image_to_ntfs_volume(WIMStruct *w, int image,
348                                                 const char *device, int flags)
349 {
350         ERROR("wimlib was compiled without support for NTFS-3g, so");
351         ERROR("we cannot apply a WIM image directly to a NTFS volume");
352         return WIMLIB_ERR_UNSUPPORTED;
353 }
354 #endif /* WITH_NTFS_3G */