NTFS-3g apply: Open extracted files by MFT reference
[wimlib] / src / unix_apply.c
1 /*
2  * unix_apply.c - Code to apply files from a WIM image on UNIX.
3  */
4
5 /*
6  * Copyright (C) 2012, 2013 Eric Biggers
7  *
8  * This file is part of wimlib, a library for working with WIM files.
9  *
10  * wimlib is free software; you can redistribute it and/or modify it under the
11  * terms of the GNU General Public License as published by the Free
12  * Software Foundation; either version 3 of the License, or (at your option)
13  * any later version.
14  *
15  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
16  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
17  * A PARTICULAR PURPOSE. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with wimlib; if not, see http://www.gnu.org/licenses/.
22  */
23
24 #ifndef __WIN32__
25
26 #ifdef HAVE_CONFIG_H
27 #  include "config.h"
28 #endif
29
30 #include "wimlib/apply.h"
31 #include "wimlib/error.h"
32 #include "wimlib/lookup_table.h"
33 #include "wimlib/resource.h"
34 #include "wimlib/timestamp.h"
35
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <limits.h>
39 #include <sys/stat.h>
40 #include <sys/types.h>
41 #include <unistd.h>
42
43 #ifdef HAVE_UTIME_H
44 #  include <utime.h>
45 #endif
46
47 static int
48 unix_start_extract(const char *target, struct apply_ctx *ctx)
49 {
50         ctx->supported_features.hard_links = 1;
51         ctx->supported_features.symlink_reparse_points = 1;
52         ctx->supported_features.unix_data = 1;
53         return 0;
54 }
55
56 static int
57 unix_create_file(const char *path, struct apply_ctx *ctx, u64 *cookie_ret)
58 {
59         int fd = open(path, O_TRUNC | O_CREAT | O_WRONLY, 0644);
60         if (fd < 0)
61                 return WIMLIB_ERR_OPEN;
62         close(fd);
63         return 0;
64 }
65
66 static int
67 unix_create_directory(const tchar *path, struct apply_ctx *ctx, u64 *cookie_ret)
68 {
69         struct stat stbuf;
70
71         if (mkdir(path, 0755)) {
72                 if (errno != EEXIST)
73                         return WIMLIB_ERR_MKDIR;
74                 if (lstat(path, &stbuf))
75                         return WIMLIB_ERR_STAT;
76                 errno = EEXIST;
77                 if (!S_ISDIR(stbuf.st_mode))
78                         return WIMLIB_ERR_NOTDIR;
79         }
80         return 0;
81 }
82
83 static int
84 unix_makelink(const tchar *oldpath, const tchar *newpath,
85               int (*makelink)(const tchar *oldpath, const tchar *newpath))
86 {
87         if ((*makelink)(oldpath, newpath)) {
88                 if (errno == EEXIST) {
89                         if (unlink(newpath))
90                                 return WIMLIB_ERR_LINK;
91                         if ((*makelink)(oldpath, newpath))
92                                 return WIMLIB_ERR_LINK;
93                         return 0;
94                 }
95                 return WIMLIB_ERR_LINK;
96         }
97         return 0;
98 }
99 static int
100 unix_create_hardlink(const tchar *oldpath, const tchar *newpath,
101                      struct apply_ctx *ctx)
102 {
103         return unix_makelink(oldpath, newpath, link);
104 }
105
106 static int
107 unix_create_symlink(const tchar *oldpath, const tchar *newpath,
108                     struct apply_ctx *ctx)
109 {
110         return unix_makelink(oldpath, newpath, symlink);
111 }
112
113 static int
114 unix_extract_unnamed_stream(file_spec_t file,
115                             struct wim_lookup_table_entry *lte,
116                             struct apply_ctx *ctx)
117 {
118         const char *path = file.path;
119         struct filedes fd;
120         int raw_fd;
121         int ret;
122
123         raw_fd = open(path, O_WRONLY | O_TRUNC);
124         if (raw_fd < 0)
125                 return WIMLIB_ERR_OPEN;
126         filedes_init(&fd, raw_fd);
127         ret = extract_wim_resource_to_fd(lte, &fd, wim_resource_size(lte));
128         if (filedes_close(&fd) && !ret)
129                 ret = WIMLIB_ERR_WRITE;
130         return ret;
131 }
132
133 static int
134 unix_set_unix_data(const tchar *path, const struct wimlib_unix_data *data,
135                    struct apply_ctx *ctx)
136 {
137         struct stat stbuf;
138
139         if (lstat(path, &stbuf))
140                 return WIMLIB_ERR_SET_SECURITY;
141         if (!S_ISLNK(stbuf.st_mode))
142                 if (chmod(path, data->mode))
143                         return WIMLIB_ERR_SET_SECURITY;
144         if (lchown(path, data->uid, data->gid))
145                 return WIMLIB_ERR_SET_SECURITY;
146         return 0;
147 }
148
149 static int
150 unix_set_timestamps(const tchar *path, u64 creation_time, u64 last_write_time,
151                     u64 last_access_time, struct apply_ctx *ctx)
152 {
153         int ret;
154
155 #ifdef HAVE_UTIMENSAT
156         /* Convert the WIM timestamps, which are accurate to 100 nanoseconds,
157          * into `struct timespec's for passing to utimensat(), which is accurate
158          * to 1 nanosecond. */
159
160         struct timespec ts[2];
161         ts[0] = wim_timestamp_to_timespec(last_access_time);
162         ts[1] = wim_timestamp_to_timespec(last_write_time);
163         ret = utimensat(AT_FDCWD, path, ts, AT_SYMLINK_NOFOLLOW);
164         if (ret)
165                 ret = errno;
166 #else
167         ret = ENOSYS;
168 #endif
169
170         if (ret == ENOSYS) {
171                 /* utimensat() not implemented or not available */
172         #ifdef HAVE_LUTIMES
173                 /* Convert the WIM timestamps, which are accurate to 100
174                  * nanoseconds, into `struct timeval's for passing to lutimes(),
175                  * which is accurate to 1 microsecond. */
176                 struct timeval tv[2];
177                 tv[0] = wim_timestamp_to_timeval(last_access_time);
178                 tv[1] = wim_timestamp_to_timeval(last_write_time);
179                 ret = lutimes(path, tv);
180                 if (ret)
181                         ret = errno;
182         #endif
183         }
184
185         if (ret == ENOSYS) {
186                 /* utimensat() and lutimes() both not implemented or not
187                  * available */
188         #ifdef HAVE_UTIME
189                 /* Convert the WIM timestamps, which are accurate to 100
190                  * nanoseconds, into a `struct utimbuf's for passing to
191                  * utime(), which is accurate to 1 second. */
192                 struct utimbuf buf;
193                 buf.actime = wim_timestamp_to_unix(last_access_time);
194                 buf.modtime = wim_timestamp_to_unix(last_write_time);
195                 ret = utime(path, &buf);
196         #endif
197         }
198         if (ret)
199                 return WIMLIB_ERR_SET_TIMESTAMPS;
200         return 0;
201 }
202
203 const struct apply_operations unix_apply_ops = {
204         .name = "UNIX",
205
206         .start_extract          = unix_start_extract,
207         .create_file            = unix_create_file,
208         .create_directory       = unix_create_directory,
209         .create_hardlink        = unix_create_hardlink,
210         .create_symlink         = unix_create_symlink,
211         .extract_unnamed_stream = unix_extract_unnamed_stream,
212         .set_unix_data          = unix_set_unix_data,
213         .set_timestamps         = unix_set_timestamps,
214
215         .path_separator = '/',
216         .path_max = PATH_MAX,
217
218         .requires_target_in_paths = 1,
219         .supports_case_sensitive_filenames = 1,
220 };
221
222 #endif /* !__WIN32__ */