/* Create empty named data streams. * * Since these won't have 'struct wim_lookup_table_entry's, they won't show up * in the call to extract_stream_list(). Hence the need for the special case. */ static int ntfs_3g_create_any_empty_ads(ntfs_inode *ni, const struct wim_inode *inode, const struct ntfs_3g_apply_ctx *ctx) { for (u16 i = 0; i < inode->i_num_ads; i++) { const struct wim_ads_entry *entry; entry = &inode->i_ads_entries[i]; /* Not named? */ if (!entry->stream_name_nbytes) continue; /* Not empty? */ if (entry->lte) continue; if (ntfs_attr_add(ni, AT_DATA, entry->stream_name, entry->stream_name_nbytes / sizeof(utf16lechar), NULL, 0)) { ERROR_WITH_ERRNO("Failed to create named data stream " "of \"%s\"", dentry_full_path( inode_first_extraction_dentry(inode))); return WIMLIB_ERR_NTFS_3G; } } return 0; }
static int calculate_chunk_sha1(struct filedes *in_fd, size_t this_chunk_size, off_t offset, u8 sha1_md[]) { u8 buf[BUFFER_SIZE]; SHA_CTX ctx; size_t bytes_remaining; size_t bytes_to_read; int ret; bytes_remaining = this_chunk_size; sha1_init(&ctx); do { bytes_to_read = min(bytes_remaining, sizeof(buf)); ret = full_pread(in_fd, buf, bytes_to_read, offset); if (ret) { ERROR_WITH_ERRNO("Read error while calculating " "integrity checksums"); return ret; } sha1_update(&ctx, buf, bytes_to_read); bytes_remaining -= bytes_to_read; offset += bytes_to_read; } while (bytes_remaining); sha1_final(sha1_md, &ctx); return 0; }
/* Recursively creates all the subdirectories of @dir, which has been created as * the NTFS inode @dir_ni. */ static int ntfs_3g_create_dirs_recursive(ntfs_inode *dir_ni, struct wim_dentry *dir, struct ntfs_3g_apply_ctx *ctx) { struct wim_dentry *child; for_dentry_child(child, dir) { ntfs_inode *ni; int ret; if (!(child->d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)) continue; if (!will_extract_dentry(child)) continue; ni = ntfs_create(dir_ni, 0, child->d_extraction_name, child->d_extraction_name_nchars, S_IFDIR); if (!ni) { ERROR_WITH_ERRNO("Error creating \"%s\" in NTFS volume", dentry_full_path(child)); return WIMLIB_ERR_NTFS_3G; } child->d_inode->i_mft_no = ni->mft_no; ret = report_file_created(&ctx->common); if (!ret) ret = ntfs_3g_set_metadata(ni, child->d_inode, ctx); if (!ret) ret = ntfs_3g_create_any_empty_ads(ni, child->d_inode, ctx); if (!ret) ret = ntfs_3g_create_dirs_recursive(ni, child, ctx); if (ntfs_inode_close_in_dir(ni, dir_ni) && !ret) { ERROR_WITH_ERRNO("Error closing \"%s\" in NTFS volume", dentry_full_path(child)); ret = WIMLIB_ERR_NTFS_3G; } if (ret) return ret; }
/* Restore the DOS name of the @dentry. * This closes both @ni and @dir_ni. * If either is NULL, then they are opened temporarily. */ static int ntfs_3g_restore_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, struct wim_dentry *dentry, ntfs_volume *vol) { int ret; const char *dos_name; size_t dos_name_nbytes; /* Note: ntfs_set_ntfs_dos_name() closes both inodes (even if it fails). * And it takes in a multibyte string, even though it translates it to * UTF-16LE internally... which is annoying because we currently have * the UTF-16LE string but not the multibyte string. */ ret = utf16le_get_tstr(dentry->short_name, dentry->short_name_nbytes, &dos_name, &dos_name_nbytes); if (ret) goto out_close; if (!dir_ni) dir_ni = ntfs_inode_open(vol, dentry->d_parent->d_inode->i_mft_no); if (!ni) ni = ntfs_inode_open(vol, dentry->d_inode->i_mft_no); if (dir_ni && ni) { ret = ntfs_set_ntfs_dos_name(ni, dir_ni, dos_name, dos_name_nbytes, 0); dir_ni = NULL; ni = NULL; } else { ret = -1; } utf16le_put_tstr(dos_name); if (ret) { ERROR_WITH_ERRNO("Failed to set DOS name of \"%s\" in NTFS " "volume", dentry_full_path(dentry)); ret = WIMLIB_ERR_SET_SHORT_NAME; goto out_close; } /* Unlike most other NTFS-3g functions, ntfs_set_ntfs_dos_name() * changes the directory's last modification timestamp... * Change it back. */ return ntfs_3g_restore_timestamps(vol, dentry->d_parent->d_inode); out_close: /* ntfs_inode_close() can take a NULL argument, but it's probably best * not to rely on this behavior. */ if (ni) ntfs_inode_close(ni); if (dir_ni) ntfs_inode_close(dir_ni); return ret; }
static int open_wim_file(const tchar *filename, struct filedes *fd_ret) { int raw_fd; raw_fd = topen(filename, O_RDONLY | O_BINARY); if (raw_fd < 0) { ERROR_WITH_ERRNO("Can't open \"%"TS"\" read-only", filename); return WIMLIB_ERR_OPEN; } filedes_init(fd_ret, raw_fd); return 0; }
/* * can_modify_wim - Check if a given WIM is writeable. This is only the case if * it meets the following three conditions: * * 1. Write access is allowed to the underlying file (if any) at the filesystem level. * 2. The WIM is not part of a spanned set. * 3. The WIM_HDR_FLAG_READONLY flag is not set in the WIM header. * * Return value is 0 if writable; WIMLIB_ERR_WIM_IS_READONLY otherwise. */ int can_modify_wim(WIMStruct *wim) { if (wim->filename) { if (taccess(wim->filename, W_OK)) { ERROR_WITH_ERRNO("Can't modify \"%"TS"\"", wim->filename); return WIMLIB_ERR_WIM_IS_READONLY; } } if (wim->hdr.total_parts != 1) { ERROR("Cannot modify \"%"TS"\": is part of a split WIM", wim->filename); return WIMLIB_ERR_WIM_IS_READONLY; } if (wim->hdr.flags & WIM_HDR_FLAG_READONLY) { ERROR("Cannot modify \"%"TS"\": is marked read-only", wim->filename); return WIMLIB_ERR_WIM_IS_READONLY; } return 0; }
static int execute_rename_command(struct update_command_journal *j, WIMStruct *wim, const struct wimlib_update_command *rename_cmd) { int ret; ret = rename_wim_path(wim, rename_cmd->rename.wim_source_path, rename_cmd->rename.wim_target_path, WIMLIB_CASE_PLATFORM_DEFAULT, j); if (ret) { ret = -ret; errno = ret; ERROR_WITH_ERRNO("Can't rename \"%"TS"\" to \"%"TS"\"", rename_cmd->rename.wim_source_path, rename_cmd->rename.wim_target_path); switch (ret) { case ENOMEM: ret = WIMLIB_ERR_NOMEM; break; case ENOTDIR: ret = WIMLIB_ERR_NOTDIR; break; case ENOTEMPTY: case EBUSY: /* XXX: EBUSY is returned when the rename would create a * loop. It maybe should have its own error code. */ ret = WIMLIB_ERR_NOTEMPTY; break; case EISDIR: ret = WIMLIB_ERR_IS_DIRECTORY; break; case ENOENT: default: ret = WIMLIB_ERR_PATH_DOES_NOT_EXIST; break; } } return ret; }
/* Restore the timestamps on the NTFS inode corresponding to @inode. */ static int ntfs_3g_restore_timestamps(ntfs_volume *vol, const struct wim_inode *inode) { ntfs_inode *ni; int res; ni = ntfs_inode_open(vol, inode->i_mft_no); if (!ni) goto fail; res = ntfs_3g_set_timestamps(ni, inode); if (ntfs_inode_close(ni) || res) goto fail; return 0; fail: ERROR_WITH_ERRNO("Failed to update timestamps of \"%s\" in NTFS volume", dentry_full_path(inode_first_extraction_dentry(inode))); return WIMLIB_ERR_SET_TIMESTAMPS; }
/* * Begins the reading of a WIM file; opens the file and reads its header and * blob table, and optionally checks the integrity. */ static int begin_read(WIMStruct *wim, const void *wim_filename_or_fd, int open_flags) { int ret; int xml_num_images; const tchar *wimfile; if (open_flags & WIMLIB_OPEN_FLAG_FROM_PIPE) { wimfile = NULL; filedes_init(&wim->in_fd, *(const int*)wim_filename_or_fd); wim->in_fd.is_pipe = 1; } else { wimfile = wim_filename_or_fd; ret = open_wim_file(wimfile, &wim->in_fd); if (ret) return ret; /* The absolute path to the WIM is requested so that * wimlib_overwrite() still works even if the process changes * its working directory. This actually happens if a WIM is * mounted read-write, since the FUSE thread changes directory * to "/", and it needs to be able to find the WIM file again. * * This will break if the full path to the WIM changes in the * intervening time... * * Warning: in Windows native builds, realpath() calls the * replacement function in win32_replacements.c. */ wim->filename = realpath(wimfile, NULL); if (!wim->filename) { ERROR_WITH_ERRNO("Failed to get full path to file " "\"%"TS"\"", wimfile); if (errno == ENOMEM) return WIMLIB_ERR_NOMEM; else return WIMLIB_ERR_NO_FILENAME; } } ret = read_wim_header(wim, &wim->hdr); if (ret) return ret; if (wim->hdr.flags & WIM_HDR_FLAG_WRITE_IN_PROGRESS) { WARNING("The WIM_HDR_FLAG_WRITE_IN_PROGRESS flag is set in the header of\n" " \"%"TS"\". It may be being changed by another process,\n" " or a process may have crashed while writing the WIM.", wimfile); } if (open_flags & WIMLIB_OPEN_FLAG_WRITE_ACCESS) { ret = can_modify_wim(wim); if (ret) return ret; } if ((open_flags & WIMLIB_OPEN_FLAG_ERROR_IF_SPLIT) && (wim->hdr.total_parts != 1)) return WIMLIB_ERR_IS_SPLIT_WIM; /* If the boot index is invalid, print a warning and set it to 0 */ if (wim->hdr.boot_idx > wim->hdr.image_count) { WARNING("Ignoring invalid boot index."); wim->hdr.boot_idx = 0; } /* Check and cache the compression type */ if (wim->hdr.flags & WIM_HDR_FLAG_COMPRESSION) { if (wim->hdr.flags & WIM_HDR_FLAG_COMPRESS_LZX) { wim->compression_type = WIMLIB_COMPRESSION_TYPE_LZX; } else if (wim->hdr.flags & (WIM_HDR_FLAG_COMPRESS_XPRESS | WIM_HDR_FLAG_COMPRESS_XPRESS_2)) { wim->compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS; } else if (wim->hdr.flags & WIM_HDR_FLAG_COMPRESS_LZMS) { wim->compression_type = WIMLIB_COMPRESSION_TYPE_LZMS; } else { return WIMLIB_ERR_INVALID_COMPRESSION_TYPE; } } else { wim->compression_type = WIMLIB_COMPRESSION_TYPE_NONE; } wim->out_compression_type = wim->compression_type; /* Check and cache the chunk size. */ wim->chunk_size = wim->hdr.chunk_size; wim->out_chunk_size = wim->chunk_size; if (!wim_chunk_size_valid(wim->chunk_size, wim->compression_type)) { ERROR("Invalid chunk size (%"PRIu32" bytes) " "for compression type %"TS"!", wim->chunk_size, wimlib_get_compression_type_string(wim->compression_type)); return WIMLIB_ERR_INVALID_CHUNK_SIZE; } if (open_flags & WIMLIB_OPEN_FLAG_CHECK_INTEGRITY) { ret = check_wim_integrity(wim); if (ret == WIM_INTEGRITY_NONEXISTENT) { WARNING("\"%"TS"\" does not contain integrity " "information. Skipping integrity check.", wimfile); } else if (ret == WIM_INTEGRITY_NOT_OK) { return WIMLIB_ERR_INTEGRITY; } else if (ret != WIM_INTEGRITY_OK) { return ret; } } if (wim->hdr.image_count != 0 && wim->hdr.part_number == 1) { wim->image_metadata = new_image_metadata_array(wim->hdr.image_count); if (!wim->image_metadata) return WIMLIB_ERR_NOMEM; } if (open_flags & WIMLIB_OPEN_FLAG_FROM_PIPE) { wim->blob_table = new_blob_table(9001); if (!wim->blob_table) return WIMLIB_ERR_NOMEM; } else { ret = read_wim_xml_data(wim); if (ret) return ret; xml_num_images = wim_info_get_num_images(wim->wim_info); if (xml_num_images != wim->hdr.image_count) { ERROR("The WIM's header is inconsistent with its XML data.\n" " Please submit a bug report if you believe this " "WIM file should be considered valid."); return WIMLIB_ERR_IMAGE_COUNT; } ret = read_blob_table(wim); if (ret) return ret; } return 0; }
/* Set attributes, security descriptor, and timestamps on the NTFS inode @ni. */ static int ntfs_3g_set_metadata(ntfs_inode *ni, const struct wim_inode *inode, const struct ntfs_3g_apply_ctx *ctx) { int extract_flags; const struct wim_security_data *sd; struct wim_dentry *one_dentry; int ret; extract_flags = ctx->common.extract_flags; sd = wim_get_current_security_data(ctx->common.wim); one_dentry = inode_first_extraction_dentry(inode); /* Attributes */ if (!(extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES)) { u32 attrib = inode->i_attributes; attrib &= ~(FILE_ATTRIBUTE_SPARSE_FILE | FILE_ATTRIBUTE_ENCRYPTED); if (ntfs_set_ntfs_attrib(ni, (const char *)&attrib, sizeof(attrib), 0)) { ERROR_WITH_ERRNO("Failed to set attributes on \"%s\" " "in NTFS volume", dentry_full_path(one_dentry)); return WIMLIB_ERR_SET_ATTRIBUTES; } } /* Security descriptor */ if ((inode->i_security_id >= 0) && !(extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS)) { const void *desc; size_t desc_size; desc = sd->descriptors[inode->i_security_id]; desc_size = sd->sizes[inode->i_security_id]; ret = ntfs_3g_set_security_descriptor(ni, desc, desc_size); if (ret) { if (wimlib_print_errors) { ERROR_WITH_ERRNO("Failed to set security descriptor " "on \"%s\" in NTFS volume", dentry_full_path(one_dentry)); fprintf(wimlib_error_file, "The security descriptor is: "); print_byte_field(desc, desc_size, wimlib_error_file); fprintf(wimlib_error_file, "\n"); } return ret; } } /* Timestamps */ ret = ntfs_3g_set_timestamps(ni, inode); if (ret) { ERROR_WITH_ERRNO("Failed to set timestamps on \"%s\" " "in NTFS volume", dentry_full_path(one_dentry)); return ret; } return 0; }