- /* Treat the root dentry specially. */
- if (dentry_is_root(dentry))
- return apply_root_dentry_ntfs(dentry, vol, w,
- args->extract_flags);
-
- /* NTFS filename namespaces need careful consideration. A name for a
- * NTFS file may be in either the POSIX, Win32, DOS, or Win32+DOS
- * namespaces. A NTFS file (a.k.a. inode) may have multiple names in
- * multiple directories (i.e. hard links); however, a NTFS file can have
- * at most 1 DOS name total. Furthermore, a Win32 name is always
- * associated with a DOS name (either as a Win32+DOS name, or a Win32
- * name and a DOS name separately), which implies that a NTFS file can
- * have at most 1 Win32 name.
- *
- * A WIM dentry just contains a "long name", which wimlib makes sure is
- * non-empty, and a "short name", which may be empty. So, wimlib must
- * map these to the correct NTFS names. wimlib collects all WIM
- * dentries that map to the same NTFS inode and factors out the common
- * information into a 'struct wim_inode', so this should make the
- * mapping a little more obvious. As a NTFS file can have at most 1 DOS
- * name, a WIM inode cannot have more than 1 dentry with a non-empty
- * short name, and this is checked in the verify_inode() function in
- * verify.c. Furthermore, a WIM dentry, if any, that has a DOS name
- * must have a long name that corresponds to a Win32 name or Win32+DOS
- * name.
- *
- * WIM dentries that have a long name but no associated short name are
- * assumed to be in the POSIX namespace.
- *
- * So, given a WIM inode that is to map to a NTFS inode, we must apply
- * the Win32 and DOS or Win32+DOS names, if they exist, then any
- * additional (POSIX) names. A caveat when actually doing this: as
- * confirmed by the libntfs-3g authors, ntfs_set_ntfs_dos_name() is only
- * guaranteed to associate a DOS name with the appropriate long name if
- * it's called when that long name is the only one in existence for that
- * file. So, this implies that the correct ordering of function calls
- * to extract a NTFS file are:
+ ret = 0;
+ if (short_name_nchars == 0)
+ goto out;
+
+ vol = ntfs_3g_apply_ctx_get_volume(ctx);
+
+ ret = WIMLIB_ERR_OPEN;
+ dir_ni = ntfs_3g_open_parent_inode(path, vol);
+ if (!dir_ni)
+ goto out;
+
+ ret = WIMLIB_ERR_OPEN;
+ ni = ntfs_pathname_to_inode(vol, NULL, path);
+ if (!ni)
+ goto out_close_dir_ni;
+
+ ret = utf16le_to_tstr(short_name, short_name_nchars * 2,
+ &dosname, &dosname_nbytes);
+ if (ret)
+ goto out_close_ni;
+
+ ret = 0;
+ if (ntfs_set_ntfs_dos_name(ni, dir_ni, dosname,
+ dosname_nbytes, 0))
+ 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_WRITE;
+out_close_dir_ni:
+ if (ntfs_inode_close(dir_ni))
+ 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;
+ 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.