Beispiel #1
0
/* 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;
}
Beispiel #2
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;
}
Beispiel #3
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;
	}
Beispiel #4
0
/* 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;
}
Beispiel #5
0
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;
}
Beispiel #6
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;
}
Beispiel #7
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;
}
Beispiel #8
0
/* 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;
}
Beispiel #9
0
/*
 * 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;
}
Beispiel #10
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;
}