#include "wimlib/reparse.h"
#include "wimlib/security.h"
-static inline ntfschar *
-attr_record_name(ATTR_RECORD *ar)
+static inline const ntfschar *
+attr_record_name(const ATTR_RECORD *record)
{
- return (ntfschar*)((u8*)ar + le16_to_cpu(ar->name_offset));
+ return (const ntfschar *)
+ ((const u8 *)record + le16_to_cpu(record->name_offset));
}
static ntfs_attr *
loc->attr_name,
loc->attr_name_nchars);
if (!na) {
- ERROR_WITH_ERRNO("Failed to open attribute of \"%"TS"\" in "
- "NTFS volume", loc->path);
+ ERROR_WITH_ERRNO("Failed to open attribute of NTFS inode %"PRIu64,
+ loc->mft_no);
}
return na;
}
int ret;
u8 buf[BUFFER_SIZE];
- ni = ntfs_pathname_to_inode(vol, NULL, loc->path);
+ ni = ntfs_inode_open(vol, loc->mft_no);
if (!ni) {
- ERROR_WITH_ERRNO("Can't find NTFS inode for \"%"TS"\"", loc->path);
+ ERROR_WITH_ERRNO("Failed to open NTFS inode %"PRIu64,
+ loc->mft_no);
ret = WIMLIB_ERR_NTFS_3G;
goto out;
}
while (bytes_remaining) {
s64 to_read = min(bytes_remaining, sizeof(buf));
if (ntfs_attr_pread(na, pos, to_read, buf) != to_read) {
- ERROR_WITH_ERRNO("Error reading \"%"TS"\"", loc->path);
+ ERROR_WITH_ERRNO("Error reading data from NTFS inode "
+ "%"PRIu64, loc->mft_no);
ret = WIMLIB_ERR_NTFS_3G;
goto out_close_ntfs_attr;
}
}
}
-/* Load attributes of the specified type from a file in the NTFS volume */
+/* When sorting blobs located in NTFS volumes for sequential reading, we sort
+ * first by starting LCN of the attribute if available, otherwise no sort order
+ * is defined. This usually results in better sequential access to the volume.
+ */
+static int
+set_attr_sort_key(ntfs_inode *ni, struct ntfs_location *loc)
+{
+ ntfs_attr *na;
+ runlist_element *rl;
+
+ na = open_ntfs_attr(ni, loc);
+ if (!na)
+ return WIMLIB_ERR_NTFS_3G;
+
+ rl = ntfs_attr_find_vcn(na, 0);
+ if (rl && rl->lcn != LCN_HOLE)
+ loc->sort_key = rl->lcn;
+ else
+ loc->sort_key = 0;
+
+ ntfs_attr_close(na);
+ return 0;
+}
+
+/* Save information about an NTFS attribute (stream) to a WIM inode. */
static int
-load_ntfs_attrs_with_type(struct wim_inode *inode,
+scan_ntfs_attr(struct wim_inode *inode,
+ ntfs_inode *ni,
+ const char *path,
+ size_t path_len,
+ struct list_head *unhashed_blobs,
+ ntfs_volume *vol,
+ ATTR_TYPES type,
+ const ATTR_RECORD *record)
+{
+ const u64 data_size = ntfs_get_attribute_value_length(record);
+ const size_t name_nchars = record->name_length;
+ struct blob_descriptor *blob = NULL;
+ utf16lechar *stream_name = NULL;
+ struct wim_inode_stream *strm;
+ int ret;
+
+ if (unlikely(name_nchars)) {
+ /* Named stream */
+ stream_name = utf16le_dupz(attr_record_name(record),
+ name_nchars * sizeof(ntfschar));
+ if (!stream_name) {
+ ret = WIMLIB_ERR_NOMEM;
+ goto out_cleanup;
+ }
+ }
+
+ /* If the stream is non-empty, set up a blob descriptor for it. */
+ if (data_size != 0) {
+ blob = new_blob_descriptor();
+ if (unlikely(!blob)) {
+ ret = WIMLIB_ERR_NOMEM;
+ goto out_cleanup;
+ }
+
+ blob->ntfs_loc = CALLOC(1, sizeof(struct ntfs_location));
+ if (unlikely(!blob->ntfs_loc)) {
+ ret = WIMLIB_ERR_NOMEM;
+ goto out_cleanup;
+ }
+
+ blob->blob_location = BLOB_IN_NTFS_VOLUME;
+ blob->size = data_size;
+ blob->ntfs_loc->ntfs_vol = vol;
+ blob->ntfs_loc->attr_type = type;
+ blob->ntfs_loc->mft_no = ni->mft_no;
+
+ if (unlikely(name_nchars)) {
+ blob->ntfs_loc->attr_name = utf16le_dup(stream_name);
+ if (!blob->ntfs_loc->attr_name) {
+ ret = WIMLIB_ERR_NOMEM;
+ goto out_cleanup;
+ }
+ blob->ntfs_loc->attr_name_nchars = name_nchars;
+ }
+
+ ret = set_attr_sort_key(ni, blob->ntfs_loc);
+ if (ret)
+ goto out_cleanup;
+
+ if (unlikely(type == AT_REPARSE_POINT)) {
+ if (blob->size < REPARSE_DATA_OFFSET) {
+ ERROR("Reparse data of \"%s\" "
+ "is invalid (only %"PRIu64" bytes)!",
+ path, data_size);
+ ret = WIMLIB_ERR_INVALID_REPARSE_DATA;
+ goto out_cleanup;
+ }
+ blob->size -= REPARSE_DATA_OFFSET;
+ ret = read_reparse_tag(ni, blob->ntfs_loc,
+ &inode->i_reparse_tag);
+ if (ret)
+ goto out_cleanup;
+ }
+ }
+
+ strm = inode_add_stream(inode,
+ attr_type_to_wimlib_stream_type(type),
+ stream_name ? stream_name : NO_STREAM_NAME,
+ blob);
+ if (unlikely(!strm)) {
+ ret = WIMLIB_ERR_NOMEM;
+ goto out_cleanup;
+ }
+ prepare_unhashed_blob(blob, inode, strm->stream_id, unhashed_blobs);
+ blob = NULL;
+ ret = 0;
+out_cleanup:
+ free_blob_descriptor(blob);
+ FREE(stream_name);
+ return ret;
+}
+
+/* Scan attributes of the specified type from a file in the NTFS volume */
+static int
+scan_ntfs_attrs_with_type(struct wim_inode *inode,
ntfs_inode *ni,
char *path,
size_t path_len,
ATTR_TYPES type)
{
ntfs_attr_search_ctx *actx;
- struct ntfs_location *ntfs_loc;
int ret;
- struct blob_descriptor *blob;
- utf16lechar *stream_name;
- DEBUG("Loading NTFS attributes from \"%s\"", path);
+ DEBUG("Scanning NTFS attributes from \"%s\"", path);
- /* Get context to search the attributes of the NTFS file. */
actx = ntfs_attr_get_search_ctx(ni, NULL);
if (!actx) {
- ERROR_WITH_ERRNO("Cannot get NTFS attribute search "
+ ERROR_WITH_ERRNO("Failed to get NTFS attribute search "
"context for \"%s\"", path);
return WIMLIB_ERR_NTFS_3G;
}
- /* Save each attribute */
while (!ntfs_attr_lookup(type, NULL, 0,
CASE_SENSITIVE, 0, NULL, 0, actx))
{
- u64 data_size = ntfs_get_attribute_value_length(actx->attr);
- size_t name_nchars = actx->attr->name_length;
- struct wim_inode_stream *strm;
-
- if (name_nchars) {
- stream_name = utf16le_dupz(attr_record_name(actx->attr),
- name_nchars * sizeof(ntfschar));
- if (!stream_name) {
- ret = WIMLIB_ERR_NOMEM;
- goto out_put_actx;
- }
- } else {
- stream_name = NULL;
- }
-
- if (data_size == 0) {
- /* Empty attribute. No blob is needed. */
- blob = NULL;
- ntfs_loc = NULL;
- } else {
- ntfs_loc = CALLOC(1, sizeof(*ntfs_loc));
- if (!ntfs_loc) {
- ret = WIMLIB_ERR_NOMEM;
- goto out_free_stream_name;
- }
- ntfs_loc->ntfs_vol = vol;
- ntfs_loc->attr_type = type;
- ntfs_loc->path = memdup(path, path_len + 1);
- if (!ntfs_loc->path) {
- ret = WIMLIB_ERR_NOMEM;
- goto out_free_ntfs_loc;
- }
- if (name_nchars) {
- ntfs_loc->attr_name = utf16le_dup(stream_name);
- if (!ntfs_loc->attr_name) {
- ret = WIMLIB_ERR_NOMEM;
- goto out_free_ntfs_loc;
- }
- ntfs_loc->attr_name_nchars = name_nchars;
- }
-
- blob = new_blob_descriptor();
- if (!blob) {
- ret = WIMLIB_ERR_NOMEM;
- goto out_free_ntfs_loc;
- }
- blob->blob_location = BLOB_IN_NTFS_VOLUME;
- blob->ntfs_loc = ntfs_loc;
- blob->size = data_size;
- ntfs_loc = NULL;
- if (type == AT_REPARSE_POINT) {
- if (data_size < REPARSE_DATA_OFFSET) {
- ERROR("Reparse data of \"%s\" "
- "is invalid (only %u bytes)!",
- path, (unsigned)data_size);
- ret = WIMLIB_ERR_NTFS_3G;
- goto out_free_blob;
- }
- blob->size -= REPARSE_DATA_OFFSET;
- ret = read_reparse_tag(ni, blob->ntfs_loc,
- &inode->i_reparse_tag);
- if (ret)
- goto out_free_blob;
- }
- }
-
- strm = inode_add_stream(inode,
- attr_type_to_wimlib_stream_type(type),
- stream_name ? stream_name : NO_STREAM_NAME,
- blob);
- if (!strm) {
- ret = WIMLIB_ERR_NOMEM;
- goto out_free_blob;
- }
- prepare_unhashed_blob(blob, inode, strm->stream_id, unhashed_blobs);
-
- FREE(stream_name);
- stream_name = NULL;
+ ret = scan_ntfs_attr(inode,
+ ni,
+ path,
+ path_len,
+ unhashed_blobs,
+ vol,
+ type,
+ actx->attr);
+ if (ret)
+ goto out_put_actx;
}
- if (errno == ENOENT) {
- ret = 0;
- } else {
+ if (errno != ENOENT) {
ERROR_WITH_ERRNO("Error listing NTFS attributes of \"%s\"", path);
ret = WIMLIB_ERR_NTFS_3G;
+ goto out_put_actx;
}
- goto out_put_actx;
-out_free_blob:
- free_blob_descriptor(blob);
-out_free_ntfs_loc:
- if (ntfs_loc) {
- FREE(ntfs_loc->path);
- FREE(ntfs_loc->attr_name);
- FREE(ntfs_loc);
- }
-out_free_stream_name:
- FREE(stream_name);
+ ret = 0;
out_put_actx:
ntfs_attr_put_search_ctx(actx);
- if (ret == 0)
- DEBUG("Successfully loaded NTFS attributes from \"%s\"", path);
- else
- ERROR("Failed to load NTFS attributes from \"%s\"", path);
return ret;
}
inode->i_attributes = attributes;
if (attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
- /* Load the reparse point stream. */
- ret = load_ntfs_attrs_with_type(inode, ni, path, path_len,
+ /* Scan the reparse point stream. */
+ ret = scan_ntfs_attrs_with_type(inode, ni, path, path_len,
params->unhashed_blobs,
vol, AT_REPARSE_POINT);
if (ret)
goto out;
}
- /* Load the data streams.
+ /* Scan the data streams.
*
* Note: directories should not have an unnamed data stream, but they
* may have named data streams. Nondirectories (including reparse
* points) can have an unnamed data stream as well as named data
* streams. */
- ret = load_ntfs_attrs_with_type(inode, ni, path, path_len,
+ ret = scan_ntfs_attrs_with_type(inode, ni, path, path_len,
params->unhashed_blobs, vol, AT_DATA);
if (ret)
goto out;