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
7677 map (fun path -> File_exists path) (path_gen s);
7778 map (fun path -> Is_directory path) (path_gen s);
7879 map (fun (path ,new_dir_name ) -> Remove (path, new_dir_name)) (pair_gen s);
80+ map2 (fun old_path new_path -> Rename (old_path, new_path)) (path_gen s) (path_gen s);
7981 map (fun (path ,new_dir_name ) -> Mkdir (path, new_dir_name)) (pair_gen s);
8082 map (fun (path ,delete_dir_name ) -> Rmdir (path, delete_dir_name)) (pair_gen s);
8183 map (fun path -> Readdir path) (path_gen s);
@@ -102,6 +104,35 @@ struct
102104
103105 let mem_model fs path = find_opt_model fs path <> None
104106
107+ let path_is_a_dir fs path =
108+ match find_opt_model fs path with
109+ | None
110+ | Some File -> false
111+ | Some (Directory _ ) -> true
112+
113+ let path_is_a_file fs path =
114+ match find_opt_model fs path with
115+ | None
116+ | Some (Directory _ ) -> false
117+ | Some File -> true
118+
119+ let rec path_prefixes path = match path with
120+ | [] -> []
121+ | [_] -> []
122+ | n ::ns -> [n]::(List. map (fun p -> n::p) (path_prefixes ns))
123+
124+ let separate_path path =
125+ match List. rev path with
126+ | [] -> None
127+ | name ::rev_path -> Some (List. rev rev_path, name)
128+
129+ let rec is_true_prefix path1 path2 = match path1, path2 with
130+ | [] , [] -> false
131+ | [] , _ ::_ -> true
132+ | _ ::_ , [] -> false
133+ | n1 ::p1 , n2 ::p2 -> n1= n2 && is_true_prefix p1 p2
134+
135+ (* generic removal function *)
105136 let rec remove_model fs path file_name =
106137 match fs with
107138 | File -> fs
@@ -110,46 +141,14 @@ struct
110141 | [] ->
111142 (match Map_names. find_opt file_name d.fs_map with
112143 | None
113- | Some (Directory _ ) -> fs
114- | Some File -> Directory { fs_map = Map_names. remove file_name d.fs_map }
115- )
144+ | Some _ -> Directory { fs_map = Map_names. remove file_name d.fs_map })
116145 | dir ::dirs ->
117146 Directory
118147 { fs_map = Map_names. update dir (function
119148 | None -> None
120149 | Some File -> Some File
121150 | Some (Directory _ as d' ) -> Some (remove_model d' dirs file_name)) d.fs_map
122- }
123- (*
124- (match Map_names.find_opt dir d.fs_map with
125- | None
126- | Some File -> fs
127- | Some (Directory _ as d') ->
128- let fs' = remove_model d' dirs file_name in
129- Directory { fs_map = Map_names.update dir d.fs_map }
130- )
131- *)
132- )
133-
134- let rec mkdir_model fs path new_dir_name =
135- match fs with
136- | File -> fs
137- | Directory d ->
138- (match path with
139- | [] ->
140- let new_dir = Directory {fs_map = Map_names. empty} in
141- Directory {fs_map = Map_names. add new_dir_name new_dir d.fs_map}
142- | next_in_path :: tl_path ->
143- (match Map_names. find_opt next_in_path d.fs_map with
144- | None -> fs
145- | Some sub_fs ->
146- let nfs = mkdir_model sub_fs tl_path new_dir_name in
147- if nfs = sub_fs
148- then fs
149- else
150- let new_map = Map_names. remove next_in_path d.fs_map in
151- let new_map = Map_names. add next_in_path nfs new_map in
152- Directory {fs_map = new_map}))
151+ })
153152
154153 let readdir_model fs path =
155154 match find_opt_model fs path with
@@ -159,60 +158,70 @@ struct
159158 | File -> None
160159 | Directory d -> Some (Map_names. fold (fun k _ l -> k::l) d.fs_map [] ))
161160
162- let rec rmdir_model fs path delete_dir_name =
161+ (* generic insertion function *)
162+ let rec insert_model fs path new_file_name sub_tree =
163163 match fs with
164164 | File -> fs
165165 | Directory d ->
166166 (match path with
167167 | [] ->
168- (match Map_names. find_opt delete_dir_name d.fs_map with
169- | Some (Directory target ) when Map_names. is_empty target.fs_map ->
170- Directory {fs_map = Map_names. remove delete_dir_name d.fs_map}
171- | None | Some File | Some (Directory _ ) -> fs)
168+ Directory {fs_map = Map_names. add new_file_name sub_tree d.fs_map}
172169 | next_in_path :: tl_path ->
173170 (match Map_names. find_opt next_in_path d.fs_map with
174171 | None -> fs
175172 | Some sub_fs ->
176- let nfs = rmdir_model sub_fs tl_path delete_dir_name in
177- if nfs = sub_fs
178- then fs
179- else Directory {fs_map = (update_map_name d.fs_map next_in_path nfs)}))
180-
181- let rec mkfile_model fs path new_file_name =
182- match fs with
183- | File -> fs
184- | Directory d ->
185- (match path with
186- | [] ->
187- let new_file = File in
188- Directory {fs_map = Map_names. add new_file_name new_file d.fs_map}
189- | next_in_path :: tl_path ->
190- (match Map_names. find_opt next_in_path d.fs_map with
191- | None -> fs
192- | Some sub_fs ->
193- let nfs = mkfile_model sub_fs tl_path new_file_name in
173+ let nfs = insert_model sub_fs tl_path new_file_name sub_tree in
194174 if nfs = sub_fs
195175 then fs
196176 else Directory {fs_map = update_map_name d.fs_map next_in_path nfs}))
197177
178+ let rename_model fs old_path new_path =
179+ match separate_path old_path, separate_path new_path with
180+ | None , _
181+ | _ , None -> fs
182+ | Some (old_path_pref , old_name ), Some (new_path_pref , new_name ) ->
183+ (match find_opt_model fs new_path_pref with
184+ | None
185+ | Some File -> fs
186+ | Some (Directory _ ) ->
187+ (match find_opt_model fs old_path with
188+ | None -> fs
189+ | Some sub_fs ->
190+ let fs' = remove_model fs old_path_pref old_name in
191+ insert_model fs' new_path_pref new_name sub_fs))
192+
198193 let next_state c fs =
199194 match c with
200195 | File_exists _path -> fs
201196 | Mkdir (path , new_dir_name ) ->
202197 if mem_model fs (path @ [new_dir_name])
203198 then fs
204- else mkdir_model fs path new_dir_name
205- | Remove (path , file_name ) -> remove_model fs path file_name
199+ else insert_model fs path new_dir_name (Directory {fs_map = Map_names. empty})
200+ | Remove (path , file_name ) ->
201+ if find_opt_model fs (path @ [file_name]) = Some File
202+ then remove_model fs path file_name
203+ else fs
204+ | Rename (old_path , new_path ) ->
205+ if is_true_prefix old_path new_path
206+ then fs
207+ else
208+ (match find_opt_model fs old_path with
209+ | None -> fs
210+ | Some File ->
211+ if (not (mem_model fs new_path) || path_is_a_file fs new_path) then rename_model fs old_path new_path else fs
212+ | Some (Directory _ ) ->
213+ if (not (mem_model fs new_path) || readdir_model fs new_path = Some [] ) then rename_model fs old_path new_path else fs)
206214 | Is_directory _path -> fs
207215 | Rmdir (path ,delete_dir_name ) ->
208- if mem_model fs (path @ [delete_dir_name])
209- then rmdir_model fs path delete_dir_name
216+ let complete_path = path @ [delete_dir_name] in
217+ if mem_model fs complete_path && readdir_model fs complete_path = Some []
218+ then remove_model fs path delete_dir_name
210219 else fs
211220 | Readdir _path -> fs
212221 | Mkfile (path , new_file_name ) ->
213222 if mem_model fs (path @ [new_file_name])
214223 then fs
215- else mkfile_model fs path new_file_name
224+ else insert_model fs path new_file_name File
216225
217226 let init_sut () =
218227 try Sys. mkdir sandbox_root 0o700
@@ -238,6 +247,7 @@ struct
238247 | File_exists path -> Res (bool , Sys. file_exists (p path))
239248 | Is_directory path -> Res (result bool exn , protect Sys. is_directory (p path))
240249 | Remove (path , file_name ) -> Res (result unit exn , protect Sys. remove ((p path) / file_name))
250+ | Rename (old_path , new_path ) -> Res (result unit exn , protect (Sys. rename (p old_path)) (p new_path))
241251 | Mkdir (path , new_dir_name ) ->
242252 Res (result unit exn , protect (Sys. mkdir ((p path) / new_dir_name)) 0o755 )
243253 | Rmdir (path , delete_dir_name ) ->
@@ -247,18 +257,6 @@ struct
247257 | Mkfile (path , new_file_name ) ->
248258 Res (result unit exn , protect mkfile (p path / new_file_name))
249259
250- let fs_is_a_dir fs = match fs with | Directory _ -> true | File -> false
251-
252- let path_is_a_dir fs path =
253- match find_opt_model fs path with
254- | None -> false
255- | Some target_fs -> fs_is_a_dir target_fs
256-
257- let rec path_prefixes path = match path with
258- | [] -> []
259- | [_] -> []
260- | n ::ns -> [n]::(List. map (fun p -> n::p) (path_prefixes ns))
261-
262260 let postcond c (fs : filesys ) res =
263261 match c, res with
264262 | File_exists path , Res ((Bool,_ ),b ) -> b = mem_model fs path
@@ -270,19 +268,34 @@ struct
270268 | Some File -> b = false
271269 | None -> false )
272270 | Error (Sys_error s ) ->
273- (s = (p path) ^ " : No such file or directory" && find_opt_model fs path = None ) ||
274- (s = p path ^ " : Not a directory" && List. exists (fun pref -> Some File = find_opt_model fs pref) (path_prefixes path))
271+ (s = (p path) ^ " : No such file or directory" && not (mem_model fs path) ) ||
272+ (s = p path ^ " : Not a directory" && List. exists (fun pref -> not (path_is_a_dir fs pref) ) (path_prefixes path))
275273 | _ -> false )
276274 | Remove (path , file_name ), Res ((Result (Unit,Exn),_ ), res ) ->
277275 let complete_path = (path @ [file_name]) in
278276 (match res with
279277 | Ok () -> mem_model fs complete_path && path_is_a_dir fs path && not (path_is_a_dir fs complete_path)
280278 | Error (Sys_error s ) ->
281- (s = (p complete_path) ^ " : No such file or directory" && find_opt_model fs complete_path = None ) ||
279+ (s = (p complete_path) ^ " : No such file or directory" && not (mem_model fs complete_path) ) ||
282280 (s = (p complete_path) ^ " : Is a directory" && path_is_a_dir fs complete_path) ||
283281 (s = (p complete_path) ^ " : Not a directory" && not (path_is_a_dir fs path))
284282 | Error _ -> false
285283 )
284+ | Rename (old_path , new_path ), Res ((Result (Unit,Exn),_ ), res ) ->
285+ (match res with
286+ | Ok () -> mem_model fs old_path
287+ | Error (Sys_error s ) ->
288+ (s = " No such file or directory" &&
289+ not (mem_model fs old_path) || List. exists (fun pref -> not (path_is_a_dir fs pref)) (path_prefixes new_path)) ||
290+ (s = " Invalid argument" && is_true_prefix old_path new_path) ||
291+ (s = " Not a directory" &&
292+ List. exists (path_is_a_file fs) (path_prefixes old_path) ||
293+ List. exists (path_is_a_file fs) (path_prefixes new_path) ||
294+ path_is_a_dir fs old_path && mem_model fs new_path && not (path_is_a_dir fs new_path)) ||
295+ (s = " Is a directory" && path_is_a_dir fs new_path) ||
296+ (s = " Directory not empty" &&
297+ is_true_prefix new_path old_path || (path_is_a_dir fs new_path && not (readdir_model fs new_path = Some [] )))
298+ | Error _ -> false )
286299 | Mkdir (path , new_dir_name ), Res ((Result (Unit,Exn),_ ), res ) ->
287300 let complete_path = (path @ [new_dir_name]) in
288301 (match res with
0 commit comments