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