pub fn from(mut file: RawFile) -> Result<QcowFile> {
        let header = QcowHeader::new(&mut file)?;

        // Only v2 and v3 files are supported.
        if header.version != 2 && header.version != 3 {
            return Err(Error::UnsupportedVersion(header.version));
        }

        // Make sure that the L1 table fits in RAM.
        if u64::from(header.l1_size) > MAX_RAM_POINTER_TABLE_SIZE {
            return Err(Error::InvalidL1TableSize(header.l1_size));
        }

        let cluster_bits: u32 = header.cluster_bits;
        if !(MIN_CLUSTER_BITS..=MAX_CLUSTER_BITS).contains(&cluster_bits) {
            return Err(Error::InvalidClusterSize);
        }
        let cluster_size = 0x01u64 << cluster_bits;

        // Limit the total size of the disk.
        if header.size > MAX_QCOW_FILE_SIZE {
            return Err(Error::FileTooBig(header.size));
        }

        // No current support for backing files.
        if header.backing_file_offset != 0 {
            return Err(Error::BackingFilesNotSupported);
        }

        // Only support two byte refcounts.
        let refcount_bits: u64 = 0x01u64
            .checked_shl(header.refcount_order)
            .ok_or(Error::UnsupportedRefcountOrder)?;
        if refcount_bits != 16 {
            return Err(Error::UnsupportedRefcountOrder);
        }
        let refcount_bytes = (refcount_bits + 7) / 8;

        // Need at least one refcount cluster
        if header.refcount_table_clusters == 0 {
            return Err(Error::NoRefcountClusters);
        }
        offset_is_cluster_boundary(header.backing_file_offset, header.cluster_bits)?;
        offset_is_cluster_boundary(header.l1_table_offset, header.cluster_bits)?;
        offset_is_cluster_boundary(header.snapshots_offset, header.cluster_bits)?;
        // refcount table must be a cluster boundary, and within the file's virtual or actual size.
        offset_is_cluster_boundary(header.refcount_table_offset, header.cluster_bits)?;
        let file_size = file.metadata().map_err(Error::GettingFileSize)?.len();
        if header.refcount_table_offset > max(file_size, header.size) {
            return Err(Error::RefcountTableOffEnd);
        }

        // The first cluster should always have a non-zero refcount, so if it is 0,
        // this is an old file with broken refcounts, which requires a rebuild.
        let mut refcount_rebuild_required = true;
        file.seek(SeekFrom::Start(header.refcount_table_offset))
            .map_err(Error::SeekingFile)?;
        let first_refblock_addr = file.read_u64::<BigEndian>().map_err(Error::ReadingHeader)?;
        if first_refblock_addr != 0 {
            file.seek(SeekFrom::Start(first_refblock_addr))
                .map_err(Error::SeekingFile)?;
            let first_cluster_refcount =
                file.read_u16::<BigEndian>().map_err(Error::ReadingHeader)?;
            if first_cluster_refcount != 0 {
                refcount_rebuild_required = false;
            }
        }

        if (header.compatible_features & COMPATIBLE_FEATURES_LAZY_REFCOUNTS) != 0 {
            refcount_rebuild_required = true;
        }

        let mut raw_file =
            QcowRawFile::from(file, cluster_size).ok_or(Error::InvalidClusterSize)?;
        if refcount_rebuild_required {
            QcowFile::rebuild_refcounts(&mut raw_file, header)?;
        }

        let l2_size = cluster_size / size_of::<u64>() as u64;
        let num_clusters = div_round_up_u64(header.size, cluster_size);
        let num_l2_clusters = div_round_up_u64(num_clusters, l2_size);
        let l1_clusters = div_round_up_u64(num_l2_clusters, cluster_size);
        let header_clusters = div_round_up_u64(size_of::<QcowHeader>() as u64, cluster_size);
        if num_l2_clusters > MAX_RAM_POINTER_TABLE_SIZE {
            return Err(Error::TooManyL1Entries(num_l2_clusters));
        }
        let l1_table = VecCache::from_vec(
            raw_file
                .read_pointer_table(
                    header.l1_table_offset,
                    num_l2_clusters,
                    Some(L1_TABLE_OFFSET_MASK),
                )
                .map_err(Error::ReadingHeader)?,
        );

        let num_clusters = div_round_up_u64(header.size, cluster_size);
        let refcount_clusters = max_refcount_clusters(
            header.refcount_order,
            cluster_size as u32,
            (num_clusters + l1_clusters + num_l2_clusters + header_clusters) as u32,
        );
        // Check that the given header doesn't have a suspiciously sized refcount table.
        if u64::from(header.refcount_table_clusters) > 2 * refcount_clusters {
            return Err(Error::RefcountTableTooLarge);
        }
        if l1_clusters + refcount_clusters > MAX_RAM_POINTER_TABLE_SIZE {
            return Err(Error::TooManyRefcounts(refcount_clusters));
        }
        let refcount_block_entries = cluster_size / refcount_bytes;
        let refcounts = RefCount::new(
            &mut raw_file,
            header.refcount_table_offset,
            refcount_clusters,
            refcount_block_entries,
            cluster_size,
        )
        .map_err(Error::ReadingRefCounts)?;

        let l2_entries = cluster_size / size_of::<u64>() as u64;

        let mut qcow = QcowFile {
            raw_file,
            header,
            l1_table,
            l2_entries,
            l2_cache: CacheMap::new(100),
            refcounts,
            current_offset: 0,
            unref_clusters: Vec::new(),
            avail_clusters: Vec::new(),
        };

        // Check that the L1 and refcount tables fit in a 64bit address space.
        qcow.header
            .l1_table_offset
            .checked_add(qcow.l1_address_offset(qcow.virtual_size()))
            .ok_or(Error::InvalidL1TableOffset)?;
        qcow.header
            .refcount_table_offset
            .checked_add(u64::from(qcow.header.refcount_table_clusters) * cluster_size)
            .ok_or(Error::InvalidRefcountTableOffset)?;

        qcow.find_avail_clusters()?;

        Ok(qcow)
    }
