X-Git-Url: https://wimlib.net/git/?a=blobdiff_plain;f=src%2Fntfs-apply.c;h=fcf141f5b89e3cfdea469aa70cdda66ecd9d2689;hb=ba9a659ed6efcc409de76c250d9fa0971b92b954;hp=998702f2d1959cba9d8b82085bc95c4b776704f9;hpb=1530b6dab02a9e1e5faf81529ab502aee68d8cd2;p=wimlib diff --git a/src/ntfs-apply.c b/src/ntfs-apply.c index 998702f2..fcf141f5 100644 --- a/src/ntfs-apply.c +++ b/src/ntfs-apply.c @@ -197,21 +197,21 @@ static ntfs_inode *dentry_open_parent_ni(const struct wim_dentry *dentry, * Or, in other words, this adds a new name @from_dentry->full_path_utf8 to an * existing NTFS inode which already has a name @inode->i_extracted_file. * - * Return 0 on success, nonzero on failure. + * The new name is made in the POSIX namespace (this is the behavior of + * ntfs_link()). + * + * 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; @@ -233,12 +233,12 @@ static int apply_ntfs_hardlink(const struct wim_dentry *from_dentry, 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); @@ -338,6 +338,7 @@ static int apply_reparse_data(ntfs_inode *ni, const struct wim_dentry *dentry, 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 */ @@ -356,57 +357,6 @@ static int apply_reparse_data(ntfs_inode *ni, const struct wim_dentry *dentry, return 0; } -static int do_apply_dentry_ntfs(struct wim_dentry *dentry, ntfs_inode *dir_ni, - struct apply_args *args); - -/* - * If @dentry is part of a hard link group, search for hard-linked dentries in - * the same directory that have a nonempty DOS (short) filename. There should - * be exactly 0 or 1 such dentries. If there is 1, extract that dentry first, - * so that the DOS name is correctly associated with the corresponding long name - * in the Win32 namespace, and not any of the additional names in the POSIX - * namespace created from hard links. - */ -static int preapply_dentry_with_dos_name(struct wim_dentry *dentry, - ntfs_inode **dir_ni_p, - struct apply_args *args) -{ - struct wim_dentry *other; - struct wim_dentry *dentry_with_dos_name; - - dentry_with_dos_name = NULL; - inode_for_each_dentry(other, dentry->d_inode) { - if (other != dentry && (dentry->parent == other->parent) - && other->short_name_len) - { - if (dentry_with_dos_name) { - ERROR("Found multiple DOS names for file `%s' " - "in the same directory", - dentry_with_dos_name->full_path_utf8); - return WIMLIB_ERR_INVALID_DENTRY; - } - dentry_with_dos_name = other; - } - } - /* If there's a dentry with a DOS name, extract it first */ - if (dentry_with_dos_name && !dentry_with_dos_name->is_extracted) { - int ret; - ntfs_volume *vol = (*dir_ni_p)->vol; - - DEBUG("pre-applying DOS name `%s'", - dentry_with_dos_name->full_path_utf8); - ret = do_apply_dentry_ntfs(dentry_with_dos_name, - *dir_ni_p, args); - if (ret != 0) - return ret; - - *dir_ni_p = dentry_open_parent_ni(dentry, vol); - if (!*dir_ni_p) - return WIMLIB_ERR_NTFS_3G; - } - return 0; -} - /* * Applies a WIM dentry to a NTFS filesystem. * @@ -421,37 +371,22 @@ static int do_apply_dentry_ntfs(struct wim_dentry *dentry, ntfs_inode *dir_ni, 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; if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) { type = S_IFDIR; } else { - /* If this dentry is hard-linked to any other dentries in the - * same directory, make sure to apply the one (if any) with a - * DOS name first. Otherwise, NTFS-3g might not assign the file - * names correctly. */ - if (dentry->short_name_len == 0) { - ret = preapply_dentry_with_dos_name(dentry, - &dir_ni, args); - if (ret != 0) - return ret; - } 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); - if (ret == 0) - goto out_set_dos_name; - else - 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 @@ -502,10 +437,8 @@ static int do_apply_dentry_ntfs(struct wim_dentry *dentry, ntfs_inode *dir_ni, goto out_close_dir_ni; } -out_set_dos_name: /* Set DOS (short) name if given */ if (dentry->short_name_len != 0) { - char *short_name_utf8; size_t short_name_utf8_len; ret = utf16_to_utf8(dentry->short_name, @@ -515,20 +448,6 @@ out_set_dos_name: if (ret != 0) goto out_close_dir_ni; - if (!ni) { - /* Hardlink was made; linked inode needs to be looked up - * again. */ - ni = ntfs_pathname_to_inode(vol, dir_ni, - dentry->file_name_utf8); - if (!ni) { - ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'", - dentry->full_path_utf8); - FREE(short_name_utf8); - ret = WIMLIB_ERR_NTFS_3G; - goto out_close_dir_ni; - } - } - DEBUG("Setting short (DOS) name of `%s' to %s", dentry->full_path_utf8, short_name_utf8); @@ -541,9 +460,8 @@ out_set_dos_name: ret = WIMLIB_ERR_NTFS_3G; } /* inodes have been closed by ntfs_set_ntfs_dos_name(). */ - return ret; + goto out; } - out_close_dir_ni: if (dir_ni) { if (ni) { @@ -560,9 +478,8 @@ out_close_dir_ni: ERROR_WITH_ERRNO("Failed to close inode of directory " "containing `%s'", dentry->full_path_utf8); } - } else { - wimlib_assert(ni == NULL); } +out: return ret; } @@ -592,16 +509,86 @@ int apply_dentry_ntfs(struct wim_dentry *dentry, void *arg) struct apply_args *args = arg; ntfs_volume *vol = args->vol; WIMStruct *w = args->w; - ntfs_inode *dir_ni; + struct wim_dentry *orig_dentry; + struct wim_dentry *other; + int ret; + /* Treat the root dentry specially. */ if (dentry_is_root(dentry)) return apply_root_dentry_ntfs(dentry, vol, w); - dir_ni = dentry_open_parent_ni(dentry, vol); - if (dir_ni) - return do_apply_dentry_ntfs(dentry, dir_ni, arg); - else - return WIMLIB_ERR_NTFS_3G; + /* 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. + */ +again: + orig_dentry = NULL; + 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 != 0) { + orig_dentry = dentry; + dentry = other; + break; + } + } + } + dentry->d_inode->i_dos_name_extracted = 1; + ntfs_inode *dir_ni = dentry_open_parent_ni(dentry, vol); + if (dir_ni) { + ret = do_apply_dentry_ntfs(dentry, dir_ni, arg); + if (ret == 0 && orig_dentry != NULL) { + dentry = orig_dentry; + goto again; + } + } else { + ret = WIMLIB_ERR_NTFS_3G; + } + return ret; } /* Transfers the 100-nanosecond precision timestamps from a WIM dentry to a NTFS