@@ -677,12 +677,6 @@ impl<T: Clone + 'static> Qcow2Driver<T> {
677677 break ;
678678 }
679679
680- if let Err ( e) = self . refcount . flush_refcount_block_cache ( ) {
681- err_msg = format ! ( "{:?}" , e) ;
682- error_stage = 5 ;
683- break ;
684- }
685-
686680 let mut new_header = self . header . clone ( ) ;
687681 new_header. snapshots_offset = new_snapshot_table_offset;
688682 new_header. nb_snapshots -= 1 ;
@@ -711,6 +705,7 @@ impl<T: Clone + 'static> Qcow2Driver<T> {
711705 }
712706
713707 // Error handling, to revert some operation.
708+ self . refcount . discard_list . clear ( ) ;
714709 if error_stage >= 6 {
715710 self . refcount . update_refcount (
716711 self . header . snapshots_offset ,
@@ -882,6 +877,7 @@ impl<T: Clone + 'static> Qcow2Driver<T> {
882877 }
883878
884879 // Error handling, to revert some operation.
880+ self . refcount . discard_list . clear ( ) ;
885881 if error_stage >= 5 && self . header . snapshots_offset != 0 {
886882 self . refcount . update_refcount (
887883 self . header . snapshots_offset ,
@@ -1069,7 +1065,7 @@ impl<T: Clone + 'static> Qcow2Driver<T> {
10691065
10701066 let date = get_format_time ( snap. date_sec as i64 ) ;
10711067 let date_str = format ! (
1072- "{:04}-{:02}-{:02} {:02}- {:02}- {:02}" ,
1068+ "{:04}-{:02}-{:02} {:02}: {:02}: {:02}" ,
10731069 date[ 0 ] , date[ 1 ] , date[ 2 ] , date[ 3 ] , date[ 4 ] , date[ 5 ]
10741070 ) ;
10751071
@@ -1334,10 +1330,10 @@ impl<T: Clone + Send + Sync> BlockDriverOps<T> for Qcow2Driver<T> {
13341330 let mut left = iovec. to_vec ( ) ;
13351331 let total = std:: cmp:: min ( nbytes, self . virtual_disk_size ( ) - offset as u64 ) ;
13361332 let mut req_list = Vec :: new ( ) ;
1337- let mut copyed = 0 ;
1338- while copyed < total {
1339- let pos = offset as u64 + copyed ;
1340- let count = self . cluster_aligned_bytes ( pos, total - copyed ) ;
1333+ let mut copied = 0 ;
1334+ while copied < total {
1335+ let pos = offset as u64 + copied ;
1336+ let count = self . cluster_aligned_bytes ( pos, total - copied ) ;
13411337 let ( begin, end) = iovecs_split ( left, count) ;
13421338 left = end;
13431339 if let HostOffset :: DataAddress ( host_offset) = self . host_offset_for_read ( pos) ? {
@@ -1350,7 +1346,7 @@ impl<T: Clone + Send + Sync> BlockDriverOps<T> for Qcow2Driver<T> {
13501346 } else {
13511347 iov_from_buf_direct ( & begin, & vec ! [ 0_u8 ; count as usize ] ) ?;
13521348 }
1353- copyed += count;
1349+ copied += count;
13541350 }
13551351
13561352 self . driver . read_vectored ( req_list, completecb)
@@ -1364,10 +1360,10 @@ impl<T: Clone + Send + Sync> BlockDriverOps<T> for Qcow2Driver<T> {
13641360 let mut left = iovec. to_vec ( ) ;
13651361 let total = std:: cmp:: min ( nbytes, self . virtual_disk_size ( ) - offset as u64 ) ;
13661362 let mut req_list = Vec :: new ( ) ;
1367- let mut copyed = 0 ;
1368- while copyed < total {
1369- let pos = offset as u64 + copyed ;
1370- let count = self . cluster_aligned_bytes ( pos, total - copyed ) ;
1363+ let mut copied = 0 ;
1364+ while copied < total {
1365+ let pos = offset as u64 + copied ;
1366+ let count = self . cluster_aligned_bytes ( pos, total - copied ) ;
13711367 let ( begin, end) = iovecs_split ( left, count) ;
13721368 left = end;
13731369 if let HostOffset :: DataAddress ( host_offset) = self . host_offset_for_write ( pos) ? {
@@ -1377,7 +1373,7 @@ impl<T: Clone + Send + Sync> BlockDriverOps<T> for Qcow2Driver<T> {
13771373 offset : host_offset,
13781374 nbytes,
13791375 } ) ;
1380- copyed += count;
1376+ copied += count;
13811377 }
13821378 }
13831379
@@ -2045,4 +2041,216 @@ mod test {
20452041 assert ! ( qcow2_read( & mut qcow2_driver, & mut test_buf, offset_start) . is_ok( ) ) ;
20462042 assert ! ( vec_is_zero( & test_buf) ) ;
20472043 }
2044+
2045+ #[ test]
2046+ fn test_snapshot_basic ( ) {
2047+ // TODO:
2048+ // 1) add check step when stratovirt-img works.
2049+ // 2) add snapshot apply step to check function.
2050+ let path = "/tmp/snashot_test.qcow2" ;
2051+ let cluster_bits = 16 ;
2052+ let cluster_size = 1 << cluster_bits;
2053+ let ( _, mut qcow2) = create_qcow2 ( path) ;
2054+
2055+ let guest_offsets = [
2056+ cluster_size * 0 ,
2057+ cluster_size * 10 ,
2058+ cluster_size * 100 ,
2059+ cluster_size * 1000 ,
2060+ cluster_size * 10000 ,
2061+ ] ;
2062+
2063+ let wbuf = vec ! [ 1_u8 ; CLUSTER_SIZE as usize ] ;
2064+ // Write data and create snapshot 'snap1'.
2065+ for offset in guest_offsets {
2066+ qcow2_write ( & mut qcow2, & wbuf, offset) . unwrap ( ) ;
2067+ }
2068+ qcow2. qcow2_create_snapshot ( "snap1" . to_string ( ) , 0 ) . unwrap ( ) ;
2069+
2070+ let wbuf = vec ! [ 2_u8 ; CLUSTER_SIZE as usize ] ;
2071+ // Write data and create snapshot 'snap2'.
2072+ for offset in guest_offsets {
2073+ qcow2_write ( & mut qcow2, & wbuf, offset) . unwrap ( ) ;
2074+ }
2075+ qcow2. qcow2_create_snapshot ( "snap2" . to_string ( ) , 0 ) . unwrap ( ) ;
2076+
2077+ // Read 1 byte for checking. Add more checks after implementing snapshot restore.
2078+ let mut rbuf = vec ! [ 0_u8 ; 1 ] ;
2079+ for offset in guest_offsets {
2080+ qcow2_read ( & mut qcow2, & mut rbuf, offset) . unwrap ( ) ;
2081+ assert_eq ! ( rbuf, [ 2 ] ) ;
2082+ }
2083+
2084+ // Delete snapshot 'snap2'.
2085+ qcow2. qcow2_delete_snapshot ( "snap2" . to_string ( ) ) . unwrap ( ) ;
2086+
2087+ // Delete snapshot 'snap1'.
2088+ qcow2. qcow2_delete_snapshot ( "snap1" . to_string ( ) ) . unwrap ( ) ;
2089+ }
2090+
2091+ fn get_host_offset ( qcow2_driver : & mut Qcow2Driver < ( ) > , guest_offset : u64 ) -> u64 {
2092+ let l2_index = qcow2_driver. table . get_l2_table_index ( guest_offset) ;
2093+ // All used l2 table will be cached for it's little data size in these tests.
2094+ let l2_table = qcow2_driver
2095+ . table
2096+ . get_l2_table_cache_entry ( guest_offset)
2097+ . unwrap ( ) ;
2098+ let l2_entry = l2_table
2099+ . borrow_mut ( )
2100+ . get_entry_map ( l2_index as usize )
2101+ . unwrap ( ) ;
2102+ let host_offset = l2_entry & L2_TABLE_OFFSET_MASK ;
2103+
2104+ host_offset
2105+ }
2106+
2107+ // Change snapshot table offset to unaligned address which will lead to error in refcount update process.
2108+ #[ test]
2109+ fn simulate_revert_snapshot_creation ( ) {
2110+ let path = "/tmp/revert_create.qcow2" ;
2111+ let ( _image, mut qcow2_driver) = create_qcow2 ( path) ;
2112+
2113+ // Write some random data.
2114+ let ( case_list, _buf_list) = generate_rw_random_list ( ) ;
2115+ for case in & case_list {
2116+ qcow2_driver
2117+ . write_vectored ( & case. wiovec , case. offset , ( ) )
2118+ . unwrap ( ) ;
2119+ }
2120+
2121+ // Change snapshot table offset to a fake address which is not align to cluster size and
2122+ // it will fail in update_refcount.
2123+ qcow2_driver. header . snapshots_offset = 0x1111 ;
2124+ let result = qcow2_driver. create_snapshot ( "snapshot1" . to_string ( ) , 0 ) ;
2125+ assert ! ( result. is_err( ) ) ;
2126+
2127+ // Check
2128+ // 1) No snapshot.
2129+ assert_eq ! ( qcow2_driver. header. nb_snapshots, 0 ) ;
2130+ // 2) Refcount is right.
2131+ for case in & case_list {
2132+ let host_offset = get_host_offset ( & mut qcow2_driver, case. offset as u64 ) ;
2133+ assert_eq ! ( qcow2_driver. refcount. get_refcount( host_offset) . unwrap( ) , 1 ) ;
2134+ }
2135+ // 3) L1 table refcount is right.
2136+ assert_eq ! (
2137+ qcow2_driver
2138+ . refcount
2139+ . get_refcount( qcow2_driver. header. l1_table_offset)
2140+ . unwrap( ) ,
2141+ 1
2142+ ) ;
2143+ // 4) L2 table refcount is right.
2144+ let mut l1_table = qcow2_driver. table . l1_table . clone ( ) ;
2145+ for l1_entry in l1_table. iter_mut ( ) {
2146+ if * l1_entry == 0 {
2147+ // No l2 table.
2148+ continue ;
2149+ }
2150+ assert_eq ! (
2151+ qcow2_driver
2152+ . refcount
2153+ . get_refcount( * l1_entry & L1_TABLE_OFFSET_MASK )
2154+ . unwrap( ) ,
2155+ 1
2156+ ) ;
2157+ }
2158+ }
2159+
2160+ // Change snapshot table offset to unaligned address which will lead to error in refcount update process.
2161+ #[ test]
2162+ fn simulate_revert_snapshot_deletion ( ) {
2163+ let path = "/tmp/revert_delete.qcow2" ;
2164+ let ( _image, mut qcow2_driver) = create_qcow2 ( path) ;
2165+
2166+ // Write some random data.
2167+ let ( case_list, _buf_list) = generate_rw_random_list ( ) ;
2168+ for case in & case_list {
2169+ qcow2_driver
2170+ . write_vectored ( & case. wiovec , case. offset , ( ) )
2171+ . unwrap ( ) ;
2172+ }
2173+
2174+ // Create two new snapshots.
2175+ qcow2_driver
2176+ . qcow2_create_snapshot ( "snaptest1" . to_string ( ) , 0 )
2177+ . unwrap ( ) ;
2178+ qcow2_driver
2179+ . qcow2_create_snapshot ( "snaptest2" . to_string ( ) , 0 )
2180+ . unwrap ( ) ;
2181+
2182+ // Check.
2183+ // 1) 2 snapshots: snaptest1, snaptest2.
2184+ assert_eq ! ( qcow2_driver. header. nb_snapshots, 2 ) ;
2185+ assert_eq ! ( qcow2_driver. snapshot. snapshots[ 0 ] . name, "snaptest1" ) ;
2186+ assert_eq ! ( qcow2_driver. snapshot. snapshots[ 1 ] . name, "snaptest2" ) ;
2187+ // 2) Data cluster refcount is right.
2188+ for case in & case_list {
2189+ let host_offset = get_host_offset ( & mut qcow2_driver, case. offset as u64 ) ;
2190+ assert_eq ! ( qcow2_driver. refcount. get_refcount( host_offset) . unwrap( ) , 3 ) ;
2191+ }
2192+ // 3) L1 table refcount is right.
2193+ assert_eq ! (
2194+ qcow2_driver
2195+ . refcount
2196+ . get_refcount( qcow2_driver. header. l1_table_offset)
2197+ . unwrap( ) ,
2198+ 1
2199+ ) ;
2200+ // 4) L2 table refcount is right.
2201+ let mut l1_table = qcow2_driver. table . l1_table . clone ( ) ;
2202+ for l1_entry in l1_table. iter_mut ( ) {
2203+ if * l1_entry == 0 {
2204+ // No l2 table.
2205+ continue ;
2206+ }
2207+ assert_eq ! (
2208+ qcow2_driver
2209+ . refcount
2210+ . get_refcount( * l1_entry & L1_TABLE_OFFSET_MASK )
2211+ . unwrap( ) ,
2212+ 3
2213+ ) ;
2214+ }
2215+
2216+ // Change snapshot table offset to a fake address which is not align to cluster size and
2217+ // it will fail in update_refcount.
2218+ qcow2_driver. header . snapshots_offset = 0x1111 ;
2219+ let result = qcow2_driver. delete_snapshot ( "snapshot1" . to_string ( ) ) ;
2220+ assert ! ( result. is_err( ) ) ;
2221+
2222+ // Check again.
2223+ // 1) 2 snapshots: snaptest1, snaptest2.
2224+ assert_eq ! ( qcow2_driver. header. nb_snapshots, 2 ) ;
2225+ assert_eq ! ( qcow2_driver. snapshot. snapshots[ 0 ] . name, "snaptest1" ) ;
2226+ assert_eq ! ( qcow2_driver. snapshot. snapshots[ 1 ] . name, "snaptest2" ) ;
2227+ // 2) Data cluster refcount is right.
2228+ for case in & case_list {
2229+ let host_offset = get_host_offset ( & mut qcow2_driver, case. offset as u64 ) ;
2230+ assert_eq ! ( qcow2_driver. refcount. get_refcount( host_offset) . unwrap( ) , 3 ) ;
2231+ }
2232+ // 3) L1 table refcount is right.
2233+ assert_eq ! (
2234+ qcow2_driver
2235+ . refcount
2236+ . get_refcount( qcow2_driver. header. l1_table_offset)
2237+ . unwrap( ) ,
2238+ 1
2239+ ) ;
2240+ // 4) L2 table refcount is right.
2241+ let mut l1_table = qcow2_driver. table . l1_table . clone ( ) ;
2242+ for l1_entry in l1_table. iter_mut ( ) {
2243+ if * l1_entry == 0 {
2244+ // No l2 table.
2245+ continue ;
2246+ }
2247+ assert_eq ! (
2248+ qcow2_driver
2249+ . refcount
2250+ . get_refcount( * l1_entry & L1_TABLE_OFFSET_MASK )
2251+ . unwrap( ) ,
2252+ 3
2253+ ) ;
2254+ }
2255+ }
20482256}
0 commit comments