*/
/*
- * Copyright (C) 2012 Eric Biggers
+ * Copyright (C) 2012, 2013 Eric Biggers
*
* This file is part of wimlib, a library for working with WIM files.
*
unsigned stream_idx = 0;
ntfschar *stream_name = AT_UNNAMED;
u32 stream_name_len = 0;
+ const char *stream_name_utf8;
const struct wim_inode *inode = dentry->d_inode;
struct wim_lookup_table_entry *lte;
lte = inode->i_lte;
while (1) {
if (stream_name_len) {
+
+ /* Skip special UNIX data entries (see documentation for
+ * WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA) */
+ if (stream_name_len == WIMLIB_UNIX_DATA_TAG_LEN
+ && !memcmp(stream_name_utf8,
+ WIMLIB_UNIX_DATA_TAG,
+ WIMLIB_UNIX_DATA_TAG_LEN))
+ goto cont;
+
/* Create an empty named stream. */
ret = ntfs_attr_add(ni, AT_DATA, stream_name,
stream_name_len, NULL, 0);
* have been extracted. */
progress_info->extract.completed_bytes += wim_resource_size(lte);
}
+ cont:
if (stream_idx == inode->i_num_ads) /* Has the last stream been extracted? */
break;
/* Get the name and lookup table entry for the next stream. */
stream_name = (ntfschar*)inode->i_ads_entries[stream_idx].stream_name;
+ stream_name_utf8 = inode->i_ads_entries[stream_idx].stream_name_utf8;
stream_name_len = inode->i_ads_entries[stream_idx].stream_name_len / 2;
lte = inode->i_ads_entries[stream_idx].lte;
stream_idx++;
* existing NTFS inode which already has a name @inode->i_extracted_file.
*
* The new name is made in the POSIX namespace (this is the behavior of
- * ntfs_link()). I am assuming this is an acceptable behavior; however, it's
- * possible that the original name was actually in the Win32 namespace. Note
- * that the WIM format does not provide enough information to distinguish Win32
- * names from POSIX names in all cases.
+ * ntfs_link()).
*
- * Return 0 on success, nonzero on failure.
+ * Return 0 on success, nonzero on failure. dir_ni is closed either way.
*/
static int apply_ntfs_hardlink(const struct wim_dentry *from_dentry,
const struct wim_inode *inode,
- ntfs_inode **dir_ni_p)
+ ntfs_inode *dir_ni)
{
int ret;
ntfs_inode *to_ni;
- ntfs_inode *dir_ni;
ntfs_volume *vol;
- dir_ni = *dir_ni_p;
vol = dir_ni->vol;
ret = ntfs_inode_close(dir_ni);
- *dir_ni_p = NULL;
if (ret != 0) {
ERROR_WITH_ERRNO("Error closing directory");
return WIMLIB_ERR_NTFS_3G;
return WIMLIB_ERR_NTFS_3G;
}
- *dir_ni_p = dir_ni;
-
ret = ntfs_link(to_ni, dir_ni,
(ntfschar*)from_dentry->file_name,
from_dentry->file_name_len / 2);
- if (ntfs_inode_close_in_dir(to_ni, dir_ni) || ret != 0) {
+ ret |= ntfs_inode_close(dir_ni);
+ ret |= ntfs_inode_close(to_ni);
+ if (ret) {
ERROR_WITH_ERRNO("Could not create hard link `%s' => `%s'",
from_dentry->full_path_utf8,
inode->i_extracted_file);
u8 reparse_data_buf[8 + wim_resource_size(lte)];
u8 *p = reparse_data_buf;
p = put_u32(p, dentry->d_inode->i_reparse_tag); /* ReparseTag */
+ DEBUG("ReparseTag = %#x", dentry->d_inode->i_reparse_tag);
p = put_u16(p, wim_resource_size(lte)); /* ReparseDataLength */
p = put_u16(p, 0); /* Reserved */
int ret = 0;
mode_t type;
ntfs_inode *ni = NULL;
- ntfs_volume *vol = dir_ni->vol;
struct wim_inode *inode = dentry->d_inode;
dentry->is_extracted = 1;
type = S_IFREG;
if (inode->i_nlink > 1) {
/* Inode has multiple dentries referencing it. */
-
if (inode->i_extracted_file) {
/* Already extracted another dentry in the hard
* link group. Make a hard link instead of
* extracting the file data. */
- ret = apply_ntfs_hardlink(dentry, inode,
- &dir_ni);
- goto out_close_dir_ni;
+ ret = apply_ntfs_hardlink(dentry, inode, dir_ni);
+ /* dir_ni was closed */
+ goto out;
} else {
/* None of the dentries of this inode have been
* extracted yet, so go ahead and extract the
/* Treat the root dentry specially. */
if (dentry_is_root(dentry))
return apply_root_dentry_ntfs(dentry, vol, w);
+
/* NTFS filename namespaces need careful consideration. A name for a
* NTFS file may be in either the POSIX, Win32, DOS, or Win32+DOS
- * namespaces. The following list of assumptions and facts clarify the
- * way that WIM dentries are mapped to NTFS files. The statements
- * marked ASSUMPTION are statements I am assuming to be true due to the
- * lack of documentation; they are verified in verify_dentry() and
- * verify_inode() in verify.c.
+ * 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.
*
- * - ASSUMPTION: The root WIM dentry has neither a "long name" nor a
- * "short 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.
*
- * - ASSUMPTION: Every WIM dentry other than the root directory provides
- * a non-empty "long name" and a possibly empty "short name". The
- * "short name" corresponds to the DOS name of the file, while the
- * "long name" may be Win32 or POSIX.
+ * WIM dentries that have a long name but no associated short name are
+ * assumed to be in the POSIX namespace.
*
- * XXX It may actually be legal to have a short name but no long name
+ * 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:
*
- * - FACT: If a dentry has a "long name" but no "short name", then it is
- * ambigious whether the name is POSIX or Win32+DOS, unless the name
- * is a valid POSIX name but not a valid Win32+DOS name. wimlib
- * currently will always create POSIX names for these files, as this
- * is the behavior of the ntfs_create() and ntfs_link() functions.
- *
- * - FACT: Multiple WIM dentries may correspond to the same underlying
- * inode, as provided at this point in the code by the d_inode member.
+ * 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.
*/
-
-
- /* Currently wimlib does not apply DOS names to hard linked files due to
- * issues with ntfs-3g, so the following is commented out. */
-#if 0
again:
- /*
- * libntfs-3g requires that for an NTFS inode with a DOS name, the
- * corresponding long name be extracted first so that the DOS name is
- * associated with the correct long name. Note that by the last
- * ASSUMPTION above, a NTFS inode can have at most one DOS name (i.e. a
- * WIM inode can have at most one non-empty short name).
- *
- * Therefore, search for an alias of this dentry that has a short name,
- * and extract it first unless it was already extracted.
- */
orig_dentry = NULL;
- if (!dentry->d_inode->i_dos_name_extracted) {
+ if (!dentry->d_inode->i_dos_name_extracted &&
+ dentry->short_name_len == 0)
+ {
inode_for_each_dentry(other, dentry->d_inode) {
- if (other->short_name_len && other != dentry &&
- !other->is_extracted)
- {
+ if (other->short_name_len != 0) {
orig_dentry = dentry;
dentry = other;
break;
}
}
- dentry->d_inode->i_dos_name_extracted = 1;
}
-#endif
-
+ dentry->d_inode->i_dos_name_extracted = 1;
ntfs_inode *dir_ni = dentry_open_parent_ni(dentry, vol);
- if (dir_ni)
+ if (dir_ni) {
ret = do_apply_dentry_ntfs(dentry, dir_ni, arg);
- else
+ if (ret == 0 && orig_dentry != NULL) {
+ dentry = orig_dentry;
+ goto again;
+ }
+ } else {
ret = WIMLIB_ERR_NTFS_3G;
-
-#if 0
- if (ret == 0 && orig_dentry) {
- dentry = orig_dentry;
- goto again;
}
-#endif
return ret;
}