X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fntfs-3g_apply.c;h=bf122a81568710ad4f61f81ac951422e16791c41;hp=b8aabe866e3c2f68d9fc284553e7d89c4065c9fa;hb=01be73ad93236a0f6cf7e000b4f8ac91fea6dff3;hpb=3d84c998673ba7acf82ec5c26769a41e28a2cc7b diff --git a/src/ntfs-3g_apply.c b/src/ntfs-3g_apply.c index b8aabe86..bf122a81 100644 --- a/src/ntfs-3g_apply.c +++ b/src/ntfs-3g_apply.c @@ -31,20 +31,17 @@ #ifdef WITH_NTFS_3G +#include #include +#include #include -#include /* NTFS-3g headers are missing include */ +#ifdef HAVE_ALLOCA_H +# include +#endif #include #include #include -#include - -#ifdef HAVE_ALLOCA_H -# include -#else -# include -#endif #include "wimlib/apply.h" #include "wimlib/encoding.h" @@ -53,6 +50,7 @@ #include "wimlib/ntfs_3g.h" #include "wimlib/paths.h" #include "wimlib/resource.h" +#include "wimlib/security_descriptor.h" static ntfs_volume * ntfs_3g_apply_ctx_get_volume(struct apply_ctx *ctx) @@ -103,7 +101,8 @@ ntfs_3g_open_parent_inode(const char *path, ntfs_volume *vol) } static int -ntfs_3g_create(const char *path, struct apply_ctx *ctx, mode_t mode) +ntfs_3g_create(const char *path, struct apply_ctx *ctx, u64 *cookie_ret, + mode_t mode) { ntfs_volume *vol; ntfs_inode *dir_ni, *ni; @@ -114,7 +113,7 @@ ntfs_3g_create(const char *path, struct apply_ctx *ctx, mode_t mode) vol = ntfs_3g_apply_ctx_get_volume(ctx); - ret = WIMLIB_ERR_NTFS_3G; + ret = WIMLIB_ERR_OPEN; dir_ni = ntfs_3g_open_parent_inode(path, vol); if (!dir_ni) goto out; @@ -125,29 +124,36 @@ ntfs_3g_create(const char *path, struct apply_ctx *ctx, mode_t mode) if (ret) goto out_close_dir_ni; - ret = 0; + ret = WIMLIB_ERR_OPEN; ni = ntfs_create(dir_ni, 0, name_utf16le, name_utf16le_nbytes / 2, mode); - if (!ni || ntfs_inode_close_in_dir(ni, dir_ni)) - ret = WIMLIB_ERR_NTFS_3G; + if (!ni) + goto out_free_name_utf16le; + *cookie_ret = MK_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number)); + if (ntfs_inode_close_in_dir(ni, dir_ni)) + goto out_free_name_utf16le; + ret = 0; +out_free_name_utf16le: FREE(name_utf16le); out_close_dir_ni: if (ntfs_inode_close(dir_ni)) - ret = WIMLIB_ERR_NTFS_3G; + ret = WIMLIB_ERR_WRITE; out: return ret; } static int -ntfs_3g_create_file(const char *path, struct apply_ctx *ctx) +ntfs_3g_create_file(const char *path, struct apply_ctx *ctx, + u64 *cookie_ret) { - return ntfs_3g_create(path, ctx, S_IFREG); + return ntfs_3g_create(path, ctx, cookie_ret, S_IFREG); } static int -ntfs_3g_create_directory(const char *path, struct apply_ctx *ctx) +ntfs_3g_create_directory(const char *path, struct apply_ctx *ctx, + u64 *cookie_ret) { - return ntfs_3g_create(path, ctx, S_IFDIR); + return ntfs_3g_create(path, ctx, cookie_ret, S_IFDIR); } static int @@ -163,12 +169,12 @@ ntfs_3g_create_hardlink(const char *oldpath, const char *newpath, vol = ntfs_3g_apply_ctx_get_volume(ctx); - ret = WIMLIB_ERR_NTFS_3G; + ret = WIMLIB_ERR_OPEN; ni = ntfs_pathname_to_inode(vol, NULL, oldpath); if (!ni) goto out; - ret = WIMLIB_ERR_NTFS_3G; + ret = WIMLIB_ERR_OPEN; dir_ni = ntfs_3g_open_parent_inode(newpath, vol); if (!dir_ni) goto out_close_ni; @@ -180,14 +186,14 @@ ntfs_3g_create_hardlink(const char *oldpath, const char *newpath, goto out_close_dir_ni; ret = 0; if (ntfs_link(ni, dir_ni, name_utf16le, name_utf16le_nbytes / 2)) - ret = WIMLIB_ERR_NTFS_3G; + ret = WIMLIB_ERR_LINK; FREE(name_utf16le); out_close_dir_ni: if (ntfs_inode_close(dir_ni)) - ret = WIMLIB_ERR_NTFS_3G; + ret = WIMLIB_ERR_WRITE; out_close_ni: if (ntfs_inode_close(ni)) - ret = WIMLIB_ERR_NTFS_3G; + ret = WIMLIB_ERR_WRITE; out: return ret; } @@ -196,7 +202,7 @@ out: * Extract a stream (default or alternate data) to an attribute of a NTFS file. */ static int -ntfs_3g_extract_stream(const char *path, const utf16lechar *raw_stream_name, +ntfs_3g_extract_stream(file_spec_t file, const utf16lechar *raw_stream_name, size_t stream_name_nchars, struct wim_lookup_table_entry *lte, struct apply_ctx *ctx) { @@ -219,14 +225,14 @@ ntfs_3g_extract_stream(const char *path, const utf16lechar *raw_stream_name, if (!stream_name_nchars && !lte) goto out; - /* Look up NTFS inode to which to extract the stream. */ - ret = WIMLIB_ERR_NTFS_3G; - ni = ntfs_3g_apply_pathname_to_inode(path, ctx); + /* Open NTFS inode to which to extract the stream. */ + ret = WIMLIB_ERR_OPEN; + ni = ntfs_inode_open(ntfs_3g_apply_ctx_get_volume(ctx), file.cookie); if (!ni) goto out; /* Add the stream if it's not the default (unnamed) stream. */ - ret = WIMLIB_ERR_NTFS_3G; + ret = WIMLIB_ERR_OPEN; if (stream_name_nchars) if (ntfs_attr_add(ni, AT_DATA, stream_name, stream_name_nchars, NULL, 0)) @@ -238,13 +244,23 @@ ntfs_3g_extract_stream(const char *path, const utf16lechar *raw_stream_name, goto out_close; /* Open the stream (NTFS attribute). */ - ret = WIMLIB_ERR_NTFS_3G; + ret = WIMLIB_ERR_OPEN; na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_nchars); if (!na) goto out_close; - /* (Optional) Immediately resize attribute to size of stream. */ - ret = WIMLIB_ERR_NTFS_3G; + /* (Optional) Immediately resize attribute to size of stream. + * + * This dramatically speeds up extraction, as demonstrated with the + * following timing results: + * + * 18 mins. 27 sec. to apply Windows 7 image (with resize) + * 32 mins. 45 sec. to apply Windows 7 image (no resize) + * + * It probably would speed things up even more if we could get NTFS-3g + * to skip even more useless work (for example it fills resized + * attributes with 0's, then we just override it.) */ + ret = WIMLIB_ERR_WRITE; if (ntfs_attr_truncate_solid(na, wim_resource_size(lte))) goto out_attr_close; @@ -258,7 +274,7 @@ out_attr_close: ntfs_attr_close(na); out_close: if (ntfs_inode_close(ni)) - ret = WIMLIB_ERR_NTFS_3G; + ret = WIMLIB_ERR_WRITE; out: if (ret && !errno) errno = -1; @@ -266,36 +282,36 @@ out: } static int -ntfs_3g_extract_unnamed_stream(const char *path, +ntfs_3g_extract_unnamed_stream(file_spec_t file, struct wim_lookup_table_entry *lte, struct apply_ctx *ctx) { - return ntfs_3g_extract_stream(path, NULL, 0, lte, ctx); + return ntfs_3g_extract_stream(file, NULL, 0, lte, ctx); } static int -ntfs_3g_extract_named_stream(const char *path, const utf16lechar *stream_name, +ntfs_3g_extract_named_stream(file_spec_t file, const utf16lechar *stream_name, size_t stream_name_nchars, struct wim_lookup_table_entry *lte, struct apply_ctx *ctx) { - return ntfs_3g_extract_stream(path, stream_name, + return ntfs_3g_extract_stream(file, stream_name, stream_name_nchars, lte, ctx); } static int ntfs_3g_set_file_attributes(const char *path, u32 attributes, - struct apply_ctx *ctx) + struct apply_ctx *ctx, unsigned pass) { ntfs_inode *ni; int ret = 0; ni = ntfs_3g_apply_pathname_to_inode(path, ctx); if (!ni) - return WIMLIB_ERR_NTFS_3G; + return WIMLIB_ERR_OPEN; if (ntfs_set_ntfs_attrib(ni, (const char*)&attributes, sizeof(u32), 0)) - ret = WIMLIB_ERR_NTFS_3G; + ret = WIMLIB_ERR_SET_ATTRIBUTES; if (ntfs_inode_close(ni)) - ret = WIMLIB_ERR_NTFS_3G; + ret = WIMLIB_ERR_WRITE; return ret; } @@ -308,11 +324,11 @@ ntfs_3g_set_reparse_data(const char *path, const u8 *rpbuf, u16 rpbuflen, ni = ntfs_3g_apply_pathname_to_inode(path, ctx); if (!ni) - return WIMLIB_ERR_NTFS_3G; + return WIMLIB_ERR_OPEN; if (ntfs_set_ntfs_reparse_data(ni, rpbuf, rpbuflen, 0)) - ret = WIMLIB_ERR_NTFS_3G; + ret = WIMLIB_ERR_SET_REPARSE_DATA; if (ntfs_inode_close(ni)) - ret = WIMLIB_ERR_NTFS_3G; + ret = WIMLIB_ERR_WRITE; return ret; } @@ -332,12 +348,12 @@ ntfs_3g_set_short_name(const char *path, const utf16lechar *short_name, vol = ntfs_3g_apply_ctx_get_volume(ctx); - ret = WIMLIB_ERR_NTFS_3G; + ret = WIMLIB_ERR_OPEN; dir_ni = ntfs_3g_open_parent_inode(path, vol); if (!dir_ni) goto out; - ret = WIMLIB_ERR_NTFS_3G; + ret = WIMLIB_ERR_OPEN; ni = ntfs_pathname_to_inode(vol, NULL, path); if (!ni) goto out_close_dir_ni; @@ -350,42 +366,160 @@ ntfs_3g_set_short_name(const char *path, const utf16lechar *short_name, ret = 0; if (ntfs_set_ntfs_dos_name(ni, dir_ni, dosname, dosname_nbytes, 0)) - ret = WIMLIB_ERR_NTFS_3G; + ret = WIMLIB_ERR_SET_SHORT_NAME; /* ntfs_set_ntfs_dos_name() always closes the inodes. */ FREE(dosname); goto out; out_close_ni: if (ntfs_inode_close_in_dir(ni, dir_ni)) - ret = WIMLIB_ERR_NTFS_3G; + ret = WIMLIB_ERR_WRITE; out_close_dir_ni: if (ntfs_inode_close(dir_ni)) - ret = WIMLIB_ERR_NTFS_3G; + ret = WIMLIB_ERR_WRITE; out: return ret; } +static size_t +sid_size(const wimlib_SID *sid) +{ + return offsetof(wimlib_SID, sub_authority) + + sizeof(le32) * sid->sub_authority_count; +} + +/* + * sd_fixup - Fix up a Windows NT security descriptor for libntfs-3g. + * + * libntfs-3g validates security descriptors before setting them, but old + * versions contain bugs causing it to reject unusual but valid security + * descriptors: + * + * - Versions before 2013.1.13 reject security descriptors ending with an empty + * SACL (System Access Control List). This bug can be worked around either by + * moving the empty SACL earlier in the security descriptor or by removing the + * SACL entirely. The latter work-around is valid because an empty SACL is + * equivalent to a "null", or non-existent, SACL. + * - Versions up to and including 2013.1.13 reject security descriptors ending + * with an empty DACL (Discretionary Access Control List). This is very + * similar to the SACL bug and should be fixed in the next release after + * 2013.1.13. However, removing the DACL is not a valid workaround because + * this changes the meaning of the security descriptor--- an empty DACL allows + * no access, whereas a "null" DACL allows all access. + * + * If the security descriptor was fixed, this function returns an allocated + * buffer containing the fixed security descriptor, and its size is updated. + * Otherwise (or if no memory is available) the original descriptor is returned. + */ +static u8 * +sd_fixup(const u8 *_desc, size_t *size_p) +{ + u32 owner_offset, group_offset, dacl_offset, sacl_offset; + bool owner_valid, group_valid; + size_t size = *size_p; + const wimlib_SECURITY_DESCRIPTOR_RELATIVE *desc = + (const wimlib_SECURITY_DESCRIPTOR_RELATIVE*)_desc; + wimlib_SECURITY_DESCRIPTOR_RELATIVE *desc_new; + u32 sid_offset; + const wimlib_SID *owner, *group, *sid; + + /* Don't attempt to fix clearly invalid security descriptors. */ + if (size < sizeof(wimlib_SECURITY_DESCRIPTOR_RELATIVE)) + return (u8*)_desc; + + if (le16_to_cpu(desc->control) & wimlib_SE_DACL_PRESENT) + dacl_offset = le32_to_cpu(desc->dacl_offset); + else + dacl_offset = 0; + + if (le16_to_cpu(desc->control) & wimlib_SE_SACL_PRESENT) + sacl_offset = le32_to_cpu(desc->sacl_offset); + else + sacl_offset = 0; + + /* Check if the security descriptor will be affected by one of the bugs. + * If not, do nothing and return. + * + * Note: HAVE_NTFS_MNT_RDONLY is defined if libntfs-3g is + * version 2013.1.13 or later. */ + if (!( + #if !defined(HAVE_NTFS_MNT_RDONLY) + (sacl_offset != 0 && sacl_offset == size - sizeof(wimlib_ACL)) || + #endif + (dacl_offset != 0 && dacl_offset == size - sizeof(wimlib_ACL)))) + return (u8*)_desc; + + owner_offset = le32_to_cpu(desc->owner_offset); + group_offset = le32_to_cpu(desc->group_offset); + owner = (const wimlib_SID*)((const u8*)desc + owner_offset); + group = (const wimlib_SID*)((const u8*)desc + group_offset); + + /* We'll try to move the owner or group SID to the end of the security + * descriptor to avoid the bug. This is only possible if at least one + * is valid. */ + owner_valid = (owner_offset != 0) && + (owner_offset % 4 == 0) && + (owner_offset <= size - sizeof(SID)) && + (owner_offset + sid_size(owner) <= size) && + (owner_offset >= sizeof(wimlib_SECURITY_DESCRIPTOR_RELATIVE)); + group_valid = (group_offset != 0) && + (group_offset % 4 == 0) && + (group_offset <= size - sizeof(SID)) && + (group_offset + sid_size(group) <= size) && + (group_offset >= sizeof(wimlib_SECURITY_DESCRIPTOR_RELATIVE)); + if (owner_valid) { + sid = owner; + } else if (group_valid) { + sid = group; + } else { + return (u8*)_desc; + } + + desc_new = MALLOC(size + sid_size(sid)); + if (desc_new == NULL) + return (u8*)_desc; + + memcpy(desc_new, desc, size); + if (owner_valid) + desc_new->owner_offset = cpu_to_le32(size); + else if (group_valid) + desc_new->group_offset = cpu_to_le32(size); + memcpy((u8*)desc_new + size, sid, sid_size(sid)); + *size_p = size + sid_size(sid); + return (u8*)desc_new; +} + static int ntfs_3g_set_security_descriptor(const char *path, const u8 *desc, size_t desc_size, - struct apply_ctx *ctx, bool strict) + struct apply_ctx *ctx) { ntfs_volume *vol; ntfs_inode *ni; struct SECURITY_CONTEXT sec_ctx; - int ret = 0; + u8 *desc_fixed; + int ret; vol = ntfs_3g_apply_ctx_get_volume(ctx); ni = ntfs_pathname_to_inode(vol, NULL, path); if (!ni) - return WIMLIB_ERR_NTFS_3G; + return WIMLIB_ERR_OPEN; memset(&sec_ctx, 0, sizeof(sec_ctx)); sec_ctx.vol = vol; - if (ntfs_set_ntfs_acl(&sec_ctx, ni, desc, desc_size, 0)) - ret = WIMLIB_ERR_NTFS_3G; + desc_fixed = sd_fixup(desc, &desc_size); + + ret = 0; + + if (ntfs_set_ntfs_acl(&sec_ctx, ni, desc_fixed, desc_size, 0)) + ret = WIMLIB_ERR_SET_SECURITY; + + if (desc_fixed != desc) + FREE(desc_fixed); + if (ntfs_inode_close(ni)) - ret = WIMLIB_ERR_NTFS_3G; + ret = WIMLIB_ERR_WRITE; + return ret; } @@ -400,7 +534,7 @@ ntfs_3g_set_timestamps(const char *path, u64 creation_time, ni = ntfs_3g_apply_pathname_to_inode(path, ctx); if (!ni) - return WIMLIB_ERR_NTFS_3G; + return WIMLIB_ERR_OPEN; /* Note: ntfs_inode_set_times() expects the times in native byte order, * not little endian. */ @@ -410,9 +544,9 @@ ntfs_3g_set_timestamps(const char *path, u64 creation_time, if (ntfs_inode_set_times(ni, (const char*)ntfs_timestamps, sizeof(ntfs_timestamps), 0)) - ret = WIMLIB_ERR_NTFS_3G; + ret = WIMLIB_ERR_SET_TIMESTAMPS; if (ntfs_inode_close(ni)) - ret = WIMLIB_ERR_NTFS_3G; + ret = WIMLIB_ERR_WRITE; return ret; } @@ -431,7 +565,7 @@ ntfs_3g_start_extract(const char *path, struct apply_ctx *ctx) vol = ntfs_mount(ctx->target, 0); if (!vol) { ERROR_WITH_ERRNO("Failed to mount \"%"TS"\" with NTFS-3g", ctx->target); - return WIMLIB_ERR_NTFS_3G; + return WIMLIB_ERR_OPEN; } ntfs_3g_apply_ctx_set_volume(ctx, vol); @@ -459,7 +593,7 @@ ntfs_3g_finish_or_abort_extract(struct apply_ctx *ctx) if (ntfs_umount(vol, FALSE)) { ERROR_WITH_ERRNO("Failed to unmount \"%"TS"\" with NTFS-3g", ctx->target); - return WIMLIB_ERR_NTFS_3G; + return WIMLIB_ERR_WRITE; } return 0; } @@ -493,8 +627,42 @@ const struct apply_operations ntfs_3g_apply_ops = { .path_separator = '/', .path_max = 32768, + /* By default, NTFS-3g creates names in the NTFS POSIX namespace, which + * is case-sensitive. */ .supports_case_sensitive_filenames = 1, + + /* The root directory of the NTFS volume should not be created + * explicitly. */ .root_directory_is_special = 1, + + /* NTFS-3g can open files by MFT reference. */ + .uses_cookies = 1, + + /* + * With NTFS-3g, the extraction order of the names of a file that has a + * short name needs to be: + * + * 1. Create file using the long name that has an associated short name. + * This long name is temporarily placed in the POSIX namespace. + * 2. Set the short name on the file. This will either change the POSIX + * name to Win32 and create a new DOS name, or replace the POSIX name + * with a Win32+DOS name. + * 3. Create additional long names (links) of the file, which are placed + * in the POSIX namespace. + * + * The reason for this is that two issues can come up when the + * extraction is done otherwise: + * + * - If a DOS name is set on a file in a directory with several long + * names, it is ambiguous which long name to use (at least with the + * exported ntfs_set_ntfs_dos_name() function). + * - NTFS-3g 2013.1.13 will no longer allow even setting the DOS name on + * a file with multiple existing long names, even if those long names + * are in different directories and the ntfs_set_ntfs_dos_name() call + * is therefore unambiguous. (This was apparently changed with the + * FUSE interface in mind.) + */ + .requires_short_name_reordering = 1, }; #endif /* WITH_NTFS_3G */