@@ -168,6 +168,40 @@ let test_mkdir () =
168168 ; (dom0, tid, (Transaction_end , [" T" ]), (Transaction_end , [" OK" ]))
169169 ]
170170
171+ let check_history_length expected =
172+ Alcotest. (check' int )
173+ ~msg: " Check history length is as expected"
174+ ~actual: (List. length ! History. history)
175+ ~expected
176+
177+ let test_history_trim () =
178+ initialize_main_loop () ;
179+ let one_loop_iteration, store, cons, doms = Xenstored. main () in
180+ let dom0 = Hashtbl. find cons.domains 0 in
181+ let dom1 = create_domU_conn cons doms 1 in
182+
183+ run store cons doms
184+ [
185+ (dom0, none, (Write , [" /local/domain/1" ; " " ]), (Write , [" OK" ]))
186+ ; (dom0, none, (Setperms , [" /local/domain/1" ; " n1" ]), (Setperms , [" OK" ]))
187+ ] ;
188+
189+ (* Start a long-running transaction *)
190+ one_loop_iteration () ;
191+ let _tid = start_transaction store cons doms dom1 in
192+ Unix. sleepf ! Define. conflict_max_history_seconds ;
193+
194+ for _ = 1 to 100 do
195+ run store cons doms
196+ [(dom1, none, (Write , [" /local/domain/1/a" ; " " ]), (Write , [" OK" ]))]
197+ done ;
198+ (* Without running frequent_ops, history list is long *)
199+ check_history_length 100 ;
200+
201+ (* frequent_ops trims history list, removing long-running transactions *)
202+ one_loop_iteration () ;
203+ check_history_length 0
204+
171205(* Check that I can read an empty value *)
172206let test_empty () =
173207 let store, doms, cons = initialize () in
@@ -367,7 +401,20 @@ let test_simple_watches () =
367401 ; (dom0, none, (Read , [" /a/1" ]), (Error , [" ENOENT" ]))
368402 ; (dom1, none, (Read , [" /a" ]), (Read , [" foo\000 " ]))
369403 ; (dom1, none, (Read , [" /a/1" ]), (Error , [" ENOENT" ]))
370- ]
404+ ] ;
405+
406+ (* Unwatch returns an error on a nonexistent path/token,
407+ removes the watch otherwise.
408+ Different connection can't touch other's watches. *)
409+ run store cons doms
410+ [
411+ (dom0, none, (Unwatch , [" /b" ; " token" ]), (Error , [" ENOENT" ]))
412+ ; (dom0, none, (Unwatch , [" /a" ; " wrongtoken" ]), (Error , [" ENOENT" ]))
413+ ; (dom1, none, (Unwatch , [" /a" ; " token" ]), (Error , [" ENOENT" ]))
414+ ; (dom0, none, (Unwatch , [" /a" ; " token" ]), (Unwatch , [" OK" ]))
415+ ; (dom0, none, (Unwatch , [" /a" ; " token" ]), (Error , [" ENOENT" ]))
416+ ] ;
417+ assert_watches dom0 []
371418
372419(* Check watches on relative paths *)
373420let test_relative_watches () =
@@ -394,7 +441,18 @@ let test_relative_watches () =
394441 )
395442 ] ;
396443 check_for_watchevent dom0 " device/vbd" " token" ;
397- assert_watches dom0 [(" device" , " token" , None )]
444+ assert_watches dom0 [(" device" , " token" , None )] ;
445+
446+ (* Unwatch returns an error on a nonexistent path/token,
447+ removes the watch otherwise *)
448+ run store cons doms
449+ [
450+ (dom0, none, (Unwatch , [" devices" ; " token" ]), (Error , [" ENOENT" ]))
451+ ; (dom0, none, (Unwatch , [" device" ; " wrongtoken" ]), (Error , [" ENOENT" ]))
452+ ; (dom0, none, (Unwatch , [" device" ; " token" ]), (Unwatch , [" OK" ]))
453+ ; (dom0, none, (Unwatch , [" device" ; " token" ]), (Error , [" ENOENT" ]))
454+ ] ;
455+ assert_watches dom0 []
398456
399457(* Check that a connection only receives a watch if it
400458 can read the node that was modified. *)
@@ -782,6 +840,41 @@ let check_quota_ent_per_domain store ~domid expected =
782840 ~actual: (get_current_entries_quota store domid)
783841 ~expected
784842
843+ let test_xsa_483 () =
844+ initialize_main_loop () ;
845+ let one_loop_iteration, store, cons, doms = Xenstored. main () in
846+ let dom0 = Hashtbl. find cons.domains 0 in
847+
848+ (* Domains > 2000 are considered dead on the first query for test purposes *)
849+ let domU = create_domU_conn cons doms 2001 in
850+
851+ run store cons doms
852+ [
853+ (dom0, none, (Write , [" /local/domain/2001" ; " " ]), (Write , [" OK" ]))
854+ ; ( dom0
855+ , none
856+ , (Setperms , [" /local/domain/2001" ; " r2001" ])
857+ , (Setperms , [" OK" ])
858+ )
859+ ] ;
860+
861+ check_quota_ent_per_domain store ~domid: 2001 1 ;
862+
863+ (* domU adds some nodes to its sub-tree *)
864+ run store cons doms
865+ [
866+ (domU, none, (Write , [" /local/domain/2001/x" ; " " ]), (Write , [" OK" ]))
867+ ; (domU, none, (Write , [" /local/domain/2001/y" ; " " ]), (Write , [" OK" ]))
868+ ; (domU, none, (Write , [" /local/domain/2001/z" ; " " ]), (Write , [" OK" ]))
869+ ] ;
870+ check_quota_ent_per_domain store ~domid: 2001 4 ;
871+
872+ (* dom2001 dies, is cleaned up *)
873+ one_loop_iteration () ;
874+
875+ (* Its quota should be reset back to 0 *)
876+ check_quota_ent_per_domain store ~domid: 2001 0
877+
785878(* Check that node creation and destruction changes a quota *)
786879let test_quota () =
787880 let store, doms, cons = initialize () in
@@ -871,6 +964,65 @@ let test_quota_transaction () =
871964 check_quota_ent_per_domain store ~domid: 1 2 ;
872965 check_quota_ent_per_domain store ~domid: 2 4
873966
967+ let test_quota_transaction_overflow () =
968+ let store, doms, cons = initialize () in
969+ let dom0 = create_dom0_conn cons doms in
970+ let dom1 = create_domU_conn cons doms 1 in
971+
972+ store.quota < - {store.quota with maxent= 3 } ;
973+ run store cons doms
974+ [
975+ (dom0, none, (Write , [" /local/domain/1/attr/x" ; " " ]), (Write , [" OK" ]))
976+ ; (dom0, none, (Write , [" /local/domain/1/attr/y" ; " " ]), (Write , [" OK" ]))
977+ ; ( dom0
978+ , none
979+ , (Setperms , [" /local/domain/1/attr/x" ; " r1" ])
980+ , (Setperms , [" OK" ])
981+ )
982+ ; ( dom0
983+ , none
984+ , (Setperms , [" /local/domain/1/attr/y" ; " r1" ])
985+ , (Setperms , [" OK" ])
986+ )
987+ ] ;
988+ check_quota_ent_per_domain store ~domid: 1 2 ;
989+
990+ (* dom1 should only be able to create one more node now *)
991+
992+ (* Creating two nodes in one transaction fails with EQUOTA during the transaction *)
993+ let tid_0 = start_transaction store cons doms dom1 in
994+ run store cons doms
995+ [
996+ (dom1, tid_0, (Write , [" /local/domain/1/attr/x/1" ; " " ]), (Write , [" OK" ]))
997+ ; ( dom1
998+ , tid_0
999+ , (Write , [" /local/domain/1/attr/y/1" ; " " ])
1000+ , (Error , [" EQUOTA" ])
1001+ )
1002+ ] ;
1003+
1004+ (* Two transactions create a node each - writes need to be coalescable *)
1005+ let tid_1 = start_transaction store cons doms dom1 in
1006+ let tid_2 = start_transaction store cons doms dom1 in
1007+ run store cons doms
1008+ [
1009+ (dom1, tid_1, (Write , [" /local/domain/1/attr/x/1" ; " " ]), (Write , [" OK" ]))
1010+ ; (dom1, tid_2, (Write , [" /local/domain/1/attr/y/1" ; " " ]), (Write , [" OK" ]))
1011+ ] ;
1012+
1013+ (* Both transactions return OK, but EQUOTA is generated during transaction
1014+ replay and nodes are not created over the limit *)
1015+ run store cons doms
1016+ [
1017+ (dom1, tid_1, (Transaction_end , [" T" ]), (Transaction_end , [" OK" ]))
1018+ ; (dom1, tid_2, (Transaction_end , [" T" ]), (Transaction_end , [" OK" ]))
1019+ ] ;
1020+ run store cons doms
1021+ [
1022+ (dom1, tid_0, (Read , [" /local/domain/1/attr/x/1" ]), (Read , [" \000 " ]))
1023+ ; (dom1, tid_0, (Read , [" /local/domain/1/attr/y/1" ]), (Error , [" ENOENT" ]))
1024+ ]
1025+
8741026(* Check that string length quota is checked correctly *)
8751027let test_quota_maxsize () =
8761028 let store, doms, cons = initialize () in
@@ -983,6 +1135,7 @@ let () =
9831135 , `Quick
9841136 , test_transactions_really_do_conflict
9851137 )
1138+ ; (" test_history_trim" , `Quick , test_history_trim)
9861139 ]
9871140 )
9881141 ; ( " Watches tests"
@@ -1008,7 +1161,12 @@ let () =
10081161 ; ( " Quota tests"
10091162 , [
10101163 (" test_quota" , `Quick , test_quota)
1164+ ; (" test_xsa_483" , `Quick , test_xsa_483)
10111165 ; (" test_quota_transaction" , `Quick , test_quota_transaction)
1166+ ; ( " test_quota_transaction_overflow"
1167+ , `Quick
1168+ , test_quota_transaction_overflow
1169+ )
10121170 ; (" test_quota_maxsize" , `Quick , test_quota_maxsize)
10131171 ; (" test_quota_maxent" , `Quick , test_quota_maxent)
10141172 ]
0 commit comments