unix_apply.c: Allow creating symlinks and hardlinks over existing 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/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)
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)
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(const tchar *path,
115                             struct wim_lookup_table_entry *lte,
116                             struct apply_ctx *ctx)
117 {
118         struct filedes fd;
119         int raw_fd;
120         int ret;
121
122         raw_fd = open(path, O_WRONLY | O_TRUNC);
123         if (raw_fd < 0)
124                 return WIMLIB_ERR_OPEN;
125         filedes_init(&fd, raw_fd);
126         ret = extract_wim_resource_to_fd(lte, &fd, wim_resource_size(lte));
127         if (filedes_close(&fd) && !ret)
128                 ret = WIMLIB_ERR_WRITE;
129         return ret;
130 }
131
132 static int
133 unix_set_unix_data(const tchar *path, const struct wimlib_unix_data *data,
134                    struct apply_ctx *ctx)
135 {
136         struct stat stbuf;
137
138         if (lstat(path, &stbuf))
139                 return WIMLIB_ERR_SET_SECURITY;
140         if (!S_ISLNK(stbuf.st_mode))
141                 if (chmod(path, data->mode))
142                         return WIMLIB_ERR_SET_SECURITY;
143         if (lchown(path, data->uid, data->gid))
144                 return WIMLIB_ERR_SET_SECURITY;
145         return 0;
146 }
147
148 static int
149 unix_set_timestamps(const tchar *path, u64 creation_time, u64 last_write_time,
150                     u64 last_access_time, struct apply_ctx *ctx)
151 {
152         int ret;
153
154 #ifdef HAVE_UTIMENSAT
155         /* Convert the WIM timestamps, which are accurate to 100 nanoseconds,
156          * into `struct timespec's for passing to utimensat(), which is accurate
157          * to 1 nanosecond. */
158
159         struct timespec ts[2];
160         ts[0] = wim_timestamp_to_timespec(last_access_time);
161         ts[1] = wim_timestamp_to_timespec(last_write_time);
162         ret = utimensat(AT_FDCWD, path, ts, AT_SYMLINK_NOFOLLOW);
163         if (ret)
164                 ret = errno;
165 #else
166         ret = ENOSYS;
167 #endif
168
169         if (ret == ENOSYS) {
170                 /* utimensat() not implemented or not available */
171         #ifdef HAVE_LUTIMES
172                 /* Convert the WIM timestamps, which are accurate to 100
173                  * nanoseconds, into `struct timeval's for passing to lutimes(),
174                  * which is accurate to 1 microsecond. */
175                 struct timeval tv[2];
176                 tv[0] = wim_timestamp_to_timeval(last_access_time);
177                 tv[1] = wim_timestamp_to_timeval(last_write_time);
178                 ret = lutimes(path, tv);
179                 if (ret)
180                         ret = errno;
181         #endif
182         }
183
184         if (ret == ENOSYS) {
185                 /* utimensat() and lutimes() both not implemented or not
186                  * available */
187         #ifdef HAVE_UTIME
188                 /* Convert the WIM timestamps, which are accurate to 100
189                  * nanoseconds, into a `struct utimbuf's for passing to
190                  * utime(), which is accurate to 1 second. */
191                 struct utimbuf buf;
192                 buf.actime = wim_timestamp_to_unix(last_access_time);
193                 buf.modtime = wim_timestamp_to_unix(last_write_time);
194                 ret = utime(path, &buf);
195         #endif
196         }
197         if (ret)
198                 return WIMLIB_ERR_SET_TIMESTAMPS;
199         return 0;
200 }
201
202 const struct apply_operations unix_apply_ops = {
203         .name = "UNIX",
204
205         .start_extract          = unix_start_extract,
206         .create_file            = unix_create_file,
207         .create_directory       = unix_create_directory,
208         .create_hardlink        = unix_create_hardlink,
209         .create_symlink         = unix_create_symlink,
210         .extract_unnamed_stream = unix_extract_unnamed_stream,
211         .set_unix_data          = unix_set_unix_data,
212         .set_timestamps         = unix_set_timestamps,
213
214         .path_separator = '/',
215         .path_max = PATH_MAX,
216
217         .requires_target_in_paths = 1,
218         .supports_case_sensitive_filenames = 1,
219 };
220
221 #endif /* !__WIN32__ */