unix_apply.c: Use O_NOFOLLOW when opening files
[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/resource.h"
33 #include "wimlib/timestamp.h"
34 #include "wimlib/unix_data.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 #ifndef O_NOFOLLOW
48 #  define O_NOFOLLOW 0
49 #endif
50
51 static int
52 unix_start_extract(const char *target, struct apply_ctx *ctx)
53 {
54         ctx->supported_features.hard_links = 1;
55         ctx->supported_features.symlink_reparse_points = 1;
56         ctx->supported_features.unix_data = 1;
57         return 0;
58 }
59
60 static int
61 unix_create_file(const char *path, struct apply_ctx *ctx, u64 *cookie_ret)
62 {
63         int fd = open(path, O_TRUNC | O_CREAT | O_WRONLY | O_NOFOLLOW, 0644);
64         if (fd < 0)
65                 return WIMLIB_ERR_OPEN;
66         close(fd);
67         return 0;
68 }
69
70 static int
71 unix_create_directory(const tchar *path, struct apply_ctx *ctx, u64 *cookie_ret)
72 {
73         struct stat stbuf;
74
75         if (mkdir(path, 0755)) {
76                 if (errno != EEXIST)
77                         return WIMLIB_ERR_MKDIR;
78                 if (lstat(path, &stbuf))
79                         return WIMLIB_ERR_MKDIR;
80                 errno = EEXIST;
81                 if (!S_ISDIR(stbuf.st_mode))
82                         return WIMLIB_ERR_MKDIR;
83         }
84         return 0;
85 }
86
87 static int
88 unix_makelink(const tchar *oldpath, const tchar *newpath,
89               int (*makelink)(const tchar *oldpath, const tchar *newpath))
90 {
91         if ((*makelink)(oldpath, newpath)) {
92                 if (errno != EEXIST)
93                         return WIMLIB_ERR_LINK;
94                 if (unlink(newpath))
95                         return WIMLIB_ERR_LINK;
96                 if ((*makelink)(oldpath, newpath))
97                         return WIMLIB_ERR_LINK;
98         }
99         return 0;
100 }
101 static int
102 unix_create_hardlink(const tchar *oldpath, const tchar *newpath,
103                      struct apply_ctx *ctx)
104 {
105         return unix_makelink(oldpath, newpath, link);
106 }
107
108 static int
109 unix_create_symlink(const tchar *oldpath, const tchar *newpath,
110                     struct apply_ctx *ctx)
111 {
112         return unix_makelink(oldpath, newpath, symlink);
113 }
114
115 static int
116 unix_extract_unnamed_stream(file_spec_t file,
117                             struct wim_lookup_table_entry *lte,
118                             struct apply_ctx *ctx)
119 {
120         const char *path = file.path;
121         struct filedes fd;
122         int raw_fd;
123         int ret;
124
125         raw_fd = open(path, O_WRONLY | O_TRUNC | O_NOFOLLOW);
126         if (raw_fd < 0)
127                 return WIMLIB_ERR_OPEN;
128         filedes_init(&fd, raw_fd);
129         ret = extract_full_stream_to_fd(lte, &fd);
130         if (filedes_close(&fd) && !ret)
131                 ret = WIMLIB_ERR_WRITE;
132         return ret;
133 }
134
135 static int
136 unix_set_unix_data(const tchar *path, const struct wimlib_unix_data *data,
137                    struct apply_ctx *ctx)
138 {
139         struct stat stbuf;
140
141         if (lstat(path, &stbuf))
142                 return WIMLIB_ERR_SET_SECURITY;
143         if (!S_ISLNK(stbuf.st_mode))
144                 if (chmod(path, data->mode))
145                         return WIMLIB_ERR_SET_SECURITY;
146         if (lchown(path, data->uid, data->gid))
147                 return WIMLIB_ERR_SET_SECURITY;
148         return 0;
149 }
150
151 static int
152 unix_set_timestamps(const tchar *path, u64 creation_time, u64 last_write_time,
153                     u64 last_access_time, struct apply_ctx *ctx)
154 {
155         int ret;
156
157 #ifdef HAVE_UTIMENSAT
158         /* Convert the WIM timestamps, which are accurate to 100 nanoseconds,
159          * into `struct timespec's for passing to utimensat(), which is accurate
160          * to 1 nanosecond. */
161
162         struct timespec ts[2];
163         ts[0] = wim_timestamp_to_timespec(last_access_time);
164         ts[1] = wim_timestamp_to_timespec(last_write_time);
165         ret = utimensat(AT_FDCWD, path, ts, AT_SYMLINK_NOFOLLOW);
166         if (ret)
167                 ret = errno;
168 #else
169         ret = ENOSYS;
170 #endif
171
172         if (ret == ENOSYS) {
173                 /* utimensat() not implemented or not available */
174         #ifdef HAVE_LUTIMES
175                 /* Convert the WIM timestamps, which are accurate to 100
176                  * nanoseconds, into `struct timeval's for passing to lutimes(),
177                  * which is accurate to 1 microsecond. */
178                 struct timeval tv[2];
179                 tv[0] = wim_timestamp_to_timeval(last_access_time);
180                 tv[1] = wim_timestamp_to_timeval(last_write_time);
181                 ret = lutimes(path, tv);
182                 if (ret)
183                         ret = errno;
184         #endif
185         }
186
187         if (ret == ENOSYS) {
188                 /* utimensat() and lutimes() both not implemented or not
189                  * available */
190         #ifdef HAVE_UTIME
191                 /* Convert the WIM timestamps, which are accurate to 100
192                  * nanoseconds, into a `struct utimbuf's for passing to
193                  * utime(), which is accurate to 1 second. */
194                 struct utimbuf buf;
195                 buf.actime = wim_timestamp_to_unix(last_access_time);
196                 buf.modtime = wim_timestamp_to_unix(last_write_time);
197                 ret = utime(path, &buf);
198         #endif
199         }
200         if (ret)
201                 return WIMLIB_ERR_SET_TIMESTAMPS;
202         return 0;
203 }
204
205 const struct apply_operations unix_apply_ops = {
206         .name = "UNIX",
207
208         .start_extract          = unix_start_extract,
209         .create_file            = unix_create_file,
210         .create_directory       = unix_create_directory,
211         .create_hardlink        = unix_create_hardlink,
212         .create_symlink         = unix_create_symlink,
213         .extract_unnamed_stream = unix_extract_unnamed_stream,
214         .set_unix_data          = unix_set_unix_data,
215         .set_timestamps         = unix_set_timestamps,
216
217         .path_separator = '/',
218         .path_max = PATH_MAX,
219
220         .requires_target_in_paths = 1,
221         .supports_case_sensitive_filenames = 1,
222 };
223
224 #endif /* !__WIN32__ */