/// Creates a QcowFile from `file`. File must be a valid qcow2 image.
Creates a QcowFile from `file`. File must be a valid qcow2 image.
Creates a QcowFile from ` file ` . File must be a valid qcow2 image .
pub fn from(mut file: RawFile) -> Result<QcowFile> {
        let header = QcowHeader::new(&mut file)?;
        // Only v2 and v3 files are supported.
        if header.version != 2 && header.version != 3 {
            return Err(Error::UnsupportedVersion(header.version));
        }
        // Make sure that the L1 table fits in RAM.
        if u64::from(header.l1_size) > MAX_RAM_POINTER_TABLE_SIZE {
            return Err(Error::InvalidL1TableSize(header.l1_size));
        }
        let cluster_bits: u32 = header.cluster_bits;
        if !(MIN_CLUSTER_BITS..=MAX_CLUSTER_BITS).contains(&cluster_bits) {
            return Err(Error::InvalidClusterSize);
        }
        let cluster_size = 0x01u64 << cluster_bits;
        // Limit the total size of the disk.
        if header.size > MAX_QCOW_FILE_SIZE {
            return Err(Error::FileTooBig(header.size));
        }
        // No current support for backing files.
        if header.backing_file_offset != 0 {
            return Err(Error::BackingFilesNotSupported);
        }
        // Only support two byte refcounts.
        let refcount_bits: u64 = 0x01u64
            .checked_shl(header.refcount_order)
            .ok_or(Error::UnsupportedRefcountOrder)?;
        if refcount_bits != 16 {
            return Err(Error::UnsupportedRefcountOrder);
        }
        let refcount_bytes = (refcount_bits + 7) / 8;
        // Need at least one refcount cluster
        if header.refcount_table_clusters == 0 {
            return Err(Error::NoRefcountClusters);
        }
        offset_is_cluster_boundary(header.backing_file_offset, header.cluster_bits)?;
        offset_is_cluster_boundary(header.l1_table_offset, header.cluster_bits)?;
        offset_is_cluster_boundary(header.snapshots_offset, header.cluster_bits)?;
        // refcount table must be a cluster boundary, and within the file's virtual or actual size.
        offset_is_cluster_boundary(header.refcount_table_offset, header.cluster_bits)?;
        let file_size = file.metadata().map_err(Error::GettingFileSize)?.len();
        if header.refcount_table_offset > max(file_size, header.size) {
            return Err(Error::RefcountTableOffEnd);
        }
        // The first cluster should always have a non-zero refcount, so if it is 0,
        // this is an old file with broken refcounts, which requires a rebuild.
        let mut refcount_rebuild_required = true;
        file.seek(SeekFrom::Start(header.refcount_table_offset))
            .map_err(Error::SeekingFile)?;
        let first_refblock_addr = file.read_u64::<BigEndian>().map_err(Error::ReadingHeader)?;
        if first_refblock_addr != 0 {
            file.seek(SeekFrom::Start(first_refblock_addr))
                .map_err(Error::SeekingFile)?;
            let first_cluster_refcount =
                file.read_u16::<BigEndian>().map_err(Error::ReadingHeader)?;
            if first_cluster_refcount != 0 {
                refcount_rebuild_required = false;
            }
        }
        if (header.compatible_features & COMPATIBLE_FEATURES_LAZY_REFCOUNTS) != 0 {
            refcount_rebuild_required = true;
        }
        let mut raw_file =
            QcowRawFile::from(file, cluster_size).ok_or(Error::InvalidClusterSize)?;
        if refcount_rebuild_required {
            QcowFile::rebuild_refcounts(&mut raw_file, header)?;
        }
        let l2_size = cluster_size / size_of::<u64>() as u64;
        let num_clusters = div_round_up_u64(header.size, cluster_size);
        let num_l2_clusters = div_round_up_u64(num_clusters, l2_size);
        let l1_clusters = div_round_up_u64(num_l2_clusters, cluster_size);
        let header_clusters = div_round_up_u64(size_of::<QcowHeader>() as u64, cluster_size);
        if num_l2_clusters > MAX_RAM_POINTER_TABLE_SIZE {
            return Err(Error::TooManyL1Entries(num_l2_clusters));
        }
        let l1_table = VecCache::from_vec(
            raw_file
                .read_pointer_table(
                    header.l1_table_offset,
                    num_l2_clusters,
                    Some(L1_TABLE_OFFSET_MASK),
                )
                .map_err(Error::ReadingHeader)?,
        );
        let num_clusters = div_round_up_u64(header.size, cluster_size);
        let refcount_clusters = max_refcount_clusters(
            header.refcount_order,
            cluster_size as u32,
            (num_clusters + l1_clusters + num_l2_clusters + header_clusters) as u32,
        );
        // Check that the given header doesn't have a suspiciously sized refcount table.
        if u64::from(header.refcount_table_clusters) > 2 * refcount_clusters {
            return Err(Error::RefcountTableTooLarge);
        }
        if l1_clusters + refcount_clusters > MAX_RAM_POINTER_TABLE_SIZE {
            return Err(Error::TooManyRefcounts(refcount_clusters));
        }
        let refcount_block_entries = cluster_size / refcount_bytes;
        let refcounts = RefCount::new(
            &mut raw_file,
            header.refcount_table_offset,
            refcount_clusters,
            refcount_block_entries,
            cluster_size,
        )
        .map_err(Error::ReadingRefCounts)?;
        let l2_entries = cluster_size / size_of::<u64>() as u64;
        let mut qcow = QcowFile {
            raw_file,
            header,
            l1_table,
            l2_entries,
            l2_cache: CacheMap::new(100),
            refcounts,
            current_offset: 0,
            unref_clusters: Vec::new(),
            avail_clusters: Vec::new(),
        };
        // Check that the L1 and refcount tables fit in a 64bit address space.
        qcow.header
            .l1_table_offset
            .checked_add(qcow.l1_address_offset(qcow.virtual_size()))
            .ok_or(Error::InvalidL1TableOffset)?;
        qcow.header
            .refcount_table_offset
            .checked_add(u64::from(qcow.header.refcount_table_clusters) * cluster_size)
            .ok_or(Error::InvalidRefcountTableOffset)?;
        qcow.find_avail_clusters()?;
        Ok(qcow)
    }
