From f572d61bbd0e33de6b8c540f3b5e89f673cbbfbe Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 15 Jul 2017 23:26:33 -0700 Subject: [PATCH] Use dynamically-sized path buffer when scanning files This is needed to guarantee that no buffer overflow can occur when scanning a deep directory structure. The new way also avoids using PATH_MAX, which fixes a build error on systems that don't define it. --- include/wimlib/scan.h | 29 +++-- src/ntfs-3g_capture.c | 68 +++++------- src/scan.c | 92 +++++++++++++--- src/unix_capture.c | 97 +++++++---------- src/update_image.c | 1 + src/win32_capture.c | 247 +++++++++++++++++------------------------- 6 files changed, 266 insertions(+), 268 deletions(-) diff --git a/include/wimlib/scan.h b/include/wimlib/scan.h index a43baec5..c1c3ecb2 100644 --- a/include/wimlib/scan.h +++ b/include/wimlib/scan.h @@ -57,10 +57,14 @@ struct scan_params { /* Progress data. */ union wimlib_progress_info progress; - /* Before calling try_exclude(), the scan implementation must set this - * to the number of characters that try_exclude() will strip from the - * path when testing exclusion patterns. */ - size_t capture_root_nchars; + /* Path to the file or directory currently being scanned */ + tchar *cur_path; + size_t cur_path_nchars; + size_t cur_path_alloc_nchars; + + /* Length of the prefix of 'cur_path' which names the root of the + * directory tree currently being scanned */ + size_t root_path_nchars; /* Can be used by the scan implementation. */ u64 capture_root_ino; @@ -87,7 +91,7 @@ extern bool match_pattern_list(const tchar *path, const struct string_list *list); extern int -try_exclude(const tchar *full_path, const struct scan_params *params); +try_exclude(const struct scan_params *params); typedef int (*scan_tree_t)(struct wim_dentry **, const tchar *, struct scan_params *); @@ -123,9 +127,10 @@ generate_dentry_tree(struct wim_dentry **root_ret, #define WIMLIB_ADD_FLAG_ROOT 0x80000000 static inline int -report_scan_error(struct scan_params *params, int error_code, const tchar *path) +report_scan_error(struct scan_params *params, int error_code) { - return report_error(params->progfunc, params->progctx, error_code, path); + return report_error(params->progfunc, params->progctx, error_code, + params->cur_path); } extern bool @@ -135,4 +140,14 @@ extern void attach_scanned_tree(struct wim_dentry *parent, struct wim_dentry *child, struct blob_table *blob_table); +extern int +pathbuf_init(struct scan_params *params, const tchar *root_path); + +extern const tchar * +pathbuf_append_name(struct scan_params *params, const tchar *name, + size_t name_nchars, size_t *orig_path_nchars_ret); + +extern void +pathbuf_truncate(struct scan_params *params, size_t nchars); + #endif /* _WIMLIB_SCAN_H */ diff --git a/src/ntfs-3g_capture.c b/src/ntfs-3g_capture.c index ca8d1b2f..bc8fed71 100644 --- a/src/ntfs-3g_capture.c +++ b/src/ntfs-3g_capture.c @@ -6,7 +6,7 @@ */ /* - * Copyright (C) 2012-2016 Eric Biggers + * Copyright (C) 2012-2017 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free @@ -630,8 +630,6 @@ destroy_dos_name_map(struct dos_name_map *map) struct readdir_ctx { struct wim_dentry *parent; - char *path; - size_t path_len; struct dos_name_map dos_name_map; struct ntfs_volume_wrapper *volume; struct scan_params *params; @@ -641,8 +639,7 @@ struct readdir_ctx { static int ntfs_3g_build_dentry_tree_recursive(struct wim_dentry **root_p, const MFT_REF mref, - char *path, - size_t path_len, + const char *filename, int name_type, struct ntfs_volume_wrapper *volume, struct scan_params *params); @@ -653,10 +650,11 @@ filldir(void *_ctx, const ntfschar *name, const int name_nchars, const unsigned dt_type) { struct readdir_ctx *ctx = _ctx; + struct scan_params *params = ctx->params; const size_t name_nbytes = name_nchars * sizeof(ntfschar); char *mbs_name; size_t mbs_name_nbytes; - size_t path_len; + size_t orig_path_nchars; struct wim_dentry *child; int ret; @@ -678,16 +676,17 @@ filldir(void *_ctx, const ntfschar *name, const int name_nchars, if (should_ignore_filename(mbs_name, mbs_name_nbytes)) goto out_free_mbs_name; - path_len = ctx->path_len; - if (path_len != 1) - ctx->path[path_len++] = '/'; - memcpy(ctx->path + path_len, mbs_name, mbs_name_nbytes + 1); - path_len += mbs_name_nbytes; + ret = WIMLIB_ERR_NOMEM; + if (!pathbuf_append_name(params, mbs_name, mbs_name_nbytes, + &orig_path_nchars)) + goto out_free_mbs_name; + child = NULL; - ret = ntfs_3g_build_dentry_tree_recursive(&child, mref, ctx->path, - path_len, name_type, - ctx->volume, ctx->params); - attach_scanned_tree(ctx->parent, child, ctx->params->blob_table); + ret = ntfs_3g_build_dentry_tree_recursive(&child, mref, mbs_name, + name_type, ctx->volume, + params); + pathbuf_truncate(params, orig_path_nchars); + attach_scanned_tree(ctx->parent, child, params->blob_table); out_free_mbs_name: FREE(mbs_name); out: @@ -696,8 +695,7 @@ out: } static int -ntfs_3g_recurse_directory(ntfs_inode *ni, char *path, size_t path_len, - struct wim_dentry *parent, +ntfs_3g_recurse_directory(ntfs_inode *ni, struct wim_dentry *parent, struct ntfs_volume_wrapper *volume, struct scan_params *params) { @@ -705,22 +703,20 @@ ntfs_3g_recurse_directory(ntfs_inode *ni, char *path, size_t path_len, s64 pos = 0; struct readdir_ctx ctx = { .parent = parent, - .path = path, - .path_len = path_len, .dos_name_map = { .root = NULL }, .volume = volume, .params = params, .ret = 0, }; ret = ntfs_readdir(ni, &pos, &ctx, filldir); - path[path_len] = '\0'; if (unlikely(ret)) { if (ctx.ret) { /* wimlib error */ ret = ctx.ret; } else { /* error from ntfs_readdir() itself */ - ERROR_WITH_ERRNO("Error reading directory \"%s\"", path); + ERROR_WITH_ERRNO("Error reading directory \"%s\"", + params->cur_path); ret = WIMLIB_ERR_NTFS_3G; } } else { @@ -740,19 +736,19 @@ ntfs_3g_recurse_directory(ntfs_inode *ni, char *path, size_t path_len, static int ntfs_3g_build_dentry_tree_recursive(struct wim_dentry **root_ret, const MFT_REF mref, - char *path, - size_t path_len, + const char *filename, int name_type, struct ntfs_volume_wrapper *volume, struct scan_params *params) { + const char *path = params->cur_path; u32 attributes; int ret; struct wim_dentry *root = NULL; struct wim_inode *inode = NULL; ntfs_inode *ni = NULL; - ret = try_exclude(path, params); + ret = try_exclude(params); if (unlikely(ret < 0)) /* Excluded? */ goto out_progress; if (unlikely(ret > 0)) /* Error? */ @@ -781,14 +777,12 @@ ntfs_3g_build_dentry_tree_recursive(struct wim_dentry **root_ret, ret = WIMLIB_ERR_UNSUPPORTED_FILE; goto out; } - params->progress.scan.cur_path = path; ret = do_scan_progress(params, WIMLIB_SCAN_DENTRY_UNSUPPORTED, NULL); goto out; } /* Create a WIM dentry with an associated inode, which may be shared */ - ret = inode_table_new_dentry(params->inode_table, - path_basename_with_len(path, path_len), + ret = inode_table_new_dentry(params->inode_table, filename, ni->mft_no, 0, false, &root); if (ret) goto out; @@ -854,14 +848,12 @@ ntfs_3g_build_dentry_tree_recursive(struct wim_dentry **root_ret, } if (inode_is_directory(inode)) { - ret = ntfs_3g_recurse_directory(ni, path, path_len, root, - volume, params); + ret = ntfs_3g_recurse_directory(ni, root, volume, params); if (ret) goto out; } out_progress: - params->progress.scan.cur_path = path; if (root == NULL) ret = do_scan_progress(params, WIMLIB_SCAN_DENTRY_EXCLUDED, NULL); else @@ -872,7 +864,7 @@ out: if (unlikely(ret)) { free_dentry_tree(root, params->blob_table); root = NULL; - ret = report_scan_error(params, ret, path); + ret = report_scan_error(params, ret); } *root_ret = root; return ret; @@ -884,7 +876,6 @@ ntfs_3g_build_dentry_tree(struct wim_dentry **root_ret, { struct ntfs_volume_wrapper *volume; ntfs_volume *vol; - char *path; int ret; volume = CALLOC(1, sizeof(struct ntfs_volume_wrapper)); @@ -918,20 +909,13 @@ ntfs_3g_build_dentry_tree(struct wim_dentry **root_ret, * that we do need to capture. */ NVolClearShowSysFiles(vol); - /* Currently we assume that all the paths fit into this length and there - * is no check for overflow. */ - path = MALLOC(32768); - if (!path) { - ret = WIMLIB_ERR_NOMEM; + ret = pathbuf_init(params, "/"); + if (ret) goto out_close_secure; - } - path[0] = '/'; - path[1] = '\0'; - ret = ntfs_3g_build_dentry_tree_recursive(root_ret, FILE_root, path, 1, + ret = ntfs_3g_build_dentry_tree_recursive(root_ret, FILE_root, "", FILE_NAME_POSIX, volume, params); - FREE(path); out_close_secure: /* Undo the effects of ntfs_open_secure(). This is not yet done * automatically by ntfs_umount(). But NULL out the inode to diff --git a/src/scan.c b/src/scan.c index be651439..cc86d808 100644 --- a/src/scan.c +++ b/src/scan.c @@ -3,7 +3,7 @@ */ /* - * Copyright (C) 2013-2016 Eric Biggers + * Copyright (C) 2013-2017 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free @@ -39,8 +39,8 @@ * and possibly call the progress function provided by the library user. * * @params - * Flags, optional progress function, and progress data for the scan - * operation. + * Current path, flags, optional progress function, and progress data for + * the scan operation. * @status * Status of the scanned file. * @inode @@ -69,6 +69,7 @@ do_scan_progress(struct scan_params *params, int status, return 0; break; } + params->progress.scan.cur_path = params->cur_path; params->progress.scan.status = status; if (status == WIMLIB_SCAN_DENTRY_OK) { @@ -273,12 +274,11 @@ match_pattern_list(const tchar *path, const struct string_list *list) * (1) The capture configuration file * (2) The user-provided progress function * - * The capture implementation must have set params->capture_root_nchars to an - * appropriate value. Example for UNIX: if the capture root directory is - * "foobar/subdir", then all paths will be provided starting with - * "foobar/subdir", so params->capture_root_nchars must be set to - * strlen("foobar/subdir") so that the appropriate path can be matched against - * the patterns in the exclusion list. + * params->root_path_nchars must have been set beforehand. Example for UNIX: if + * the capture root directory is "foobar/subdir", then all paths will be + * provided starting with "foobar/subdir", so params->root_path_nchars must have + * been set to strlen("foobar/subdir") so that the appropriate path suffix can + * be matched against the patterns in the exclusion list. * * Returns: * < 0 if excluded @@ -286,12 +286,12 @@ match_pattern_list(const tchar *path, const struct string_list *list) * > 0 (wimlib error code) if error */ int -try_exclude(const tchar *full_path, const struct scan_params *params) +try_exclude(const struct scan_params *params) { int ret; if (params->config) { - const tchar *path = full_path + params->capture_root_nchars; + const tchar *path = params->cur_path + params->root_path_nchars; if (match_pattern_list(path, ¶ms->config->exclusion_pats) && !match_pattern_list(path, ¶ms->config->exclusion_exception_pats)) return -1; @@ -302,10 +302,10 @@ try_exclude(const tchar *full_path, const struct scan_params *params) union wimlib_progress_info info; tchar *cookie; - info.test_file_exclusion.path = full_path; + info.test_file_exclusion.path = params->cur_path; info.test_file_exclusion.will_exclude = false; - cookie = progress_get_win32_path(full_path); + cookie = progress_get_win32_path(info.test_file_exclusion.path); ret = call_progress(params->progfunc, WIMLIB_PROGRESS_MSG_TEST_FILE_EXCLUSION, &info, params->progctx); @@ -368,3 +368,69 @@ attach_scanned_tree(struct wim_dentry *parent, struct wim_dentry *child, free_dentry_tree(child, blob_table); } } + +/* Set the path at which the directory tree scan is beginning. */ +int +pathbuf_init(struct scan_params *params, const tchar *root_path) +{ + size_t nchars = tstrlen(root_path); + size_t alloc_nchars = nchars + 1 + 1024; + + params->cur_path = MALLOC(alloc_nchars * sizeof(tchar)); + if (!params->cur_path) + return WIMLIB_ERR_NOMEM; + tmemcpy(params->cur_path, root_path, nchars + 1); + params->cur_path_nchars = nchars; + params->cur_path_alloc_nchars = alloc_nchars; + params->root_path_nchars = nchars; + return 0; +} + +/* + * Append a filename to the current path. + * + * If successful, returns a pointer to the filename component and sets + * *orig_path_nchars_ret to the old path length, which can be restored later + * using pathbuf_truncate(). Otherwise returns NULL (out of memory). + */ +const tchar * +pathbuf_append_name(struct scan_params *params, const tchar *name, + size_t name_nchars, size_t *orig_path_nchars_ret) +{ + size_t path_nchars = params->cur_path_nchars; + size_t required_nchars = path_nchars + 1 + name_nchars + 1; + tchar *buf = params->cur_path; + + if (unlikely(required_nchars > params->cur_path_alloc_nchars)) { + required_nchars += 1024; + buf = REALLOC(buf, required_nchars * sizeof(tchar)); + if (!buf) + return NULL; + params->cur_path = buf; + params->cur_path_alloc_nchars = required_nchars; + } + *orig_path_nchars_ret = path_nchars; + + /* + * Add the slash, but not if it will be a duplicate (which can happen if + * the path to the capture root directory ends in a slash), because + * on Windows duplicate slashes sometimes don't work as expected. + */ + if (path_nchars && buf[path_nchars - 1] != OS_PREFERRED_PATH_SEPARATOR) + buf[path_nchars++] = OS_PREFERRED_PATH_SEPARATOR; + + tmemcpy(&buf[path_nchars], name, name_nchars); + path_nchars += name_nchars; + buf[path_nchars] = T('\0'); + params->cur_path_nchars = path_nchars; + return &buf[path_nchars - name_nchars]; +} + +/* Truncate the current path to the specified number of characters. */ +void +pathbuf_truncate(struct scan_params *params, size_t nchars) +{ + wimlib_assert(nchars <= params->cur_path_nchars); + params->cur_path[nchars] = T('\0'); + params->cur_path_nchars = nchars; +} diff --git a/src/unix_capture.c b/src/unix_capture.c index d869f258..5ae8209b 100644 --- a/src/unix_capture.c +++ b/src/unix_capture.c @@ -3,7 +3,7 @@ */ /* - * Copyright (C) 2012-2016 Eric Biggers + * Copyright (C) 2012-2017 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free @@ -28,7 +28,6 @@ #include #include #include -#include /* for PATH_MAX */ #include #include #ifdef HAVE_SYS_XATTR_H @@ -330,13 +329,11 @@ err_nomem: static int unix_build_dentry_tree_recursive(struct wim_dentry **tree_ret, - char *path, size_t path_len, int dirfd, const char *relpath, struct scan_params *params); static int unix_scan_directory(struct wim_dentry *dir_dentry, - char *full_path, size_t full_path_len, int parent_dirfd, const char *dir_relpath, struct scan_params *params) { @@ -345,16 +342,18 @@ unix_scan_directory(struct wim_dentry *dir_dentry, DIR *dir; int ret; - dirfd = my_openat(full_path, parent_dirfd, dir_relpath, O_RDONLY); + dirfd = my_openat(params->cur_path, parent_dirfd, dir_relpath, O_RDONLY); if (dirfd < 0) { - ERROR_WITH_ERRNO("\"%s\": Can't open directory", full_path); + ERROR_WITH_ERRNO("\"%s\": Can't open directory", + params->cur_path); return WIMLIB_ERR_OPENDIR; } dir_dentry->d_inode->i_attributes = FILE_ATTRIBUTE_DIRECTORY; dir = my_fdopendir(&dirfd); if (!dir) { - ERROR_WITH_ERRNO("\"%s\": Can't open directory", full_path); + ERROR_WITH_ERRNO("\"%s\": Can't open directory", + params->cur_path); close(dirfd); return WIMLIB_ERR_OPENDIR; } @@ -364,6 +363,7 @@ unix_scan_directory(struct wim_dentry *dir_dentry, struct dirent *entry; struct wim_dentry *child; size_t name_len; + size_t orig_path_len; errno = 0; entry = readdir(dir); @@ -371,7 +371,7 @@ unix_scan_directory(struct wim_dentry *dir_dentry, if (errno) { ret = WIMLIB_ERR_READ; ERROR_WITH_ERRNO("\"%s\": Error reading directory", - full_path); + params->cur_path); } break; } @@ -381,15 +381,13 @@ unix_scan_directory(struct wim_dentry *dir_dentry, if (should_ignore_filename(entry->d_name, name_len)) continue; - full_path[full_path_len] = '/'; - memcpy(&full_path[full_path_len + 1], entry->d_name, name_len + 1); - ret = unix_build_dentry_tree_recursive(&child, - full_path, - full_path_len + 1 + name_len, - dirfd, - &full_path[full_path_len + 1], - params); - full_path[full_path_len] = '\0'; + ret = WIMLIB_ERR_NOMEM; + if (!pathbuf_append_name(params, entry->d_name, name_len, + &orig_path_len)) + break; + ret = unix_build_dentry_tree_recursive(&child, dirfd, + entry->d_name, params); + pathbuf_truncate(params, orig_path_len); if (ret) break; attach_scanned_tree(dir_dentry, child, params->blob_table); @@ -472,7 +470,7 @@ unix_relativize_link_target(char *target, u64 ino, u64 dev) } static noinline_for_stack int -unix_scan_symlink(const char *full_path, int dirfd, const char *relpath, +unix_scan_symlink(int dirfd, const char *relpath, struct wim_inode *inode, struct scan_params *params) { char orig_target[REPARSE_POINT_MAX_SIZE]; @@ -480,15 +478,16 @@ unix_scan_symlink(const char *full_path, int dirfd, const char *relpath, int ret; /* Read the UNIX symbolic link target. */ - ret = my_readlinkat(full_path, dirfd, relpath, target, + ret = my_readlinkat(params->cur_path, dirfd, relpath, target, sizeof(orig_target)); if (unlikely(ret < 0)) { ERROR_WITH_ERRNO("\"%s\": Can't read target of symbolic link", - full_path); + params->cur_path); return WIMLIB_ERR_READLINK; } if (unlikely(ret >= sizeof(orig_target))) { - ERROR("\"%s\": target of symbolic link is too long", full_path); + ERROR("\"%s\": target of symbolic link is too long", + params->cur_path); return WIMLIB_ERR_READLINK; } target[ret] = '\0'; @@ -498,7 +497,6 @@ unix_scan_symlink(const char *full_path, int dirfd, const char *relpath, if (target[0] == '/' && (params->add_flags & WIMLIB_ADD_FLAG_RPFIX)) { int status = WIMLIB_SCAN_DENTRY_NOT_FIXED_SYMLINK; - params->progress.scan.cur_path = full_path; params->progress.scan.symlink_target = target; target = unix_relativize_link_target(target, @@ -519,7 +517,8 @@ unix_scan_symlink(const char *full_path, int dirfd, const char *relpath, if (unlikely(ret)) { if (ret == WIMLIB_ERR_INVALID_UTF8_STRING) { ERROR("\"%s\": target of symbolic link is not valid " - "UTF-8. This is not supported.", full_path); + "UTF-8. This is not supported.", + params->cur_path); } return ret; } @@ -529,7 +528,7 @@ unix_scan_symlink(const char *full_path, int dirfd, const char *relpath, * (non-)directory is stored as a reparse point on a (non-)directory * file. Replicate this behavior by examining the target file. */ struct stat stbuf; - if (my_fstatat(full_path, dirfd, relpath, &stbuf, 0) == 0 && + if (my_fstatat(params->cur_path, dirfd, relpath, &stbuf, 0) == 0 && S_ISDIR(stbuf.st_mode)) inode->i_attributes |= FILE_ATTRIBUTE_DIRECTORY; return 0; @@ -537,7 +536,6 @@ unix_scan_symlink(const char *full_path, int dirfd, const char *relpath, static int unix_build_dentry_tree_recursive(struct wim_dentry **tree_ret, - char *full_path, size_t full_path_len, int dirfd, const char *relpath, struct scan_params *params) { @@ -547,7 +545,7 @@ unix_build_dentry_tree_recursive(struct wim_dentry **tree_ret, struct stat stbuf; int stat_flags; - ret = try_exclude(full_path, params); + ret = try_exclude(params); if (unlikely(ret < 0)) /* Excluded? */ goto out_progress; if (unlikely(ret > 0)) /* Error? */ @@ -559,10 +557,11 @@ unix_build_dentry_tree_recursive(struct wim_dentry **tree_ret, else stat_flags = AT_SYMLINK_NOFOLLOW; - ret = my_fstatat(full_path, dirfd, relpath, &stbuf, stat_flags); + ret = my_fstatat(params->cur_path, dirfd, relpath, &stbuf, stat_flags); if (ret) { - ERROR_WITH_ERRNO("\"%s\": Can't read metadata", full_path); + ERROR_WITH_ERRNO("\"%s\": Can't read metadata", + params->cur_path); ret = WIMLIB_ERR_STAT; goto out; } @@ -576,11 +575,10 @@ unix_build_dentry_tree_recursive(struct wim_dentry **tree_ret, WIMLIB_ADD_FLAG_NO_UNSUPPORTED_EXCLUDE) { ERROR("\"%s\": File type is unsupported", - full_path); + params->cur_path); ret = WIMLIB_ERR_UNSUPPORTED_FILE; goto out; } - params->progress.scan.cur_path = full_path; ret = do_scan_progress(params, WIMLIB_SCAN_DENTRY_UNSUPPORTED, NULL); @@ -593,7 +591,7 @@ unix_build_dentry_tree_recursive(struct wim_dentry **tree_ret, if (unlikely(ret)) { if (ret == WIMLIB_ERR_INVALID_UTF8_STRING) { ERROR("\"%s\": filename is not valid UTF-8. " - "This is not supported.", full_path); + "This is not supported.", params->cur_path); } goto out; } @@ -625,7 +623,7 @@ unix_build_dentry_tree_recursive(struct wim_dentry **tree_ret, goto out; } #ifdef HAVE_XATTR_SUPPORT - ret = scan_linux_xattrs(full_path, inode); + ret = scan_linux_xattrs(params->cur_path, inode); if (ret) goto out; #endif @@ -638,22 +636,19 @@ unix_build_dentry_tree_recursive(struct wim_dentry **tree_ret, } if (S_ISREG(stbuf.st_mode)) { - ret = unix_scan_regular_file(full_path, stbuf.st_blocks, + ret = unix_scan_regular_file(params->cur_path, stbuf.st_blocks, stbuf.st_size, inode, params->unhashed_blobs); } else if (S_ISDIR(stbuf.st_mode)) { - ret = unix_scan_directory(tree, full_path, full_path_len, - dirfd, relpath, params); + ret = unix_scan_directory(tree, dirfd, relpath, params); } else if (S_ISLNK(stbuf.st_mode)) { - ret = unix_scan_symlink(full_path, dirfd, relpath, - inode, params); + ret = unix_scan_symlink(dirfd, relpath, inode, params); } if (ret) goto out; out_progress: - params->progress.scan.cur_path = full_path; if (likely(tree)) ret = do_scan_progress(params, WIMLIB_SCAN_DENTRY_OK, inode); else @@ -662,7 +657,7 @@ out: if (unlikely(ret)) { free_dentry_tree(tree, params->blob_table); tree = NULL; - ret = report_scan_error(params, ret, full_path); + ret = report_scan_error(params, ret); } *tree_ret = tree; return ret; @@ -691,28 +686,14 @@ int unix_build_dentry_tree(struct wim_dentry **root_ret, const char *root_disk_path, struct scan_params *params) { - size_t path_len; - size_t path_bufsz; - char *path_buf; int ret; - path_len = strlen(root_disk_path); - path_bufsz = min(32790, PATH_MAX + 1); - - if (path_len >= path_bufsz) - return WIMLIB_ERR_INVALID_PARAM; - - path_buf = MALLOC(path_bufsz); - if (!path_buf) - return WIMLIB_ERR_NOMEM; - memcpy(path_buf, root_disk_path, path_len + 1); - - params->capture_root_nchars = path_len; + ret = pathbuf_init(params, root_disk_path); + if (ret) + return ret; - ret = unix_build_dentry_tree_recursive(root_ret, path_buf, path_len, - AT_FDCWD, path_buf, params); - FREE(path_buf); - return ret; + return unix_build_dentry_tree_recursive(root_ret, AT_FDCWD, + root_disk_path, params); } #endif /* !__WIN32__ */ diff --git a/src/update_image.c b/src/update_image.c index 334db386..2247a7f6 100644 --- a/src/update_image.c +++ b/src/update_image.c @@ -888,6 +888,7 @@ execute_add_command(struct update_command_journal *j, out_destroy_config: destroy_capture_config(&config); out: + FREE(params.cur_path); return ret; } diff --git a/src/win32_capture.c b/src/win32_capture.c index a93427ec..75c5e66f 100644 --- a/src/win32_capture.c +++ b/src/win32_capture.c @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2013-2016 Eric Biggers + * Copyright (C) 2013-2017 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free @@ -58,10 +58,10 @@ struct winnt_scan_ctx { }; static inline const wchar_t * -printable_path(const wchar_t *full_path) +printable_path(const struct winnt_scan_ctx *ctx) { /* Skip over \\?\ or \??\ */ - return full_path + 4; + return ctx->params->cur_path + 4; } /* Description of where data is located on a Windows filesystem */ @@ -457,7 +457,7 @@ read_win32_encrypted_file_prefix(const wchar_t *path, bool is_dir, u64 size, if (err != ERROR_SUCCESS) { win32_error(err, L"Failed to open encrypted file \"%ls\" for raw read", - printable_path(path)); + path); return WIMLIB_ERR_OPEN; } err = ReadEncryptedFileRaw(win32_encrypted_export_cb, @@ -467,14 +467,14 @@ read_win32_encrypted_file_prefix(const wchar_t *path, bool is_dir, u64 size, if (ret == 0) { win32_error(err, L"Failed to read encrypted file \"%ls\"", - printable_path(path)); + path); ret = WIMLIB_ERR_READ; } } else if (export_ctx.bytes_remaining != 0) { ERROR("Only could read %"PRIu64" of %"PRIu64" bytes from " "encrypted file \"%ls\"", size - export_ctx.bytes_remaining, size, - printable_path(path)); + path); ret = WIMLIB_ERR_READ; } else { ret = 0; @@ -532,7 +532,6 @@ winnt_get_short_name(HANDLE h, struct wim_dentry *dentry) */ static noinline_for_stack int winnt_load_security_descriptor(HANDLE h, struct wim_inode *inode, - const wchar_t *full_path, struct winnt_scan_ctx *ctx) { SECURITY_INFORMATION requestedInformation; @@ -650,7 +649,7 @@ out: FREE(buf); if (!NT_SUCCESS(status)) { winnt_error(status, L"\"%ls\": Can't read security descriptor", - printable_path(full_path)); + printable_path(ctx)); return WIMLIB_ERR_STAT; } return 0; @@ -659,7 +658,7 @@ out: /* Load a file's object ID into the corresponding WIM inode. */ static noinline_for_stack int winnt_load_object_id(HANDLE h, struct wim_inode *inode, - const wchar_t *full_path, struct winnt_scan_ctx *ctx) + struct winnt_scan_ctx *ctx) { FILE_OBJECTID_BUFFER buffer; NTSTATUS status; @@ -683,7 +682,7 @@ winnt_load_object_id(HANDLE h, struct wim_inode *inode, if (!NT_SUCCESS(status)) { winnt_error(status, L"\"%ls\": Can't read object ID", - printable_path(full_path)); + printable_path(ctx)); return WIMLIB_ERR_STAT; } @@ -699,17 +698,13 @@ winnt_load_object_id(HANDLE h, struct wim_inode *inode, static int winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret, HANDLE cur_dir, - wchar_t *full_path, - size_t full_path_nchars, - wchar_t *relative_path, + const wchar_t *relative_path, size_t relative_path_nchars, const wchar_t *filename, struct winnt_scan_ctx *ctx); static int winnt_recurse_directory(HANDLE h, - wchar_t *full_path, - size_t full_path_nchars, struct wim_dentry *parent, struct winnt_scan_ctx *ctx) { @@ -736,33 +731,27 @@ winnt_recurse_directory(HANDLE h, if (!should_ignore_filename(info->FileName, info->FileNameLength / 2)) { - wchar_t *p; - wchar_t *filename; struct wim_dentry *child; - - p = full_path + full_path_nchars; - /* Only add a backslash if we don't already have - * one. This prevents a duplicate backslash - * from being added when the path to the capture - * dir had a trailing backslash. */ - if (*(p - 1) != L'\\') - *p++ = L'\\'; - filename = p; - p = wmempcpy(filename, info->FileName, - info->FileNameLength / 2); - *p = '\0'; + size_t orig_path_nchars; + const wchar_t *filename; + + ret = WIMLIB_ERR_NOMEM; + filename = pathbuf_append_name(ctx->params, + info->FileName, + info->FileNameLength / 2, + &orig_path_nchars); + if (!filename) + goto out_free_buf; ret = winnt_build_dentry_tree_recursive( &child, h, - full_path, - p - full_path, filename, info->FileNameLength / 2, filename, ctx); - full_path[full_path_nchars] = L'\0'; + pathbuf_truncate(ctx->params, orig_path_nchars); if (ret) goto out_free_buf; @@ -778,7 +767,7 @@ winnt_recurse_directory(HANDLE h, if (unlikely(status != STATUS_NO_MORE_FILES)) { winnt_error(status, L"\"%ls\": Can't read directory", - printable_path(full_path)); + printable_path(ctx)); ret = WIMLIB_ERR_READ; } out_free_buf: @@ -910,7 +899,7 @@ out_close_root_dir: } static int -winnt_rpfix_progress(struct scan_params *params, const wchar_t *path, +winnt_rpfix_progress(struct scan_params *params, const struct link_reparse_point *link, int scan_status) { size_t print_name_nchars = link->print_name_nbytes / sizeof(wchar_t); @@ -919,14 +908,13 @@ winnt_rpfix_progress(struct scan_params *params, const wchar_t *path, wmemcpy(print_name0, link->print_name, print_name_nchars); print_name0[print_name_nchars] = L'\0'; - params->progress.scan.cur_path = path; params->progress.scan.symlink_target = print_name0; return do_scan_progress(params, scan_status, NULL); } static int winnt_try_rpfix(struct reparse_buffer_disk *rpbuf, u16 *rpbuflen_p, - const wchar_t *path, struct scan_params *params) + struct scan_params *params) { struct link_reparse_point link; const wchar_t *rel_target; @@ -970,7 +958,7 @@ winnt_try_rpfix(struct reparse_buffer_disk *rpbuf, u16 *rpbuflen_p, if (rel_target == link.substitute_name) { /* Target points outside of the tree being captured or had an * unrecognized path format. Don't adjust it. */ - return winnt_rpfix_progress(params, path, &link, + return winnt_rpfix_progress(params, &link, WIMLIB_SCAN_DENTRY_NOT_FIXED_SYMLINK); } @@ -1006,7 +994,7 @@ winnt_try_rpfix(struct reparse_buffer_disk *rpbuf, u16 *rpbuflen_p, if (make_link_reparse_point(&link, rpbuf, rpbuflen_p)) return 0; - ret = winnt_rpfix_progress(params, path, &link, + ret = winnt_rpfix_progress(params, &link, WIMLIB_SCAN_DENTRY_FIXED_SYMLINK); if (ret) return ret; @@ -1019,7 +1007,7 @@ winnt_try_rpfix(struct reparse_buffer_disk *rpbuf, u16 *rpbuflen_p, * capture root. */ static noinline_for_stack int winnt_load_reparse_data(HANDLE h, struct wim_inode *inode, - const wchar_t *full_path, struct scan_params *params) + struct winnt_scan_ctx *ctx) { struct reparse_buffer_disk rpbuf; NTSTATUS status; @@ -1030,7 +1018,7 @@ winnt_load_reparse_data(HANDLE h, struct wim_inode *inode, if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED) { /* See comment above assign_stream_types_encrypted() */ WARNING("Ignoring reparse data of encrypted file \"%ls\"", - printable_path(full_path)); + printable_path(ctx)); return 0; } @@ -1038,7 +1026,7 @@ winnt_load_reparse_data(HANDLE h, struct wim_inode *inode, NULL, 0, &rpbuf, sizeof(rpbuf), &len); if (!NT_SUCCESS(status)) { winnt_error(status, L"\"%ls\": Can't get reparse point", - printable_path(full_path)); + printable_path(ctx)); return WIMLIB_ERR_READLINK; } @@ -1046,7 +1034,7 @@ winnt_load_reparse_data(HANDLE h, struct wim_inode *inode, if (unlikely(rpbuflen < REPARSE_DATA_OFFSET)) { ERROR("\"%ls\": reparse point buffer is too short", - printable_path(full_path)); + printable_path(ctx)); return WIMLIB_ERR_INVALID_REPARSE_DATA; } @@ -1065,8 +1053,8 @@ winnt_load_reparse_data(HANDLE h, struct wim_inode *inode, return 0; } - if (params->add_flags & WIMLIB_ADD_FLAG_RPFIX) { - ret = winnt_try_rpfix(&rpbuf, &rpbuflen, full_path, params); + if (ctx->params->add_flags & WIMLIB_ADD_FLAG_RPFIX) { + ret = winnt_try_rpfix(&rpbuf, &rpbuflen, ctx->params); if (ret == RP_FIXED) inode->i_rp_flags &= ~WIM_RP_FLAG_NOT_FIXED; else if (ret) @@ -1081,7 +1069,7 @@ winnt_load_reparse_data(HANDLE h, struct wim_inode *inode, NO_STREAM_NAME, rpbuf.rpdata, rpbuflen - REPARSE_DATA_OFFSET, - params->blob_table)) + ctx->params->blob_table)) return WIMLIB_ERR_NOMEM; return 0; @@ -1110,7 +1098,7 @@ win32_get_encrypted_file_size(const wchar_t *path, bool is_dir, u64 *size_ret) if (err != ERROR_SUCCESS) { win32_error(err, L"Failed to open encrypted file \"%ls\" for raw read", - printable_path(path)); + path); return WIMLIB_ERR_OPEN; } *size_ret = 0; @@ -1119,7 +1107,7 @@ win32_get_encrypted_file_size(const wchar_t *path, bool is_dir, u64 *size_ret) if (err != ERROR_SUCCESS) { win32_error(err, L"Failed to read raw encrypted data from \"%ls\"", - printable_path(path)); + path); ret = WIMLIB_ERR_READ; } else { ret = 0; @@ -1130,9 +1118,10 @@ win32_get_encrypted_file_size(const wchar_t *path, bool is_dir, u64 *size_ret) static int winnt_scan_efsrpc_raw_data(struct wim_inode *inode, - wchar_t *path, size_t path_nchars, struct winnt_scan_ctx *ctx) { + wchar_t *path = ctx->params->cur_path; + size_t path_nchars = ctx->params->cur_path_nchars; const bool is_dir = (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY); struct windows_file *windows_file; u64 size; @@ -1192,8 +1181,7 @@ get_data_stream_name(const wchar_t *raw_stream_name, size_t raw_stream_name_ncha } static int -winnt_scan_data_stream(const wchar_t *path, size_t path_nchars, - wchar_t *raw_stream_name, size_t raw_stream_name_nchars, +winnt_scan_data_stream(wchar_t *raw_stream_name, size_t raw_stream_name_nchars, u64 stream_size, struct wim_inode *inode, struct winnt_scan_ctx *ctx) { @@ -1211,7 +1199,8 @@ winnt_scan_data_stream(const wchar_t *path, size_t path_nchars, stream_name[stream_name_nchars] = L'\0'; - windows_file = alloc_windows_file(path, path_nchars, + windows_file = alloc_windows_file(ctx->params->cur_path, + ctx->params->cur_path_nchars, stream_name, stream_name_nchars, ctx->snapshot, false); return add_stream(inode, windows_file, stream_size, STREAM_TYPE_DATA, @@ -1233,8 +1222,7 @@ winnt_scan_data_stream(const wchar_t *path, size_t path_nchars, * already present in Windows XP. */ static noinline_for_stack int -winnt_scan_data_streams(HANDLE h, const wchar_t *path, size_t path_nchars, - struct wim_inode *inode, u64 file_size, +winnt_scan_data_streams(HANDLE h, struct wim_inode *inode, u64 file_size, struct winnt_scan_ctx *ctx) { int ret; @@ -1283,7 +1271,7 @@ winnt_scan_data_streams(HANDLE h, const wchar_t *path, size_t path_nchars, default: winnt_error(status, L"\"%ls\": Failed to query stream information", - printable_path(path)); + printable_path(ctx)); ret = WIMLIB_ERR_READ; goto out_free_buf; } @@ -1299,8 +1287,7 @@ winnt_scan_data_streams(HANDLE h, const wchar_t *path, size_t path_nchars, info = (FILE_STREAM_INFORMATION *)buf; for (;;) { /* Load the stream information. */ - ret = winnt_scan_data_stream(path, path_nchars, - info->StreamName, + ret = winnt_scan_data_stream(info->StreamName, info->StreamNameLength / 2, info->StreamSize.QuadPart, inode, ctx); @@ -1330,8 +1317,8 @@ unnamed_only: { wchar_t stream_name[] = L"::$DATA"; - ret = winnt_scan_data_stream(path, path_nchars, stream_name, 7, - file_size, inode, ctx); + ret = winnt_scan_data_stream(stream_name, 7, file_size, + inode, ctx); } out_free_buf: /* Free buffer if allocated on heap. */ @@ -1375,8 +1362,7 @@ set_sort_key(struct wim_inode *inode, u64 sort_key) static inline bool should_try_to_use_wimboot_hash(const struct wim_inode *inode, - const struct winnt_scan_ctx *ctx, - const struct scan_params *params) + const struct winnt_scan_ctx *ctx) { /* Directories and encrypted files aren't valid for external backing. */ if (inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY | @@ -1388,7 +1374,7 @@ should_try_to_use_wimboot_hash(const struct wim_inode *inode, * fixup if WOF may be attached. */ if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) return (inode->i_reparse_tag == WIM_IO_REPARSE_TAG_WOF) && - (params->add_flags & WIMLIB_ADD_FLAG_WIMBOOT); + (ctx->params->add_flags & WIMLIB_ADD_FLAG_WIMBOOT); return !ctx->wof_not_attached; } @@ -1410,7 +1396,7 @@ should_try_to_use_wimboot_hash(const struct wim_inode *inode, */ static noinline_for_stack int try_to_use_wimboot_hash(HANDLE h, struct wim_inode *inode, - struct winnt_scan_ctx *ctx, const wchar_t *full_path) + struct winnt_scan_ctx *ctx) { struct blob_table *blob_table = ctx->params->blob_table; struct wim_inode_stream *reparse_strm = NULL; @@ -1479,7 +1465,7 @@ try_to_use_wimboot_hash(HANDLE h, struct wim_inode *inode, if (status != STATUS_SUCCESS) { winnt_error(status, L"\"%ls\": FSCTL_GET_EXTERNAL_BACKING failed", - full_path); + printable_path(ctx)); return WIMLIB_ERR_STAT; } @@ -1558,8 +1544,7 @@ get_file_info(HANDLE h, struct file_info *info) } static void -get_volume_information(HANDLE h, const wchar_t *full_path, - struct winnt_scan_ctx *ctx) +get_volume_information(HANDLE h, struct winnt_scan_ctx *ctx) { u8 _attr_info[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + 128] _aligned_attribute(8); FILE_FS_ATTRIBUTE_INFORMATION *attr_info = (void *)_attr_info; @@ -1578,7 +1563,7 @@ get_volume_information(HANDLE h, const wchar_t *full_path, !wmemcmp(attr_info->FileSystemName, L"NTFS", 4); } else { winnt_warning(status, L"\"%ls\": Can't get volume attributes", - printable_path(full_path)); + printable_path(ctx)); } /* Get volume ID. */ @@ -1593,7 +1578,7 @@ get_volume_information(HANDLE h, const wchar_t *full_path, ctx->params->capture_root_dev = vol_info.VolumeSerialNumber; } else { winnt_warning(status, L"\"%ls\": Can't get volume ID", - printable_path(full_path)); + printable_path(ctx)); } /* Get inode number. */ @@ -1602,16 +1587,14 @@ get_volume_information(HANDLE h, const wchar_t *full_path, ctx->params->capture_root_ino = file_info.ino; } else { winnt_warning(status, L"\"%ls\": Can't get file information", - printable_path(full_path)); + printable_path(ctx)); } } static int winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret, HANDLE cur_dir, - wchar_t *full_path, - size_t full_path_nchars, - wchar_t *relative_path, + const wchar_t *relative_path, size_t relative_path_nchars, const wchar_t *filename, struct winnt_scan_ctx *ctx) @@ -1624,7 +1607,7 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret, struct file_info file_info; u64 sort_key; - ret = try_exclude(full_path, ctx->params); + ret = try_exclude(ctx->params); if (unlikely(ret < 0)) /* Excluded? */ goto out_progress; if (unlikely(ret > 0)) /* Error? */ @@ -1643,7 +1626,7 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret, if (unlikely(!NT_SUCCESS(status))) { if (status == STATUS_DELETE_PENDING) { WARNING("\"%ls\": Deletion pending; skipping file", - printable_path(full_path)); + printable_path(ctx)); ret = 0; goto out; } @@ -1651,12 +1634,12 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret, ERROR("Can't open \"%ls\":\n" " File is in use by another process! " "Consider using snapshot (VSS) mode.", - printable_path(full_path)); + printable_path(ctx)); ret = WIMLIB_ERR_OPEN; goto out; } winnt_error(status, L"\"%ls\": Can't open file", - printable_path(full_path)); + printable_path(ctx)); if (status == STATUS_FVE_LOCKED_VOLUME) ret = WIMLIB_ERR_FVE_LOCKED_VOLUME; else @@ -1668,7 +1651,7 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret, status = get_file_info(h, &file_info); if (!NT_SUCCESS(status)) { winnt_error(status, L"\"%ls\": Can't get file information", - printable_path(full_path)); + printable_path(ctx)); ret = WIMLIB_ERR_STAT; goto out; } @@ -1724,19 +1707,19 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret, if (!(ctx->params->add_flags & WIMLIB_ADD_FLAG_NO_ACLS) && (ctx->vol_flags & FILE_PERSISTENT_ACLS)) { - ret = winnt_load_security_descriptor(h, inode, full_path, ctx); + ret = winnt_load_security_descriptor(h, inode, ctx); if (ret) goto out; } /* Get the file's object ID. */ - ret = winnt_load_object_id(h, inode, full_path, ctx); + ret = winnt_load_object_id(h, inode, ctx); if (ret) goto out; /* If this is a reparse point, load the reparse data. */ if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)) { - ret = winnt_load_reparse_data(h, inode, full_path, ctx->params); + ret = winnt_load_reparse_data(h, inode, ctx); if (ret) goto out; } @@ -1754,8 +1737,7 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret, * needed. */ NtClose(h); h = NULL; - ret = winnt_scan_efsrpc_raw_data(inode, full_path, - full_path_nchars, ctx); + ret = winnt_scan_efsrpc_raw_data(inode, ctx); if (ret) goto out; } else { @@ -1770,8 +1752,6 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret, * the EFSRPC data and the named data stream(s)...! */ ret = winnt_scan_data_streams(h, - full_path, - full_path_nchars, inode, file_info.end_of_file, ctx); @@ -1779,8 +1759,8 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret, goto out; } - if (unlikely(should_try_to_use_wimboot_hash(inode, ctx, ctx->params))) { - ret = try_to_use_wimboot_hash(h, inode, ctx, full_path); + if (unlikely(should_try_to_use_wimboot_hash(inode, ctx))) { + ret = try_to_use_wimboot_hash(h, inode, ctx); if (ret) goto out; } @@ -1801,21 +1781,16 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret, &h); if (!NT_SUCCESS(status)) { winnt_error(status, L"\"%ls\": Can't open directory", - printable_path(full_path)); + printable_path(ctx)); ret = WIMLIB_ERR_OPEN; goto out; } - ret = winnt_recurse_directory(h, - full_path, - full_path_nchars, - root, - ctx); + ret = winnt_recurse_directory(h, root, ctx); if (ret) goto out; } out_progress: - ctx->params->progress.scan.cur_path = full_path; if (likely(root)) ret = do_scan_progress(ctx->params, WIMLIB_SCAN_DENTRY_OK, inode); else @@ -1826,7 +1801,7 @@ out: if (unlikely(ret)) { free_dentry_tree(root, ctx->params->blob_table); root = NULL; - ret = report_scan_error(ctx->params, ret, full_path); + ret = report_scan_error(ctx->params, ret); } *root_ret = root; return ret; @@ -2616,7 +2591,6 @@ security_map_destroy(struct security_map *map) */ static int generate_wim_structures_recursive(struct wim_dentry **root_ret, - wchar_t *path, size_t path_nchars, const wchar_t *filename, bool is_primary_name, struct ntfs_inode *ni, struct winnt_scan_ctx *ctx, @@ -2642,17 +2616,15 @@ generate_wim_structures_recursive(struct wim_dentry **root_ret, { ret = winnt_build_dentry_tree_recursive(&root, NULL, - path, - path_nchars, - path, - path_nchars, + ctx->params->cur_path, + ctx->params->cur_path_nchars, filename, ctx); goto out; } /* Test for exclusion based on path. */ - ret = try_exclude(path, ctx->params); + ret = try_exclude(ctx->params); if (unlikely(ret < 0)) /* Excluded? */ goto out_progress; if (unlikely(ret > 0)) /* Error? */ @@ -2706,17 +2678,18 @@ generate_wim_structures_recursive(struct wim_dentry **root_ret, /* Create a mapping for this security ID and insert it * into the security map. */ - status = winnt_open(path, path_nchars, + status = winnt_open(ctx->params->cur_path, + ctx->params->cur_path_nchars, READ_CONTROL | ACCESS_SYSTEM_SECURITY, &h); if (!NT_SUCCESS(status)) { winnt_error(status, L"Can't open \"%ls\" to " "read security descriptor", - printable_path(path)); + printable_path(ctx)); ret = WIMLIB_ERR_OPEN; goto out; } - ret = winnt_load_security_descriptor(h, inode, path, ctx); + ret = winnt_load_security_descriptor(h, inode, ctx); NtClose(h); if (ret) goto out; @@ -2741,16 +2714,16 @@ generate_wim_structures_recursive(struct wim_dentry **root_ret, !(ctx->vol_flags & FILE_SUPPORTS_OPEN_BY_FILE_ID) || !(ctx->params->add_flags & WIMLIB_ADD_FLAG_FILE_PATHS_UNNEEDED)) { - windows_file = alloc_windows_file(path, - path_nchars, + windows_file = alloc_windows_file(ctx->params->cur_path, + ctx->params->cur_path_nchars, ns->name, wcslen(ns->name), ctx->snapshot, false); } else { windows_file = alloc_windows_file_for_file_id(ni->ino, - path, - ctx->params->capture_root_nchars + 1, + ctx->params->cur_path, + ctx->params->root_path_nchars, ctx->snapshot); } @@ -2771,20 +2744,18 @@ generate_wim_structures_recursive(struct wim_dentry **root_ret, const struct ntfs_dentry *nd = ni->first_child; while (nd != NULL) { - const size_t name_len = wcslen(nd->name); - wchar_t *p = path + path_nchars; + size_t orig_path_nchars; struct wim_dentry *child; const struct ntfs_dentry *next = nd->next_child; - if (*(p - 1) != L'\\') - *p++ = L'\\'; - p = wmempcpy(p, nd->name, name_len); - *p = '\0'; + ret = WIMLIB_ERR_NOMEM; + if (!pathbuf_append_name(ctx->params, nd->name, + wcslen(nd->name), + &orig_path_nchars)) + goto out; ret = generate_wim_structures_recursive( &child, - path, - p - path, nd->name, nd->is_primary, (void *)nd - nd->offset_from_inode, @@ -2792,7 +2763,7 @@ generate_wim_structures_recursive(struct wim_dentry **root_ret, inode_map, security_map); - path[path_nchars] = L'\0'; + pathbuf_truncate(ctx->params, orig_path_nchars); if (ret) goto out; @@ -2803,7 +2774,6 @@ generate_wim_structures_recursive(struct wim_dentry **root_ret, } out_progress: - ctx->params->progress.scan.cur_path = path; if (likely(root)) ret = do_scan_progress(ctx->params, WIMLIB_SCAN_DENTRY_OK, inode); else @@ -2823,12 +2793,14 @@ out: } static int -winnt_build_dentry_tree_fast(struct wim_dentry **root_ret, wchar_t *path, - size_t path_nchars, struct winnt_scan_ctx *ctx) +winnt_build_dentry_tree_fast(struct wim_dentry **root_ret, + struct winnt_scan_ctx *ctx) { struct ntfs_inode_map inode_map = { .root = NULL }; struct security_map security_map = { .root = NULL }; struct ntfs_inode *root = NULL; + wchar_t *path = ctx->params->cur_path; + size_t path_nchars = ctx->params->cur_path_nchars; bool adjust_path; int ret; @@ -2857,8 +2829,7 @@ winnt_build_dentry_tree_fast(struct wim_dentry **root_ret, wchar_t *path, root->num_aliases = 1; - ret = generate_wim_structures_recursive(root_ret, path, path_nchars, - L"", false, root, ctx, + ret = generate_wim_structures_recursive(root_ret, L"", false, root, ctx, &inode_map, &security_map); out: ntfs_inode_map_destroy(&inode_map); @@ -2872,28 +2843,17 @@ out: * Entry point for directory tree scans on Windows * *----------------------------------------------------------------------------*/ -#define WINDOWS_NT_MAX_PATH 32768 - int win32_build_dentry_tree(struct wim_dentry **root_ret, const wchar_t *root_disk_path, struct scan_params *params) { - wchar_t *path = NULL; struct winnt_scan_ctx ctx = { .params = params }; UNICODE_STRING ntpath; - size_t ntpath_nchars; HANDLE h = NULL; NTSTATUS status; int ret; - /* WARNING: There is no check for overflow later when this buffer is - * being used! But it's as long as the maximum path length understood - * by Windows NT (which is NOT the same as MAX_PATH). */ - path = MALLOC((WINDOWS_NT_MAX_PATH + 1) * sizeof(wchar_t)); - if (!path) - return WIMLIB_ERR_NOMEM; - if (params->add_flags & WIMLIB_ADD_FLAG_SNAPSHOT) ret = vss_create_snapshot(root_disk_path, &ntpath, &ctx.snapshot); else @@ -2903,28 +2863,21 @@ win32_build_dentry_tree(struct wim_dentry **root_ret, goto out; if (ntpath.Length < 4 * sizeof(wchar_t) || - ntpath.Length > WINDOWS_NT_MAX_PATH * sizeof(wchar_t) || wmemcmp(ntpath.Buffer, L"\\??\\", 4)) { ERROR("\"%ls\": unrecognized path format", root_disk_path); ret = WIMLIB_ERR_INVALID_PARAM; } else { - ntpath_nchars = ntpath.Length / sizeof(wchar_t); - wmemcpy(path, ntpath.Buffer, ntpath_nchars); - path[ntpath_nchars] = L'\0'; - - params->capture_root_nchars = ntpath_nchars; - if (path[ntpath_nchars - 1] == L'\\') - params->capture_root_nchars--; - ret = 0; + ret = pathbuf_init(params, ntpath.Buffer); } HeapFree(GetProcessHeap(), 0, ntpath.Buffer); if (ret) goto out; - status = winnt_open(path, ntpath_nchars, FILE_READ_ATTRIBUTES, &h); + status = winnt_open(params->cur_path, params->cur_path_nchars, + FILE_READ_ATTRIBUTES, &h); if (!NT_SUCCESS(status)) { - winnt_error(status, L"Can't open \"%ls\"", printable_path(path)); + winnt_error(status, L"Can't open \"%ls\"", root_disk_path); if (status == STATUS_FVE_LOCKED_VOLUME) ret = WIMLIB_ERR_FVE_LOCKED_VOLUME; else @@ -2932,14 +2885,13 @@ win32_build_dentry_tree(struct wim_dentry **root_ret, goto out; } - get_volume_information(h, path, &ctx); + get_volume_information(h, &ctx); NtClose(h); #ifdef ENABLE_FAST_MFT_SCAN if (ctx.is_ntfs && !_wgetenv(L"WIMLIB_DISABLE_QUERY_FILE_LAYOUT")) { - ret = winnt_build_dentry_tree_fast(root_ret, path, - ntpath_nchars, &ctx); + ret = winnt_build_dentry_tree_fast(root_ret, &ctx); if (ret >= 0 && ret != WIMLIB_ERR_UNSUPPORTED) goto out; if (ret >= 0) { @@ -2950,12 +2902,11 @@ win32_build_dentry_tree(struct wim_dentry **root_ret, } #endif ret = winnt_build_dentry_tree_recursive(root_ret, NULL, - path, ntpath_nchars, - path, ntpath_nchars, + params->cur_path, + params->cur_path_nchars, L"", &ctx); out: vss_put_snapshot(ctx.snapshot); - FREE(path); if (ret == 0) winnt_do_scan_warnings(root_disk_path, &ctx); return ret; -- 2.43.0