]> wimlib.net Git - wimlib/blob - src/unix_capture.c
f3e9f0e564866f3b7b760a39f5f64627ec5d34dc
[wimlib] / src / unix_capture.c
1 /*
2  * Copyright (C) 2013 Eric Biggers
3  *
4  * This file is part of wimlib, a library for working with WIM files.
5  *
6  * wimlib is free software; you can redistribute it and/or modify it under the
7  * terms of the GNU General Public License as published by the Free
8  * Software Foundation; either version 3 of the License, or (at your option)
9  * any later version.
10  *
11  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
12  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13  * A PARTICULAR PURPOSE. See the GNU General Public License for more
14  * details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with wimlib; if not, see http://www.gnu.org/licenses/.
18  */
19
20 #ifndef __WIN32__
21
22 #include "wimlib_internal.h"
23 #include "dentry.h"
24 #include "lookup_table.h"
25 #include "timestamp.h"
26
27 #include <dirent.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #include <sys/stat.h>
31
32 static int
33 unix_capture_regular_file(const char *path,
34                           u64 size,
35                           struct wim_inode *inode,
36                           struct wim_lookup_table *lookup_table)
37 {
38         inode->i_attributes = FILE_ATTRIBUTE_NORMAL;
39
40         /* Empty files do not have to have a lookup table entry. */
41         if (size != 0) {
42                 struct wim_lookup_table_entry *lte;
43                 char *file_on_disk;
44
45                 file_on_disk = STRDUP(path);
46                 if (!file_on_disk)
47                         return WIMLIB_ERR_NOMEM;
48                 lte = new_lookup_table_entry();
49                 if (!lte) {
50                         FREE(file_on_disk);
51                         return WIMLIB_ERR_NOMEM;
52                 }
53                 lte->file_on_disk = file_on_disk;
54                 lte->resource_location = RESOURCE_IN_FILE_ON_DISK;
55                 lte->resource_entry.original_size = size;
56                 lookup_table_insert_unhashed(lookup_table, lte, inode, 0);
57                 inode->i_lte = lte;
58         }
59         return 0;
60 }
61
62 static int
63 unix_build_dentry_tree_recursive(struct wim_dentry **root_ret,
64                                  char *path,
65                                  size_t path_len,
66                                  struct add_image_params *params);
67
68 static int
69 unix_capture_directory(struct wim_dentry *dir_dentry,
70                        char *path,
71                        size_t path_len,
72                        struct add_image_params *params)
73 {
74
75         DIR *dir;
76         struct dirent *entry;
77         struct wim_dentry *child;
78         int ret;
79
80         dir_dentry->d_inode->i_attributes = FILE_ATTRIBUTE_DIRECTORY;
81         dir = opendir(path);
82         if (!dir) {
83                 ERROR_WITH_ERRNO("Failed to open the directory `%s'",
84                                  path);
85                 return WIMLIB_ERR_OPEN;
86         }
87
88         /* Recurse on directory contents */
89         ret = 0;
90         for (;;) {
91                 errno = 0;
92                 entry = readdir(dir);
93                 if (!entry) {
94                         if (errno) {
95                                 ret = WIMLIB_ERR_READ;
96                                 ERROR_WITH_ERRNO("Error reading the "
97                                                  "directory `%s'", path);
98                         }
99                         break;
100                 }
101
102                 if (entry->d_name[0] == '.' && (entry->d_name[1] == '\0'
103                       || (entry->d_name[1] == '.' && entry->d_name[2] == '\0')))
104                                 continue;
105
106                 size_t name_len = strlen(entry->d_name);
107
108                 path[path_len] = '/';
109                 memcpy(&path[path_len + 1], entry->d_name, name_len + 1);
110                 ret = unix_build_dentry_tree_recursive(&child,
111                                                        path,
112                                                        path_len + 1 + name_len,
113                                                        params);
114                 if (ret)
115                         break;
116                 if (child)
117                         dentry_add_child(dir_dentry, child);
118         }
119         closedir(dir);
120         return ret;
121 }
122
123 static int
124 unix_capture_symlink(struct wim_dentry **root_p,
125                      const char *path,
126                      struct wim_inode *inode,
127                      struct add_image_params *params)
128 {
129         char deref_name_buf[4096];
130         ssize_t deref_name_len;
131         int ret;
132
133         inode->i_attributes = FILE_ATTRIBUTE_REPARSE_POINT;
134         inode->i_reparse_tag = WIM_IO_REPARSE_TAG_SYMLINK;
135
136         /* The idea here is to call readlink() to get the UNIX target of
137          * the symbolic link, then turn the target into a reparse point
138          * data buffer that contains a relative or absolute symbolic
139          * link (NOT a junction point or *full* path symbolic link with
140          * drive letter).
141          */
142         deref_name_len = readlink(path, deref_name_buf,
143                                   sizeof(deref_name_buf) - 1);
144         if (deref_name_len >= 0) {
145                 char *dest = deref_name_buf;
146
147                 dest[deref_name_len] = '\0';
148                 DEBUG("Read symlink `%s'", dest);
149
150                 if ((params->add_image_flags & WIMLIB_ADD_IMAGE_FLAG_RPFIX) &&
151                      dest[0] == '/')
152                 {
153                         dest = capture_fixup_absolute_symlink(dest,
154                                                               params->capture_root_ino,
155                                                               params->capture_root_dev);
156                         if (!dest) {
157                                 WARNING("Ignoring out of tree absolute symlink "
158                                         "\"%s\" -> \"%s\"\n"
159                                         "          (Use --norpfix to capture "
160                                         "absolute symlinks as-is)",
161                                         path, deref_name_buf);
162                                 free_dentry(*root_p);
163                                 *root_p = NULL;
164                                 return 0;
165                         }
166                         inode->i_not_rpfixed = 0;
167                 }
168                 ret = wim_inode_set_symlink(inode, dest, params->lookup_table);
169                 if (ret == 0) {
170                         /* Unfortunately, Windows seems to have the concept of
171                          * "file" symbolic links as being different from
172                          * "directory" symbolic links...  so
173                          * FILE_ATTRIBUTE_DIRECTORY needs to be set on the
174                          * symbolic link if the *target* of the symbolic link is
175                          * a directory.  */
176                         struct stat stbuf;
177                         if (stat(path, &stbuf) == 0 && S_ISDIR(stbuf.st_mode))
178                                 inode->i_attributes |= FILE_ATTRIBUTE_DIRECTORY;
179                 }
180         } else {
181                 ERROR_WITH_ERRNO("Failed to read target of "
182                                  "symbolic link `%s'", path);
183                 ret = WIMLIB_ERR_READLINK;
184         }
185         return ret;
186 }
187
188 static int
189 unix_build_dentry_tree_recursive(struct wim_dentry **root_ret,
190                                  char *path,
191                                  size_t path_len,
192                                  struct add_image_params *params)
193 {
194         struct wim_dentry *root = NULL;
195         int ret = 0;
196         struct wim_inode *inode;
197
198         if (exclude_path(path, path_len, params->config, true)) {
199                 if ((params->add_image_flags & WIMLIB_ADD_IMAGE_FLAG_EXCLUDE_VERBOSE)
200                     && params->progress_func)
201                 {
202                         union wimlib_progress_info info;
203                         info.scan.cur_path = path;
204                         info.scan.excluded = true;
205                         params->progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY, &info);
206                 }
207                 goto out;
208         }
209
210         if ((params->add_image_flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE)
211             && params->progress_func)
212         {
213                 union wimlib_progress_info info;
214                 info.scan.cur_path = path;
215                 info.scan.excluded = false;
216                 params->progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY, &info);
217         }
218
219         struct stat stbuf;
220         int (*stat_fn)(const char *restrict, struct stat *restrict);
221         if ((params->add_image_flags & WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE) ||
222             (params->add_image_flags & WIMLIB_ADD_IMAGE_FLAG_ROOT))
223                 stat_fn = stat;
224         else
225                 stat_fn = lstat;
226
227         ret = (*stat_fn)(path, &stbuf);
228         if (ret != 0) {
229                 ERROR_WITH_ERRNO("Failed to stat `%s'", path);
230                 goto out;
231         }
232         if (!S_ISREG(stbuf.st_mode) && !S_ISDIR(stbuf.st_mode)
233             && !S_ISLNK(stbuf.st_mode)) {
234                 ERROR("`%s' is not a regular file, directory, or symbolic link.",
235                       path);
236                 ret = WIMLIB_ERR_SPECIAL_FILE;
237                 goto out;
238         }
239
240         ret = inode_table_new_dentry(params->inode_table,
241                                      path_basename_with_len(path, path_len),
242                                      stbuf.st_ino, stbuf.st_dev, false, &root);
243         if (ret)
244                 goto out;
245
246         inode = root->d_inode;
247
248         if (inode->i_nlink > 1) /* Already captured this inode? */
249                 goto out;
250
251 #ifdef HAVE_STAT_NANOSECOND_PRECISION
252         inode->i_creation_time = timespec_to_wim_timestamp(stbuf.st_mtim);
253         inode->i_last_write_time = timespec_to_wim_timestamp(stbuf.st_mtim);
254         inode->i_last_access_time = timespec_to_wim_timestamp(stbuf.st_atim);
255 #else
256         inode->i_creation_time = unix_timestamp_to_wim(stbuf.st_mtime);
257         inode->i_last_write_time = unix_timestamp_to_wim(stbuf.st_mtime);
258         inode->i_last_access_time = unix_timestamp_to_wim(stbuf.st_atime);
259 #endif
260         inode->i_resolved = 1;
261         if (params->add_image_flags & WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA) {
262                 ret = inode_set_unix_data(inode, stbuf.st_uid,
263                                           stbuf.st_gid,
264                                           stbuf.st_mode,
265                                           params->lookup_table,
266                                           UNIX_DATA_ALL | UNIX_DATA_CREATE);
267                 if (ret)
268                         goto out;
269         }
270         params->add_image_flags &= ~WIMLIB_ADD_IMAGE_FLAG_ROOT;
271         if (S_ISREG(stbuf.st_mode))
272                 ret = unix_capture_regular_file(path, stbuf.st_size,
273                                                 inode, params->lookup_table);
274         else if (S_ISDIR(stbuf.st_mode))
275                 ret = unix_capture_directory(root, path, path_len, params);
276         else
277                 ret = unix_capture_symlink(&root, path, inode, params);
278 out:
279         if (ret == 0)
280                 *root_ret = root;
281         else
282                 free_dentry_tree(root, params->lookup_table);
283         return ret;
284 }
285
286 /*
287  * unix_build_dentry_tree():
288  *      Builds a tree of WIM dentries from an on-disk directory tree (UNIX
289  *      version; no NTFS-specific data is captured).
290  *
291  * @root_ret:   Place to return a pointer to the root of the dentry tree.  Only
292  *              modified if successful.  Set to NULL if the file or directory was
293  *              excluded from capture.
294  *
295  * @root_disk_path:  The path to the root of the directory tree on disk.
296  *
297  * @params:     See doc for `struct add_image_params'.
298  *
299  * @return:     0 on success, nonzero on failure.  It is a failure if any of
300  *              the files cannot be `stat'ed, or if any of the needed
301  *              directories cannot be opened or read.  Failure to add the files
302  *              to the WIM may still occur later when trying to actually read
303  *              the on-disk files during a call to wimlib_write() or
304  *              wimlib_overwrite().
305  */
306 int
307 unix_build_dentry_tree(struct wim_dentry **root_ret,
308                        const char *root_disk_path,
309                        struct add_image_params *params)
310 {
311         char *path_buf;
312         int ret;
313         size_t path_len;
314         size_t path_bufsz;
315
316         {
317                 struct stat root_stbuf;
318                 if (stat(root_disk_path, &root_stbuf)) {
319                         ERROR_WITH_ERRNO("Failed to stat \"%s\"", root_disk_path);
320                         return WIMLIB_ERR_STAT;
321                 }
322
323                 if ((params->add_image_flags & WIMLIB_ADD_IMAGE_FLAG_ROOT) &&
324                     !S_ISDIR(root_stbuf.st_mode))
325                 {
326                         ERROR("Root of capture \"%s\" is not a directory",
327                               root_disk_path);
328                         return WIMLIB_ERR_NOTDIR;
329                 }
330                 params->capture_root_ino = root_stbuf.st_ino;
331                 params->capture_root_dev = root_stbuf.st_dev;
332         }
333
334         path_bufsz = min(32790, PATH_MAX + 1);
335         path_len = strlen(root_disk_path);
336
337         if (path_len >= path_bufsz)
338                 return WIMLIB_ERR_INVALID_PARAM;
339
340         path_buf = MALLOC(path_bufsz);
341         if (!path_buf)
342                 return WIMLIB_ERR_NOMEM;
343         memcpy(path_buf, root_disk_path, path_len + 1);
344
345         ret = unix_build_dentry_tree_recursive(root_ret, path_buf,
346                                                path_len, params);
347         FREE(path_buf);
348         return ret;
349 }
350
351 #endif /* !__WIN32__ */