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
@@ -237,6 +246,7 @@ struct
237246 | File_exists path -> Res (bool , Sys. file_exists (p path))
238247 | Is_directory path -> Res (result bool exn , protect Sys. is_directory (p path))
239248 | Remove (path , file_name ) -> Res (result unit exn , protect Sys. remove ((p path) / file_name))
249+ | Rename (old_path , new_path ) -> Res (result unit exn , protect (Sys. rename (p old_path)) (p new_path))
240250 | Mkdir (path , new_dir_name ) ->
241251 Res (result unit exn , protect (Sys. mkdir ((p path) / new_dir_name)) 0o755 )
242252 | Rmdir (path , delete_dir_name ) ->
@@ -246,18 +256,6 @@ struct
246256 | Mkfile (path , new_file_name ) ->
247257 Res (result unit exn , protect mkfile (p path / new_file_name))
248258
249- let fs_is_a_dir fs = match fs with | Directory _ -> true | File -> false
250-
251- let path_is_a_dir fs path =
252- match find_opt_model fs path with
253- | None -> false
254- | Some target_fs -> fs_is_a_dir target_fs
255-
256- let rec path_prefixes path = match path with
257- | [] -> []
258- | [_] -> []
259- | n ::ns -> [n]::(List. map (fun p -> n::p) (path_prefixes ns))
260-
261259 let postcond c (fs : filesys ) res =
262260 match c, res with
263261 | File_exists path , Res ((Bool,_ ),b ) -> b = mem_model fs path
@@ -269,19 +267,34 @@ struct
269267 | Some File -> b = false
270268 | None -> false )
271269 | Error (Sys_error s ) ->
272- (s = (p path) ^ " : No such file or directory" && find_opt_model fs path = None ) ||
273- (s = p path ^ " : Not a directory" && List. exists (fun pref -> Some File = find_opt_model fs pref) (path_prefixes path))
270+ (s = (p path) ^ " : No such file or directory" && not (mem_model fs path) ) ||
271+ (s = p path ^ " : Not a directory" && List. exists (fun pref -> not (path_is_a_dir fs pref) ) (path_prefixes path))
274272 | _ -> false )
275273 | Remove (path , file_name ), Res ((Result (Unit,Exn),_ ), res ) ->
276274 let complete_path = (path @ [file_name]) in
277275 (match res with
278276 | Ok () -> mem_model fs complete_path && path_is_a_dir fs path && not (path_is_a_dir fs complete_path)
279277 | Error (Sys_error s ) ->
280- (s = (p complete_path) ^ " : No such file or directory" && find_opt_model fs complete_path = None ) ||
278+ (s = (p complete_path) ^ " : No such file or directory" && not (mem_model fs complete_path) ) ||
281279 (s = (p complete_path) ^ " : Is a directory" && path_is_a_dir fs complete_path) ||
282280 (s = (p complete_path) ^ " : Not a directory" && not (path_is_a_dir fs path))
283281 | Error _ -> false
284282 )
283+ | Rename (old_path , new_path ), Res ((Result (Unit,Exn),_ ), res ) ->
284+ (match res with
285+ | Ok () -> mem_model fs old_path
286+ | Error (Sys_error s ) ->
287+ (s = " No such file or directory" &&
288+ not (mem_model fs old_path) || List. exists (fun pref -> not (path_is_a_dir fs pref)) (path_prefixes new_path)) ||
289+ (s = " Invalid argument" && is_true_prefix old_path new_path) ||
290+ (s = " Not a directory" &&
291+ List. exists (path_is_a_file fs) (path_prefixes old_path) ||
292+ List. exists (path_is_a_file fs) (path_prefixes new_path) ||
293+ path_is_a_dir fs old_path && mem_model fs new_path && not (path_is_a_dir fs new_path)) ||
294+ (s = " Is a directory" && path_is_a_dir fs new_path) ||
295+ (s = " Directory not empty" &&
296+ is_true_prefix new_path old_path || (path_is_a_dir fs new_path && not (readdir_model fs new_path = Some [] )))
297+ | Error _ -> false )
285298 | Mkdir (path , new_dir_name ), Res ((Result (Unit,Exn),_ ), res ) ->
286299 let complete_path = (path @ [new_dir_name]) in
287300 (match res with
0 commit comments