2 * Copyright (C) 2013 Eric Biggers
4 * This file is part of wimlib, a library for working with WIM files.
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)
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
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/.
22 #include "wimlib_internal.h"
24 #include "lookup_table.h"
25 #include "timestamp.h"
33 unix_capture_regular_file(const char *path,
35 struct wim_inode *inode,
36 struct wim_lookup_table *lookup_table)
38 inode->i_attributes = FILE_ATTRIBUTE_NORMAL;
40 /* Empty files do not have to have a lookup table entry. */
42 struct wim_lookup_table_entry *lte;
45 file_on_disk = STRDUP(path);
47 return WIMLIB_ERR_NOMEM;
48 lte = new_lookup_table_entry();
51 return WIMLIB_ERR_NOMEM;
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);
63 unix_build_dentry_tree_recursive(struct wim_dentry **root_ret,
66 struct add_image_params *params);
69 unix_capture_directory(struct wim_dentry *dir_dentry,
72 struct add_image_params *params)
77 struct wim_dentry *child;
80 dir_dentry->d_inode->i_attributes = FILE_ATTRIBUTE_DIRECTORY;
83 ERROR_WITH_ERRNO("Failed to open the directory `%s'",
85 return WIMLIB_ERR_OPEN;
88 /* Recurse on directory contents */
95 ret = WIMLIB_ERR_READ;
96 ERROR_WITH_ERRNO("Error reading the "
97 "directory `%s'", path);
102 if (entry->d_name[0] == '.' && (entry->d_name[1] == '\0'
103 || (entry->d_name[1] == '.' && entry->d_name[2] == '\0')))
106 size_t name_len = strlen(entry->d_name);
108 path[path_len] = '/';
109 memcpy(&path[path_len + 1], entry->d_name, name_len + 1);
110 ret = unix_build_dentry_tree_recursive(&child,
112 path_len + 1 + name_len,
117 dentry_add_child(dir_dentry, child);
124 unix_capture_symlink(struct wim_dentry **root_p,
126 struct wim_inode *inode,
127 struct add_image_params *params)
129 char deref_name_buf[4096];
130 ssize_t deref_name_len;
133 inode->i_attributes = FILE_ATTRIBUTE_REPARSE_POINT;
134 inode->i_reparse_tag = WIM_IO_REPARSE_TAG_SYMLINK;
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
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;
147 dest[deref_name_len] = '\0';
148 DEBUG("Read symlink `%s'", dest);
150 if ((params->add_image_flags & WIMLIB_ADD_IMAGE_FLAG_RPFIX) &&
153 dest = capture_fixup_absolute_symlink(dest,
154 params->capture_root_ino,
155 params->capture_root_dev);
157 WARNING("Ignoring out of tree absolute symlink "
159 " (Use --norpfix to capture "
160 "absolute symlinks as-is)",
161 path, deref_name_buf);
162 free_dentry(*root_p);
166 inode->i_not_rpfixed = 0;
168 ret = wim_inode_set_symlink(inode, dest, params->lookup_table);
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
177 if (stat(path, &stbuf) == 0 && S_ISDIR(stbuf.st_mode))
178 inode->i_attributes |= FILE_ATTRIBUTE_DIRECTORY;
181 ERROR_WITH_ERRNO("Failed to read target of "
182 "symbolic link `%s'", path);
183 ret = WIMLIB_ERR_READLINK;
189 unix_build_dentry_tree_recursive(struct wim_dentry **root_ret,
192 struct add_image_params *params)
194 struct wim_dentry *root = NULL;
196 struct wim_inode *inode;
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)
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);
210 if ((params->add_image_flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE)
211 && params->progress_func)
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);
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))
227 ret = (*stat_fn)(path, &stbuf);
229 ERROR_WITH_ERRNO("Failed to stat `%s'", path);
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.",
236 ret = WIMLIB_ERR_SPECIAL_FILE;
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);
246 inode = root->d_inode;
248 if (inode->i_nlink > 1) /* Already captured this inode? */
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);
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);
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,
265 params->lookup_table,
266 UNIX_DATA_ALL | UNIX_DATA_CREATE);
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);
277 ret = unix_capture_symlink(&root, path, inode, params);
282 free_dentry_tree(root, params->lookup_table);
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).
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.
295 * @root_disk_path: The path to the root of the directory tree on disk.
297 * @params: See doc for `struct add_image_params'.
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().
307 unix_build_dentry_tree(struct wim_dentry **root_ret,
308 const char *root_disk_path,
309 struct add_image_params *params)
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;
323 if ((params->add_image_flags & WIMLIB_ADD_IMAGE_FLAG_ROOT) &&
324 !S_ISDIR(root_stbuf.st_mode))
326 ERROR("Root of capture \"%s\" is not a directory",
328 return WIMLIB_ERR_NOTDIR;
330 params->capture_root_ino = root_stbuf.st_ino;
331 params->capture_root_dev = root_stbuf.st_dev;
334 path_bufsz = min(32790, PATH_MAX + 1);
335 path_len = strlen(root_disk_path);
337 if (path_len >= path_bufsz)
338 return WIMLIB_ERR_INVALID_PARAM;
340 path_buf = MALLOC(path_bufsz);
342 return WIMLIB_ERR_NOMEM;
343 memcpy(path_buf, root_disk_path, path_len + 1);
345 ret = unix_build_dentry_tree_recursive(root_ret, path_buf,
351 #endif /* !__WIN32__ */