99 | File_exists of path
1010 | Is_directory of path
1111 | Remove of path * string
12+ | Rename of path * path
1213 | Mkdir of path * string
1314 | Rmdir of path * string
1415 | Readdir of path
8788 map (fun path -> File_exists path) (path_gen s);
8889 map (fun path -> Is_directory path) (path_gen s);
8990 map (fun (path ,new_dir_name ) -> Remove (path, new_dir_name)) (pair_gen s);
91+ map2 (fun old_path new_path -> Rename (old_path, new_path)) (path_gen s) (path_gen s);
9092 map (fun (path ,new_dir_name ) -> Mkdir (path, new_dir_name)) (pair_gen s);
9193 map (fun (path ,delete_dir_name ) -> Rmdir (path, delete_dir_name)) (pair_gen s);
9294 map (fun path -> Readdir path) (path_gen s);
@@ -113,6 +115,35 @@ struct
113115
114116 let mem_model fs path = find_opt_model fs path <> None
115117
118+ let path_is_a_dir fs path =
119+ match find_opt_model fs path with
120+ | None
121+ | Some File -> false
122+ | Some (Directory _ ) -> true
123+
124+ let path_is_a_file fs path =
125+ match find_opt_model fs path with
126+ | None
127+ | Some (Directory _ ) -> false
128+ | Some File -> true
129+
130+ let rec path_prefixes path = match path with
131+ | [] -> []
132+ | [_] -> []
133+ | n ::ns -> [n]::(List. map (fun p -> n::p) (path_prefixes ns))
134+
135+ let separate_path path =
136+ match List. rev path with
137+ | [] -> None
138+ | name ::rev_path -> Some (List. rev rev_path, name)
139+
140+ let rec is_true_prefix path1 path2 = match path1, path2 with
141+ | [] , [] -> false
142+ | [] , _ ::_ -> true
143+ | _ ::_ , [] -> false
144+ | n1 ::p1 , n2 ::p2 -> n1= n2 && is_true_prefix p1 p2
145+
146+ (* generic removal function *)
116147 let rec remove_model fs path file_name =
117148 match fs with
118149 | File -> fs
@@ -121,46 +152,14 @@ struct
121152 | [] ->
122153 (match Map_names. find_opt file_name d.fs_map with
123154 | None
124- | Some (Directory _ ) -> fs
125- | Some File -> Directory { fs_map = Map_names. remove file_name d.fs_map }
126- )
155+ | Some _ -> Directory { fs_map = Map_names. remove file_name d.fs_map })
127156 | dir ::dirs ->
128157 Directory
129158 { fs_map = Map_names. update dir (function
130159 | None -> None
131160 | Some File -> Some File
132161 | Some (Directory _ as d' ) -> Some (remove_model d' dirs file_name)) d.fs_map
133- }
134- (*
135- (match Map_names.find_opt dir d.fs_map with
136- | None
137- | Some File -> fs
138- | Some (Directory _ as d') ->
139- let fs' = remove_model d' dirs file_name in
140- Directory { fs_map = Map_names.update dir d.fs_map }
141- )
142- *)
143- )
144-
145- let rec mkdir_model fs path new_dir_name =
146- match fs with
147- | File -> fs
148- | Directory d ->
149- (match path with
150- | [] ->
151- let new_dir = Directory {fs_map = Map_names. empty} in
152- Directory {fs_map = Map_names. add new_dir_name new_dir d.fs_map}
153- | next_in_path :: tl_path ->
154- (match Map_names. find_opt next_in_path d.fs_map with
155- | None -> fs
156- | Some sub_fs ->
157- let nfs = mkdir_model sub_fs tl_path new_dir_name in
158- if nfs = sub_fs
159- then fs
160- else
161- let new_map = Map_names. remove next_in_path d.fs_map in
162- let new_map = Map_names. add next_in_path nfs new_map in
163- Directory {fs_map = new_map}))
162+ })
164163
165164 let readdir_model fs path =
166165 match find_opt_model fs path with
@@ -170,60 +169,70 @@ struct
170169 | File -> None
171170 | Directory d -> Some (Map_names. fold (fun k _ l -> k::l) d.fs_map [] ))
172171
173- let rec rmdir_model fs path delete_dir_name =
172+ (* generic insertion function *)
173+ let rec insert_model fs path new_file_name sub_tree =
174174 match fs with
175175 | File -> fs
176176 | Directory d ->
177177 (match path with
178178 | [] ->
179- (match Map_names. find_opt delete_dir_name d.fs_map with
180- | Some (Directory target ) when Map_names. is_empty target.fs_map ->
181- Directory {fs_map = Map_names. remove delete_dir_name d.fs_map}
182- | None | Some File | Some (Directory _ ) -> fs)
179+ Directory {fs_map = Map_names. add new_file_name sub_tree d.fs_map}
183180 | next_in_path :: tl_path ->
184181 (match Map_names. find_opt next_in_path d.fs_map with
185182 | None -> fs
186183 | Some sub_fs ->
187- let nfs = rmdir_model sub_fs tl_path delete_dir_name in
188- if nfs = sub_fs
189- then fs
190- else Directory {fs_map = (update_map_name d.fs_map next_in_path nfs)}))
191-
192- let rec mkfile_model fs path new_file_name =
193- match fs with
194- | File -> fs
195- | Directory d ->
196- (match path with
197- | [] ->
198- let new_file = File in
199- Directory {fs_map = Map_names. add new_file_name new_file d.fs_map}
200- | next_in_path :: tl_path ->
201- (match Map_names. find_opt next_in_path d.fs_map with
202- | None -> fs
203- | Some sub_fs ->
204- let nfs = mkfile_model sub_fs tl_path new_file_name in
184+ let nfs = insert_model sub_fs tl_path new_file_name sub_tree in
205185 if nfs = sub_fs
206186 then fs
207187 else Directory {fs_map = update_map_name d.fs_map next_in_path nfs}))
208188
189+ let rename_model fs old_path new_path =
190+ match separate_path old_path, separate_path new_path with
191+ | None , _
192+ | _ , None -> fs
193+ | Some (old_path_pref , old_name ), Some (new_path_pref , new_name ) ->
194+ (match find_opt_model fs new_path_pref with
195+ | None
196+ | Some File -> fs
197+ | Some (Directory _ ) ->
198+ (match find_opt_model fs old_path with
199+ | None -> fs
200+ | Some sub_fs ->
201+ let fs' = remove_model fs old_path_pref old_name in
202+ insert_model fs' new_path_pref new_name sub_fs))
203+
209204 let next_state c fs =
210205 match c with
211206 | File_exists _path -> fs
212207 | Mkdir (path , new_dir_name ) ->
213208 if mem_model fs (path @ [new_dir_name])
214209 then fs
215- else mkdir_model fs path new_dir_name
216- | Remove (path , file_name ) -> remove_model fs path file_name
210+ else insert_model fs path new_dir_name (Directory {fs_map = Map_names. empty})
211+ | Remove (path , file_name ) ->
212+ if find_opt_model fs (path @ [file_name]) = Some File
213+ then remove_model fs path file_name
214+ else fs
215+ | Rename (old_path , new_path ) ->
216+ if is_true_prefix old_path new_path
217+ then fs
218+ else
219+ (match find_opt_model fs old_path with
220+ | None -> fs
221+ | Some File ->
222+ if (not (mem_model fs new_path) || path_is_a_file fs new_path) then rename_model fs old_path new_path else fs
223+ | Some (Directory _ ) ->
224+ if (not (mem_model fs new_path) || readdir_model fs new_path = Some [] ) then rename_model fs old_path new_path else fs)
217225 | Is_directory _path -> fs
218226 | Rmdir (path ,delete_dir_name ) ->
219- if mem_model fs (path @ [delete_dir_name])
220- then rmdir_model fs path delete_dir_name
227+ let complete_path = path @ [delete_dir_name] in
228+ if mem_model fs complete_path && readdir_model fs complete_path = Some []
229+ then remove_model fs path delete_dir_name
221230 else fs
222231 | Readdir _path -> fs
223232 | Mkfile (path , new_file_name ) ->
224233 if mem_model fs (path @ [new_file_name])
225234 then fs
226- else mkfile_model fs path new_file_name
235+ else insert_model fs path new_file_name File
227236
228237 let init_sut () =
229238 try Sys. mkdir sandbox_root 0o700
@@ -249,6 +258,7 @@ struct
249258 | File_exists path -> Res (bool , Sys. file_exists (p path))
250259 | Is_directory path -> Res (result bool exn , protect Sys. is_directory (p path))
251260 | Remove (path , file_name ) -> Res (result unit exn , protect Sys. remove ((p path) / file_name))
261+ | Rename (old_path , new_path ) -> Res (result unit exn , protect (Sys. rename (p old_path)) (p new_path))
252262 | Mkdir (path , new_dir_name ) ->
253263 Res (result unit exn , protect (Sys. mkdir ((p path) / new_dir_name)) 0o755 )
254264 | Rmdir (path , delete_dir_name ) ->
@@ -258,18 +268,6 @@ struct
258268 | Mkfile (path , new_file_name ) ->
259269 Res (result unit exn , protect mkfile (p path / new_file_name))
260270
261- let fs_is_a_dir fs = match fs with | Directory _ -> true | File -> false
262-
263- let path_is_a_dir fs path =
264- match find_opt_model fs path with
265- | None -> false
266- | Some target_fs -> fs_is_a_dir target_fs
267-
268- let rec path_prefixes path = match path with
269- | [] -> []
270- | [_] -> []
271- | n ::ns -> [n]::(List. map (fun p -> n::p) (path_prefixes ns))
272-
273271 let postcond c (fs : filesys ) res =
274272 match c, res with
275273 | File_exists path , Res ((Bool,_ ),b ) -> b = mem_model fs path
@@ -281,19 +279,34 @@ struct
281279 | Some File -> b = false
282280 | None -> false )
283281 | Error (Sys_error s ) ->
284- (s = (p path) ^ " : No such file or directory" && find_opt_model fs path = None ) ||
285- (s = p path ^ " : Not a directory" && List. exists (fun pref -> Some File = find_opt_model fs pref) (path_prefixes path))
282+ (s = (p path) ^ " : No such file or directory" && not (mem_model fs path) ) ||
283+ (s = p path ^ " : Not a directory" && List. exists (fun pref -> not (path_is_a_dir fs pref) ) (path_prefixes path))
286284 | _ -> false )
287285 | Remove (path , file_name ), Res ((Result (Unit,Exn),_ ), res ) ->
288286 let complete_path = (path @ [file_name]) in
289287 (match res with
290288 | Ok () -> mem_model fs complete_path && path_is_a_dir fs path && not (path_is_a_dir fs complete_path)
291289 | Error (Sys_error s ) ->
292- (s = (p complete_path) ^ " : No such file or directory" && find_opt_model fs complete_path = None ) ||
290+ (s = (p complete_path) ^ " : No such file or directory" && not (mem_model fs complete_path) ) ||
293291 (s = (p complete_path) ^ " : Is a directory" && path_is_a_dir fs complete_path) ||
294292 (s = (p complete_path) ^ " : Not a directory" && not (path_is_a_dir fs path))
295293 | Error _ -> false
296294 )
295+ | Rename (old_path , new_path ), Res ((Result (Unit,Exn),_ ), res ) ->
296+ (match res with
297+ | Ok () -> mem_model fs old_path
298+ | Error (Sys_error s ) ->
299+ (s = " No such file or directory" &&
300+ not (mem_model fs old_path) || List. exists (fun pref -> not (path_is_a_dir fs pref)) (path_prefixes new_path)) ||
301+ (s = " Invalid argument" && is_true_prefix old_path new_path) ||
302+ (s = " Not a directory" &&
303+ List. exists (path_is_a_file fs) (path_prefixes old_path) ||
304+ List. exists (path_is_a_file fs) (path_prefixes new_path) ||
305+ path_is_a_dir fs old_path && mem_model fs new_path && not (path_is_a_dir fs new_path)) ||
306+ (s = " Is a directory" && path_is_a_dir fs new_path) ||
307+ (s = " Directory not empty" &&
308+ is_true_prefix new_path old_path || (path_is_a_dir fs new_path && not (readdir_model fs new_path = Some [] )))
309+ | Error _ -> false )
297310 | Mkdir (path , new_dir_name ), Res ((Result (Unit,Exn),_ ), res ) ->
298311 let complete_path = (path @ [new_dir_name]) in
299312 (match res with
0 commit comments