- /* 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:
- *
- * if (file has a DOS name) {
- * - Call ntfs_create() to create long name associated with
- * the DOS name (this initially creates a POSIX name)
- * - Call ntfs_set_ntfs_dos_name() to associate a DOS name
- * with the long name just created. This either changes
- * the POSIX name to Win32+DOS, or changes the POSIX name
- * to Win32 and creates a separate DOS name.
- * } else {
- * - Call ntfs_create() to create the first link to the
- * file in the POSIX namespace
- * }
- * - Call ntfs_link() to create the other names of the file, in the
- * POSIX namespace.
+/*
+ * Note: prior to NTFS-3G version 2016.2.22, ntfs_attr_pwrite() could return a
+ * short count in non-error cases, contrary to its documentation. Specifically,
+ * a short count could be returned when writing to a compressed attribute and
+ * the requested count exceeded the size of an NTFS "compression block".
+ * Therefore, we must continue calling ntfs_attr_pwrite() until all bytes have
+ * been written or a real error has occurred.
+ */
+static bool
+ntfs_3g_full_pwrite(ntfs_attr *na, u64 offset, size_t size, const u8 *data)
+{
+ while (size) {
+ s64 res = ntfs_attr_pwrite(na, offset, size, data);
+ if (unlikely(res <= 0))
+ return false;
+ wimlib_assert(res <= size);
+ offset += res;
+ size -= res;
+ data += res;
+ }
+ return true;
+}
+
+static int
+ntfs_3g_extract_chunk(const struct blob_descriptor *blob, u64 offset,
+ const void *chunk, size_t size, void *_ctx)
+{
+ struct ntfs_3g_apply_ctx *ctx = _ctx;
+ const void * const end = chunk + size;
+ const void *p;
+ bool zeroes;
+ size_t len;
+ unsigned i;
+
+ /*
+ * For sparse attributes, only write nonzero regions. This lets the
+ * filesystem use holes to represent zero regions.