pub fn from ( mut file : RawFile ) -> Result < QcowFile > { let header = QcowHeader :: new ( & mut file ) ? ; // Only v2 and v3 files are supported. if header . version != 2 && header . version != 3 { return Err ( Error :: UnsupportedVersion ( header . version ) ) ; } // Make sure that the L1 table fits in RAM. if u64 :: from ( header . l1_size ) > MAX_RAM_POINTER_TABLE_SIZE { return Err ( Error :: InvalidL1TableSize ( header . l1_size ) ) ; } let cluster_bits : u32 = header . cluster_bits ; if ! ( MIN_CLUSTER_BITS ..= MAX_CLUSTER_BITS ) . contains ( & cluster_bits ) { return Err ( Error :: InvalidClusterSize ) ; } let cluster_size = 0x01u64 << cluster_bits ; // Limit the total size of the disk. if header . size > MAX_QCOW_FILE_SIZE { return Err ( Error :: FileTooBig ( header . size ) ) ; } // No current support for backing files. if header . backing_file_offset != 0 { return Err ( Error :: BackingFilesNotSupported ) ; } // Only support two byte refcounts. let refcount_bits : u64 = 0x01u64 . checked_shl ( header . refcount_order ) . ok_or ( Error :: UnsupportedRefcountOrder ) ? ; if refcount_bits != 16 { return Err ( Error :: UnsupportedRefcountOrder ) ; } let refcount_bytes = ( refcount_bits + 7 ) / 8 ; // Need at least one refcount cluster if header . refcount_table_clusters == 0 { return Err ( Error :: NoRefcountClusters ) ; } offset_is_cluster_boundary ( header . backing_file_offset , header . cluster_bits ) ? ; offset_is_cluster_boundary ( header . l1_table_offset , header . cluster_bits ) ? ; offset_is_cluster_boundary ( header . snapshots_offset , header . cluster_bits ) ? ; // refcount table must be a cluster boundary, and within the file's virtual or actual size. offset_is_cluster_boundary ( header . refcount_table_offset , header . cluster_bits ) ? ; let file_size = file . metadata ( ) . map_err ( Error :: GettingFileSize ) ? . len ( ) ; if header . refcount_table_offset > max ( file_size , header . size ) { return Err ( Error :: RefcountTableOffEnd ) ; } // The first cluster should always have a non-zero refcount, so if it is 0, // this is an old file with broken refcounts, which requires a rebuild. let mut refcount_rebuild_required = true ; file . seek ( SeekFrom :: Start ( header . refcount_table_offset ) ) . map_err ( Error :: SeekingFile ) ? ; let first_refblock_addr = file . read_u64 :: < BigEndian > ( ) . map_err ( Error :: ReadingHeader ) ? ; if first_refblock_addr != 0 { file . seek ( SeekFrom :: Start ( first_refblock_addr ) ) . map_err ( Error :: SeekingFile ) ? ; let first_cluster_refcount = file . read_u16 :: < BigEndian > ( ) . map_err ( Error :: ReadingHeader ) ? ; if first_cluster_refcount != 0 { refcount_rebuild_required = false ; } } if ( header . compatible_features & COMPATIBLE_FEATURES_LAZY_REFCOUNTS ) != 0 { refcount_rebuild_required = true ; } let mut raw_file = QcowRawFile :: from ( file , cluster_size ) . ok_or ( Error :: InvalidClusterSize ) ? ; if refcount_rebuild_required { QcowFile :: rebuild_refcounts ( & mut raw_file , header ) ? ; } let l2_size = cluster_size / size_of :: < u64 > ( ) as u64 ; let num_clusters = div_round_up_u64 ( header . size , cluster_size ) ; let num_l2_clusters = div_round_up_u64 ( num_clusters , l2_size ) ; let l1_clusters = div_round_up_u64 ( num_l2_clusters , cluster_size ) ; let header_clusters = div_round_up_u64 ( size_of :: < QcowHeader > ( ) as u64 , cluster_size ) ; if num_l2_clusters > MAX_RAM_POINTER_TABLE_SIZE { return Err ( Error :: TooManyL1Entries ( num_l2_clusters ) ) ; } let l1_table = VecCache :: from_vec ( raw_file . read_pointer_table ( header . l1_table_offset , num_l2_clusters , Some ( L1_TABLE_OFFSET_MASK ) , ) . map_err ( Error :: ReadingHeader ) ? , ) ; let num_clusters = div_round_up_u64 ( header . size , cluster_size ) ; let refcount_clusters = max_refcount_clusters ( header . refcount_order , cluster_size as u32 , ( num_clusters + l1_clusters + num_l2_clusters + header_clusters ) as u32 , ) ; // Check that the given header doesn't have a suspiciously sized refcount table. if u64 :: from ( header . refcount_table_clusters ) > 2 * refcount_clusters { return Err ( Error :: RefcountTableTooLarge ) ; } if l1_clusters + refcount_clusters > MAX_RAM_POINTER_TABLE_SIZE { return Err ( Error :: TooManyRefcounts ( refcount_clusters ) ) ; } let refcount_block_entries = cluster_size / refcount_bytes ; let refcounts = RefCount :: new ( & mut raw_file , header . refcount_table_offset , refcount_clusters , refcount_block_entries , cluster_size , ) . map_err ( Error :: ReadingRefCounts ) ? ; let l2_entries = cluster_size / size_of :: < u64 > ( ) as u64 ; let mut qcow = QcowFile { raw_file , header , l1_table , l2_entries , l2_cache : CacheMap :: new ( 100 ) , refcounts , current_offset : 0 , unref_clusters : Vec :: new ( ) , avail_clusters : Vec :: new ( ) , } ; // Check that the L1 and refcount tables fit in a 64bit address space. qcow . header . l1_table_offset . checked_add ( qcow . l1_address_offset ( qcow . virtual_size ( ) ) ) . ok_or ( Error :: InvalidL1TableOffset ) ? ; qcow . header . refcount_table_offset . checked_add ( u64 :: from ( qcow . header . refcount_table_clusters ) * cluster_size ) . ok_or ( Error :: InvalidRefcountTableOffset ) ? ; qcow . find_avail_clusters ( ) ? ; Ok ( qcow ) }
Creates a QcowFile from `file`.
Creates a QcowFile from ` file ` .
file
RawFile
file
RawFile