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