X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fadd_image.c;h=fa3e77b04d33224c906af258a7d8b1a69c32f3e9;hp=0d1ffb29c3b4cc0836f3a152babaed6e81049fb2;hb=ebd6c0ec0ff47ac18af4ef918fd78fb8d9f19540;hpb=86e0ea554928890970618ff353bec09bf33708a8 diff --git a/src/add_image.c b/src/add_image.c index 0d1ffb29..fa3e77b0 100644 --- a/src/add_image.c +++ b/src/add_image.c @@ -21,6 +21,19 @@ * along with wimlib; if not, see http://www.gnu.org/licenses/. */ + +#if defined(__CYGWIN__) || defined(__WIN32__) +# include +# include +# include +# include +# include +# ifdef ERROR +# undef ERROR +# endif +# include "security.h" +#endif + #include "wimlib_internal.h" #include "dentry.h" #include "timestamp.h" @@ -35,6 +48,21 @@ #include #include +#if defined(__CYGWIN__) || defined(__WIN32__) +/*#define ERROR_WIN32_SAFE(format, ...) \*/ +/*{( \*/ + /*DWORD err = GetLastError(); \*/ + /*ERROR(format, ##__VA_ARGS__); \*/ + /*SetLastError(err); \*/ +/*)}*/ +#define DEBUG_WIN32_SAFE(format, ...) \ +({ \ + DWORD err = GetLastError(); \ + DEBUG(format, ##__VA_ARGS__); \ + SetLastError(err); \ +}) +#endif + #define WIMLIB_ADD_IMAGE_FLAG_ROOT 0x80000000 #define WIMLIB_ADD_IMAGE_FLAG_SOURCE 0x40000000 @@ -89,6 +117,412 @@ err: } +#if defined(__CYGWIN__) || defined(__WIN32__) +static u64 FILETIME_to_u64(const FILETIME *ft) +{ + return ((u64)ft->dwHighDateTime << 32) | (u64)ft->dwLowDateTime; +} + +#ifdef ENABLE_ERROR_MESSAGES +static void win32_error(DWORD err_code) +{ + char *buffer; + DWORD nchars; + nchars = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, + NULL, err_code, 0, + (char*)&buffer, 0, NULL); + if (nchars == 0) { + ERROR("Error printing error message! " + "Computer will self-destruct in 3 seconds."); + } else { + ERROR("Win32 error: %s", buffer); + LocalFree(buffer); + } +} +#else +#define win32_error(err_code) +#endif + +static HANDLE win32_open_file(const wchar_t *path) +{ + return CreateFileW(path, + GENERIC_READ | READ_CONTROL, + FILE_SHARE_READ, + NULL, /* lpSecurityAttributes */ + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | + FILE_FLAG_OPEN_REPARSE_POINT, + NULL /* hTemplateFile */); +} + +int win32_read_file(const char *filename, + void *handle, u64 offset, size_t size, u8 *buf) +{ + HANDLE h = handle; + DWORD err; + DWORD bytesRead; + LARGE_INTEGER liOffset = {.QuadPart = offset}; + + wimlib_assert(size <= 0xffffffff); + + if (SetFilePointerEx(h, liOffset, NULL, FILE_BEGIN)) + if (ReadFile(h, buf, size, &bytesRead, NULL) && bytesRead == size) + return 0; + err = GetLastError(); + ERROR("Error reading \"%s\"", filename); + win32_error(err); + return WIMLIB_ERR_READ; +} + +void win32_close_handle(void *handle) +{ + CloseHandle((HANDLE)handle); +} + +void *win32_open_handle(const char *path_utf16) +{ + return (void*)win32_open_file((const wchar_t*)path_utf16); +} + +static int build_dentry_tree(struct wim_dentry **root_ret, + const char *root_disk_path, + struct wim_lookup_table *lookup_table, + struct wim_security_data *sd, + const struct capture_config *config, + int add_image_flags, + wimlib_progress_func_t progress_func, + void *extra_arg); + +static int win32_get_short_name(struct wim_dentry *dentry, + const wchar_t *path_utf16) +{ + WIN32_FIND_DATAW dat; + if (FindFirstFileW(path_utf16, &dat) && + dat.cAlternateFileName[0] != L'\0') + { + size_t short_name_len = wcslen(dat.cAlternateFileName) * 2; + size_t n = short_name_len + sizeof(wchar_t); + dentry->short_name = MALLOC(n); + if (!dentry->short_name) + return WIMLIB_ERR_NOMEM; + memcpy(dentry->short_name, dat.cAlternateFileName, n); + dentry->short_name_len = short_name_len; + } + return 0; +} + +static int win32_get_security_descriptor(struct wim_dentry *dentry, + struct sd_set *sd_set, + const wchar_t *path_utf16, + const char *path) +{ + SECURITY_INFORMATION requestedInformation; + DWORD lenNeeded = 0; + BOOL status; + DWORD err; + +#ifdef BACKUP_SECURITY_INFORMATION + requestedInformation = BACKUP_SECURITY_INFORMATION; +#else + requestedInformation = DACL_SECURITY_INFORMATION | + SACL_SECURITY_INFORMATION | + OWNER_SECURITY_INFORMATION | + GROUP_SECURITY_INFORMATION; +#endif + /* Request length of security descriptor */ + status = GetFileSecurityW(path_utf16, requestedInformation, + NULL, 0, &lenNeeded); + err = GetLastError(); + + /* Error code appears to be ERROR_INSUFFICIENT_BUFFER but + * GetFileSecurity is poorly documented... */ + if (err == ERROR_INSUFFICIENT_BUFFER || err == NO_ERROR) { + DWORD len = lenNeeded; + char buf[len]; + if (GetFileSecurityW(path_utf16, requestedInformation, + buf, len, &lenNeeded)) + { + int security_id = sd_set_add_sd(sd_set, buf, len); + if (security_id < 0) + return WIMLIB_ERR_NOMEM; + else { + dentry->d_inode->i_security_id = security_id; + return 0; + } + } else { + err = GetLastError(); + } + } + ERROR("Win32 API: Failed to read security descriptor of \"%s\"", + path); + win32_error(err); + return WIMLIB_ERR_READ; +} + + +static int win32_recurse_directory(struct wim_dentry *root, + const char *root_disk_path, + struct wim_lookup_table *lookup_table, + struct wim_security_data *sd, + const struct capture_config *config, + int add_image_flags, + wimlib_progress_func_t progress_func, + struct sd_set *sd_set, + const wchar_t *path_utf16, + size_t path_utf16_nchars) +{ + WIN32_FIND_DATAW dat; + HANDLE hFind; + DWORD err; + int ret; + + { + wchar_t pattern_buf[path_utf16_nchars + 3]; + memcpy(pattern_buf, path_utf16, + path_utf16_nchars * sizeof(wchar_t)); + pattern_buf[path_utf16_nchars] = L'/'; + pattern_buf[path_utf16_nchars + 1] = L'*'; + pattern_buf[path_utf16_nchars + 2] = L'\0'; + hFind = FindFirstFileW(pattern_buf, &dat); + } + if (hFind == INVALID_HANDLE_VALUE) { + err = GetLastError(); + if (err == ERROR_FILE_NOT_FOUND) { + return 0; + } else { + ERROR("Win32 API: Failed to read directory \"%s\"", + root_disk_path); + win32_error(err); + return WIMLIB_ERR_READ; + } + } + ret = 0; + do { + if (!(dat.cFileName[0] == L'.' && + (dat.cFileName[1] == L'\0' || + (dat.cFileName[1] == L'.' && dat.cFileName[2] == L'\0')))) + { + struct wim_dentry *child; + + char *utf8_name; + size_t utf8_name_nbytes; + ret = utf16_to_utf8((const char*)dat.cFileName, + wcslen(dat.cFileName) * sizeof(wchar_t), + &utf8_name, + &utf8_name_nbytes); + if (ret) + goto out_find_close; + + char name[strlen(root_disk_path) + utf8_name_nbytes + 1]; + sprintf(name, "%s/%s", root_disk_path, utf8_name); + FREE(utf8_name); + ret = build_dentry_tree(&child, name, lookup_table, + sd, config, add_image_flags, + progress_func, sd_set); + if (ret) + goto out_find_close; + if (child) + dentry_add_child(root, child); + } + } while (FindNextFileW(hFind, &dat)); + err = GetLastError(); + if (err != ERROR_NO_MORE_FILES) { + ERROR("Win32 API: Failed to read directory \"%s\"", root_disk_path); + win32_error(err); + if (ret == 0) + ret = WIMLIB_ERR_READ; + } +out_find_close: + FindClose(hFind); + return ret; +} + +static int win32_capture_reparse_point(const char *path, + HANDLE hFile, + struct wim_inode *inode, + struct wim_lookup_table *lookup_table) +{ + /* "Reparse point data, including the tag and optional GUID, + * cannot exceed 16 kilobytes." - MSDN */ + char reparse_point_buf[16 * 1024]; + DWORD bytesReturned; + const REPARSE_DATA_BUFFER *buf; + + if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, + NULL, 0, reparse_point_buf, + sizeof(reparse_point_buf), &bytesReturned, NULL)) + { + ERROR("Win32 API: Failed to get reparse data of \"%s\"", path); + return WIMLIB_ERR_READ; + } + buf = (const REPARSE_DATA_BUFFER*)reparse_point_buf; + inode->i_reparse_tag = buf->ReparseTag; + return inode_add_ads_with_data(inode, "", (const u8*)buf + 8, + bytesReturned - 8, lookup_table); +} + +static int win32_sha1sum(const wchar_t *path, u8 hash[SHA1_HASH_SIZE]) +{ + HANDLE hFile; + SHA_CTX ctx; + u8 buf[32768]; + DWORD bytesRead; + int ret; + + hFile = win32_open_file(path); + if (hFile == INVALID_HANDLE_VALUE) + return WIMLIB_ERR_OPEN; + + sha1_init(&ctx); + for (;;) { + if (!ReadFile(hFile, buf, sizeof(buf), &bytesRead, NULL)) { + ret = WIMLIB_ERR_READ; + goto out_close_handle; + } + if (bytesRead == 0) + break; + sha1_update(&ctx, buf, bytesRead); + } + ret = 0; + sha1_final(hash, &ctx); +out_close_handle: + CloseHandle(hFile); + return ret; +} + +static int win32_capture_stream(const char *path, + const wchar_t *path_utf16, + size_t path_utf16_nchars, + struct wim_inode *inode, + struct wim_lookup_table *lookup_table, + WIN32_FIND_STREAM_DATA *dat) +{ + struct wim_ads_entry *ads_entry; + u8 hash[SHA1_HASH_SIZE]; + struct wim_lookup_table_entry *lte; + int ret; + wchar_t *p, *colon; + bool is_named_stream; + wchar_t *spath; + size_t spath_nchars; + DWORD err; + + p = dat->cStreamName; + wimlib_assert(*p == L':'); + p += 1; + colon = wcschr(p, L':'); + wimlib_assert(colon != NULL); + + if (wcscmp(colon + 1, L"$DATA")) { + /* Not a DATA stream */ + ret = 0; + goto out; + } + + is_named_stream = (p != colon); + + if (is_named_stream) { + char *utf8_stream_name; + size_t utf8_stream_name_len; + ret = utf16_to_utf8((const char *)p, + colon - p, + &utf8_stream_name, + &utf8_stream_name_len); + if (ret) + goto out; + DEBUG_WIN32_SAFE("Add alternate data stream %s:%s", path, utf8_stream_name); + ads_entry = inode_add_ads(inode, utf8_stream_name); + FREE(utf8_stream_name); + if (!ads_entry) { + ret = WIMLIB_ERR_NOMEM; + goto out; + } + } + + *colon = '\0'; + spath_nchars = path_utf16_nchars; + if (is_named_stream) + spath_nchars += colon - p + 1; + + spath = MALLOC((spath_nchars + 1) * sizeof(wchar_t)); + memcpy(spath, path_utf16, path_utf16_nchars * sizeof(wchar_t)); + if (is_named_stream) { + spath[path_utf16_nchars] = L':'; + memcpy(&spath[path_utf16_nchars + 1], p, (colon - p) * sizeof(wchar_t)); + } + spath[spath_nchars] = L'\0'; + + ret = win32_sha1sum(spath, hash); + if (ret) { + err = GetLastError(); + ERROR("Win32 API: Failed to read \"%s\" to calculate SHA1sum", path); + win32_error(err); + goto out_free_spath; + } + + lte = __lookup_resource(lookup_table, hash); + if (lte) { + lte->refcnt++; + } else { + lte = new_lookup_table_entry(); + if (!lte) { + ret = WIMLIB_ERR_NOMEM; + goto out_free_spath; + } + lte->file_on_disk = (char*)spath; + spath = NULL; + lte->resource_location = RESOURCE_WIN32; + lte->resource_entry.original_size = (uint64_t)dat->StreamSize.QuadPart; + lte->resource_entry.size = (uint64_t)dat->StreamSize.QuadPart; + copy_hash(lte->hash, hash); + lookup_table_insert(lookup_table, lte); + } + if (is_named_stream) + ads_entry->lte = lte; + else + inode->i_lte = lte; +out_free_spath: + FREE(spath); +out: + return ret; +} + +static int win32_capture_streams(const char *path, + const wchar_t *path_utf16, + size_t path_utf16_nchars, + struct wim_inode *inode, + struct wim_lookup_table *lookup_table) +{ + WIN32_FIND_STREAM_DATA dat; + int ret; + HANDLE hFind; + DWORD err; + + hFind = FindFirstStreamW(path_utf16, FindStreamInfoStandard, &dat, 0); + if (hFind == INVALID_HANDLE_VALUE) { + ERROR("Win32 API: Failed to look up data streams of \"%s\"", + path); + return WIMLIB_ERR_READ; + } + do { + ret = win32_capture_stream(path, path_utf16, + path_utf16_nchars, + inode, lookup_table, + &dat); + if (ret) + goto out_find_close; + } while (FindNextStreamW(hFind, &dat)); + err = GetLastError(); + if (err != ERROR_HANDLE_EOF) { + ERROR("Win32 API: Error reading data streams from \"%s\"", path); + win32_error(err); + ret = WIMLIB_ERR_READ; + } +out_find_close: + FindClose(hFind); + return ret; +} +#endif /* * build_dentry_tree(): @@ -132,16 +566,15 @@ static int build_dentry_tree(struct wim_dentry **root_ret, wimlib_progress_func_t progress_func, void *extra_arg) { - struct stat root_stbuf; + struct wim_dentry *root = NULL; int ret = 0; - int (*stat_fn)(const char *restrict, struct stat *restrict); - struct wim_dentry *root; struct wim_inode *inode; if (exclude_path(root_disk_path, config, true)) { if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_ROOT) { ERROR("Cannot exclude the root directory from capture"); - return WIMLIB_ERR_INVALID_CAPTURE_CONFIG; + ret = WIMLIB_ERR_INVALID_CAPTURE_CONFIG; + goto out; } if ((add_image_flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE) && progress_func) @@ -151,8 +584,7 @@ static int build_dentry_tree(struct wim_dentry **root_ret, info.scan.excluded = true; progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY, &info); } - *root_ret = NULL; - return 0; + goto out; } if ((add_image_flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE) @@ -164,6 +596,10 @@ static int build_dentry_tree(struct wim_dentry **root_ret, progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY, &info); } +#if !defined(__CYGWIN__) && !defined(__WIN32__) + /* UNIX version of capturing a directory tree */ + struct stat root_stbuf; + int (*stat_fn)(const char *restrict, struct stat *restrict); if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE) stat_fn = stat; else @@ -172,7 +608,7 @@ static int build_dentry_tree(struct wim_dentry **root_ret, ret = (*stat_fn)(root_disk_path, &root_stbuf); if (ret != 0) { ERROR_WITH_ERRNO("Failed to stat `%s'", root_disk_path); - return WIMLIB_ERR_STAT; + goto out; } if ((add_image_flags & WIMLIB_ADD_IMAGE_FLAG_ROOT) && @@ -184,28 +620,32 @@ static int build_dentry_tree(struct wim_dentry **root_ret, ret = stat(root_disk_path, &root_stbuf); if (ret != 0) { ERROR_WITH_ERRNO("Failed to stat `%s'", root_disk_path); - return WIMLIB_ERR_STAT; + ret = WIMLIB_ERR_STAT; + goto out; } if (!S_ISDIR(root_stbuf.st_mode)) { ERROR("`%s' is not a directory", root_disk_path); - return WIMLIB_ERR_NOTDIR; + ret = WIMLIB_ERR_NOTDIR; + goto out; } } if (!S_ISREG(root_stbuf.st_mode) && !S_ISDIR(root_stbuf.st_mode) && !S_ISLNK(root_stbuf.st_mode)) { ERROR("`%s' is not a regular file, directory, or symbolic link.", root_disk_path); - return WIMLIB_ERR_SPECIAL_FILE; + ret = WIMLIB_ERR_SPECIAL_FILE; + goto out; } root = new_dentry_with_timeless_inode(path_basename(root_disk_path)); if (!root) { if (errno == EILSEQ) - return WIMLIB_ERR_INVALID_UTF8_STRING; + ret = WIMLIB_ERR_INVALID_UTF8_STRING; else if (errno == ENOMEM) - return WIMLIB_ERR_NOMEM; + ret = WIMLIB_ERR_NOMEM; else - return WIMLIB_ERR_ICONV_NOT_AVAILABLE; + ret = WIMLIB_ERR_ICONV_NOT_AVAILABLE; + goto out; } inode = root->d_inode; @@ -373,6 +813,122 @@ static int build_dentry_tree(struct wim_dentry **root_ret, ret = WIMLIB_ERR_READLINK; } } +#else + /* Win32 version of capturing a directory tree */ + + wchar_t *path_utf16; + size_t path_utf16_nchars; + struct sd_set *sd_set; + DWORD err; + + if (extra_arg == NULL) { + sd_set = alloca(sizeof(struct sd_set)); + sd_set->rb_root.rb_node = NULL, + sd_set->sd = sd; + } else { + sd_set = extra_arg; + } + + DEBUG_WIN32_SAFE("root_disk_path=\"%s\"", root_disk_path); + ret = utf8_to_utf16(root_disk_path, strlen(root_disk_path), + (char**)&path_utf16, &path_utf16_nchars); + if (ret) + goto out_destroy_sd_set; + path_utf16_nchars /= sizeof(wchar_t); + + DEBUG_WIN32_SAFE("Win32: Opening file `%s'", root_disk_path); + HANDLE hFile = win32_open_file(path_utf16); + if (hFile == INVALID_HANDLE_VALUE) { + err = GetLastError(); + ERROR("Win32 API: Failed to open \"%s\"", root_disk_path); + win32_error(err); + ret = WIMLIB_ERR_OPEN; + goto out_free_path_utf16; + } + + BY_HANDLE_FILE_INFORMATION file_info; + if (!GetFileInformationByHandle(hFile, &file_info)) { + err = GetLastError(); + ERROR("Win32 API: Failed to get file information for \"%s\"", + root_disk_path); + win32_error(err); + ret = WIMLIB_ERR_STAT; + goto out_close_handle; + } + + /* Create a WIM dentry */ + root = new_dentry_with_timeless_inode(path_basename(root_disk_path)); + if (!root) { + if (errno == EILSEQ) + ret = WIMLIB_ERR_INVALID_UTF8_STRING; + else if (errno == ENOMEM) + ret = WIMLIB_ERR_NOMEM; + else + ret = WIMLIB_ERR_ICONV_NOT_AVAILABLE; + goto out_free_path_utf16; + } + + /* Start preparing the associated WIM inode */ + inode = root->d_inode; + + inode->i_attributes = file_info.dwFileAttributes; + inode->i_creation_time = FILETIME_to_u64(&file_info.ftCreationTime); + inode->i_last_write_time = FILETIME_to_u64(&file_info.ftLastWriteTime); + inode->i_last_access_time = FILETIME_to_u64(&file_info.ftLastAccessTime); + inode->i_ino = ((u64)file_info.nFileIndexHigh << 32) | + (u64)file_info.nFileIndexLow; + + inode->i_resolved = 1; + add_image_flags &= ~(WIMLIB_ADD_IMAGE_FLAG_ROOT | WIMLIB_ADD_IMAGE_FLAG_SOURCE); + + /* Get DOS name and security descriptor (if any). */ + ret = win32_get_short_name(root, path_utf16); + if (ret) + goto out_close_handle; + ret = win32_get_security_descriptor(root, sd_set, path_utf16, + root_disk_path); + if (ret) + goto out_close_handle; + + if (inode_is_directory(inode)) { + /* Directory (not a reparse point) --- recurse to children */ + DEBUG_WIN32_SAFE("Recursing to directory \"%s\"", root_disk_path); + ret = win32_recurse_directory(root, + root_disk_path, + lookup_table, + sd, + config, + add_image_flags, + progress_func, + sd_set, + path_utf16, + path_utf16_nchars); + } else if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) { + /* Reparse point: save the reparse tag and data */ + + DEBUG_WIN32_SAFE("Capturing reparse point `%s'", root_disk_path); + ret = win32_capture_reparse_point(root_disk_path, + hFile, + inode, + lookup_table); + + } else { + DEBUG_WIN32_SAFE("Capturing streams of \"%s\"", root_disk_path); + /* Not a directory, not a reparse point */ + ret = win32_capture_streams(root_disk_path, + path_utf16, + path_utf16_nchars, + inode, + lookup_table); + } +out_close_handle: + CloseHandle(hFile); +out_destroy_sd_set: + if (extra_arg == NULL) + destroy_sd_set(sd_set); +out_free_path_utf16: + FREE(path_utf16); +#endif out: if (ret == 0) *root_ret = root; @@ -381,7 +937,6 @@ out: return ret; } - enum pattern_type { NONE = 0, EXCLUSION_LIST, @@ -448,11 +1003,10 @@ static int pattern_list_add_pattern(struct pattern_list *list, /* Parses the contents of the image capture configuration file and fills in a * `struct capture_config'. */ -static int init_capture_config(const char *_config_str, size_t config_len, - const char *_prefix, struct capture_config *config) +static int init_capture_config(struct capture_config *config, + const char *_config_str, size_t config_len) { char *config_str; - char *prefix; char *p; char *eol; char *next_p; @@ -469,17 +1023,10 @@ static int init_capture_config(const char *_config_str, size_t config_len, ERROR("Could not duplicate capture config string"); return WIMLIB_ERR_NOMEM; } - prefix = STRDUP(_prefix); - if (!prefix) { - FREE(config_str); - return WIMLIB_ERR_NOMEM; - } memcpy(config_str, _config_str, config_len); next_p = config_str; config->config_str = config_str; - config->prefix = prefix; - config->prefix_len = strlen(prefix); while (bytes_remaining) { line_no++; p = next_p; @@ -554,6 +1101,19 @@ out_destroy: return ret; } +static int capture_config_set_prefix(struct capture_config *config, + const char *_prefix) +{ + char *prefix = STRDUP(_prefix); + + if (!prefix) + return WIMLIB_ERR_NOMEM; + FREE(config->prefix); + config->prefix = prefix; + config->prefix_len = strlen(prefix); + return 0; +} + static bool match_pattern(const char *path, const char *path_basename, const struct pattern_list *list) { @@ -641,17 +1201,14 @@ static void canonicalize_targets(struct wimlib_capture_source *sources, sources->wim_target_path); sources->wim_target_path = (char*)canonicalize_target_path(sources->wim_target_path); - DEBUG("\"%s\"", sources->wim_target_path); + DEBUG("Canonical target: \"%s\"", sources->wim_target_path); sources++; } } static int capture_source_cmp(const void *p1, const void *p2) { - const struct wimlib_capture_source *s1, *s2; - - s1 = p1; - s2 = p2; + const struct wimlib_capture_source *s1 = p1, *s2 = p2; return strcmp(s1->wim_target_path, s2->wim_target_path); } @@ -728,6 +1285,8 @@ new_filler_directory(const char *name) DEBUG("Creating filler directory \"%s\"", name); dentry = new_dentry_with_inode(name); if (dentry) { + /* Set the inode number to 0 for now. The final inode number + * will be assigned later by assign_inode_numbers(). */ dentry->d_inode->i_ino = 0; dentry->d_inode->i_resolved = 1; dentry->d_inode->i_attributes = FILE_ATTRIBUTE_DIRECTORY; @@ -738,29 +1297,29 @@ new_filler_directory(const char *name) /* Transfers the children of @branch to @target. It is an error if @target is * not a directory or if both @branch and @target contain a child dentry with * the same name. */ -static int do_overlay(struct wim_dentry *target, - struct wim_dentry *branch) +static int do_overlay(struct wim_dentry *target, struct wim_dentry *branch) { struct rb_root *rb_root; if (!dentry_is_directory(target)) { ERROR("Cannot overlay directory `%s' over non-directory", branch->file_name_utf8); - /* XXX Use a different error code */ - return WIMLIB_ERR_INVALID_DENTRY; + return WIMLIB_ERR_INVALID_OVERLAY; } rb_root = &branch->d_inode->i_children; while (rb_root->rb_node) { /* While @branch has children... */ - struct wim_dentry *child; - - child = container_of(rb_root->rb_node, struct wim_dentry, rb_node); + struct wim_dentry *child = rbnode_dentry(rb_root->rb_node); + /* Move @child to the directory @target */ unlink_dentry(child); if (!dentry_add_child(target, child)) { + /* Revert the change to avoid leaking the directory tree + * rooted at @child */ dentry_add_child(branch, child); - ERROR("Overlay error: file `%s' already exists as child of `%s'", + ERROR("Overlay error: file `%s' already exists " + "as a child of `%s'", child->file_name_utf8, target->file_name_utf8); - return WIMLIB_ERR_INVALID_DENTRY; + return WIMLIB_ERR_INVALID_OVERLAY; } } return 0; @@ -775,7 +1334,8 @@ static int do_overlay(struct wim_dentry *target, * @branch * Branch to add. * @target_path: - * Path in the WIM image to add the branch. + * Path in the WIM image to add the branch, with leading and trailing + * slashes stripped. */ static int attach_branch(struct wim_dentry **root_p, struct wim_dentry *branch, @@ -818,9 +1378,11 @@ static int attach_branch(struct wim_dentry **root_p, } parent = dentry; target_path = slash; + /* Skip over slashes. Note: this cannot overrun the length of + * the string because the last character cannot be a slash, as + * trailing slashes were tripped. */ do { ++target_path; - wimlib_assert(*target_path != '\0'); } while (*target_path == '/'); } @@ -850,7 +1412,8 @@ WIMLIBAPI int wimlib_add_image_multisource(WIMStruct *w, const struct capture_config *, int, wimlib_progress_func_t, void *); void *extra_arg; - struct wim_dentry *root_dentry = NULL; + struct wim_dentry *root_dentry; + struct wim_dentry *branch; struct wim_security_data *sd; struct capture_config config; struct wim_image_metadata *imd; @@ -900,14 +1463,15 @@ WIMLIBAPI int wimlib_add_image_multisource(WIMStruct *w, config_str = default_config; config_len = strlen(default_config); } - memset(&config, 0, sizeof(struct capture_config)); + ret = init_capture_config(&config, config_str, config_len); + if (ret) + goto out; DEBUG("Allocating security data"); - sd = CALLOC(1, sizeof(struct wim_security_data)); if (!sd) { ret = WIMLIB_ERR_NOMEM; - goto out_destroy_config; + goto out_destroy_capture_config; } sd->total_length = 8; sd->refcnt = 1; @@ -927,24 +1491,26 @@ WIMLIBAPI int wimlib_add_image_multisource(WIMStruct *w, if (!root_dentry) goto out_free_security_data; } else { - size_t i = 0; - struct wim_dentry *branch; - int flags; + size_t i; + + root_dentry = NULL; + i = 0; do { + int flags; + union wimlib_progress_info progress; + DEBUG("Building dentry tree for source %zu of %zu " "(\"%s\" => \"%s\")", i + 1, num_sources, sources[i].fs_source_path, sources[i].wim_target_path); - union wimlib_progress_info progress; if (progress_func) { memset(&progress, 0, sizeof(progress)); progress.scan.source = sources[i].fs_source_path; progress.scan.wim_target_path = sources[i].wim_target_path; progress_func(WIMLIB_PROGRESS_MSG_SCAN_BEGIN, &progress); } - ret = init_capture_config(config_str, config_len, - sources[i].fs_source_path, - &config); + ret = capture_config_set_prefix(&config, + sources[i].fs_source_path); if (ret) goto out_free_dentry_tree; flags = add_image_flags | WIMLIB_ADD_IMAGE_FLAG_SOURCE; @@ -961,21 +1527,20 @@ WIMLIBAPI int wimlib_add_image_multisource(WIMStruct *w, goto out_free_dentry_tree; } if (branch) { + /* Use the target name, not the source name, for + * the root of each branch from a capture + * source. (This will also set the root dentry + * of the entire image to be unnamed.) */ ret = set_dentry_name(branch, path_basename(sources[i].wim_target_path)); - if (ret) { - free_dentry_tree(branch, w->lookup_table); - goto out_free_dentry_tree; - } + if (ret) + goto out_free_branch; ret = attach_branch(&root_dentry, branch, sources[i].wim_target_path); - if (ret) { - free_dentry_tree(branch, w->lookup_table); - goto out_free_dentry_tree; - } + if (ret) + goto out_free_branch; } - destroy_capture_config(&config); if (progress_func) progress_func(WIMLIB_PROGRESS_MSG_SCAN_END, &progress); } while (++i != num_sources); @@ -1011,12 +1576,14 @@ out_destroy_imd: destroy_image_metadata(&w->image_metadata[w->hdr.image_count - 1], w->lookup_table); w->hdr.image_count--; - return ret; + goto out; +out_free_branch: + free_dentry_tree(branch, w->lookup_table); out_free_dentry_tree: free_dentry_tree(root_dentry, w->lookup_table); out_free_security_data: free_security_data(sd); -out_destroy_config: +out_destroy_capture_config: destroy_capture_config(&config); out: return ret;