1-- 2-- Copyright (c) 2014-15 John Marino <draco@marino.st> 3-- 4-- Permission to use, copy, modify, and distribute this software for any 5-- purpose with or without fee is hereby granted, provided that the above 6-- copyright notice and this permission notice appear in all copies. 7-- 8-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9-- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10-- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11-- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12-- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13-- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14-- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15-- 16 17 18with DragonFly.FileStatus; 19 20package body DragonFly.HAMMER.Ghosts is 21 22 package DFS renames DragonFly.FileStatus; 23 24 25 ---------------------------- 26 -- scan_for_file_ghosts -- 27 ---------------------------- 28 29 procedure scan_for_file_ghosts ( 30 directory_path : in String; 31 file_ghosts : out Filename_Container.Vector; 32 directory_ghosts : out Filename_Container.Vector) 33 is 34 use type DIR.File_Kind; 35 descriptor : file_descriptor; 36 just_directory : constant String := DIR.Containing_Directory 37 (directory_path & "/") & "/"; 38 begin 39 file_ghosts.Clear; 40 directory_ghosts.Clear; 41 42 if DIR.Exists (directory_path) and then 43 DIR.Kind (directory_path) = DIR.Directory 44 then 45 descriptor := HB.open_file_for_reading (just_directory); 46 if descriptor > 0 then 47 establish_baseline (just_directory); 48 scan_history_of_directory ( 49 fd => descriptor, 50 directory_path => just_directory, 51 ghosts_file => file_ghosts, 52 ghosts_dir => directory_ghosts); 53 HB.close_file (descriptor); 54 SortEntry.Sort (file_ghosts); 55 SortEntry.Sort (directory_ghosts); 56 end if; 57 end if; 58 end scan_for_file_ghosts; 59 60 61 -------------------------- 62 -- establish_baseline -- 63 -------------------------- 64 65 procedure establish_baseline (directory_path : in String) 66 is 67 begin 68 living_files.Clear; 69 living_dirs.Clear; 70 declare 71 Search : DIR.Search_Type; 72 item : DIR.Directory_Entry_Type; 73 dir_filter : constant DIR.Filter_Type := (True, False, False); 74 begin 75 DIR.Start_Search (Search => Search, 76 Directory => directory_path, 77 Pattern => "", 78 Filter => dir_filter); 79 while DIR.More_Entries (Search) loop 80 DIR.Get_Next_Entry (Search => Search, Directory_Entry => item); 81 living_dirs.Append (DIR.Simple_Name (item)); 82 end loop; 83 end; 84 declare 85 Search : DIR.Search_Type; 86 item : DIR.Directory_Entry_Type; 87 ord_filter : constant DIR.Filter_Type := (False, True, False); 88 begin 89 DIR.Start_Search (Search => Search, 90 Directory => directory_path, 91 Pattern => "", 92 Filter => ord_filter); 93 while DIR.More_Entries (Search) loop 94 DIR.Get_Next_Entry (Search => Search, Directory_Entry => item); 95 living_files.Append (DIR.Simple_Name (item)); 96 end loop; 97 end; 98 end establish_baseline; 99 100 101 ------------------------------------------ 102 -- Format %016x : format_as_hex (tid) -- 103 ------------------------------------------ 104 105 function format_as_hex (bignum : HB.hammer_tid) return String is 106 begin 107 return zeropad_hex (uInt64 (bignum)); 108 end format_as_hex; 109 110 111 ------------------------ 112 -- scan_transaction -- 113 ------------------------ 114 115 procedure scan_transaction ( 116 directory_path : in String; 117 transaction : in String; 118 fkind : in DIR.File_Kind; 119 ghost : in out Filename_Container.Vector) 120 is 121 use type DIR.File_Kind; 122 filter : DIR.Filter_Type := (others => False); 123 Search : DIR.Search_Type; 124 item : DIR.Directory_Entry_Type; 125 126 begin 127 filter (fkind) := True; 128 DIR.Start_Search (Search => Search, 129 Directory => directory_path & transaction, 130 Pattern => "", 131 Filter => filter); 132 while DIR.More_Entries (Search) loop 133 DIR.Get_Next_Entry (Search => Search, Directory_Entry => item); 134 declare 135 ndx : Filename_Container.Extended_Index; 136 filename : constant String := DIR.Simple_Name (item); 137 truedir : constant String := transaction & "/" & filename; 138 traxpath : constant String := directory_path & truedir; 139 okay : constant Boolean := 140 not (filename = "." or filename = ".."); 141 found : Boolean := False; 142 begin 143 if okay then 144 if fkind = DIR.Directory then 145 ndx := living_dirs.Find_Index (Item => filename); 146 if ndx = Filename_Container.No_Index then 147 for k in 1 .. Natural (ghost.Length) loop 148 if not found and then 149 ghost.Element (k) (22 .. ghost.Element (k)'Last) = 150 filename 151 then 152 found := True; 153 end if; 154 end loop; 155 if not found and then 156 directory_has_files (traxpath) 157 then 158 ghost.Append (truedir); 159 end if; 160 end if; 161 else 162 ndx := living_files.Find_Index (Item => filename); 163 if ndx = Filename_Container.No_Index then 164 ndx := ghost.Find_Index (Item => filename); 165 if ndx = Filename_Container.No_Index then 166 declare 167 inode : DFS.inode_data; 168 result : Int32; 169 begin 170 DFS.stat (traxpath, inode, result); 171 if result < 0 then 172 raise FILE_Failure; 173 end if; 174 if (inode.st_mode and DFS.S_IFIFO) > 0 then 175 raise Fake_Transaction; 176 end if; 177 ghost.Append (filename); 178 exception 179 when FILE_Failure => null; 180 when Fake_Transaction => null; 181 end; 182 end if; 183 end if; 184 end if; 185 end if; 186 end; 187 end loop; 188 189 end scan_transaction; 190 191 192 --------------------------------- 193 -- scan_history_of_directory -- 194 --------------------------------- 195 196 procedure scan_history_of_directory ( 197 fd : in file_descriptor; 198 directory_path : in String; 199 ghosts_file : in out Filename_Container.Vector; 200 ghosts_dir : in out Filename_Container.Vector) 201 is 202 history : HB.hammer_ioc_history; 203 eject : Boolean; 204 tids : TID_Container.Vector; 205 ndx : TID_Container.Extended_Index; 206 begin 207 history := HB.initialize_history; 208 history.nxt_key := HB.HAMMER_MAX_KEY; 209 history.head.flags := HB.HAMMER_IOC_HISTORY_ATKEY; 210 HB.retrieve_history (open_file => fd, history => history); 211 tids.Clear; 212 scan_loop : 213 loop 214 for j in 0 .. history.count - 1 loop 215 ndx := tids.Find_Index (Item => history.hist_ary (j).tid); 216 if ndx = TID_Container.No_Index then 217 tids.Append (history.hist_ary (j).tid); 218 end if; 219 end loop; 220 exit scan_loop when 221 (history.head.flags and HB.HAMMER_IOC_HISTORY_EOF) > 0; 222 223 if (history.head.flags and HB.HAMMER_IOC_HISTORY_NEXT_KEY) > 0 and 224 history.key /= history.nxt_key 225 then 226 history.key := history.nxt_key; 227 history.nxt_key := HB.HAMMER_MAX_KEY; 228 eject := False; 229 end if; 230 if (history.head.flags and HB.HAMMER_IOC_HISTORY_NEXT_TID) > 0 and 231 history.beg_tid /= history.nxt_tid 232 then 233 history.beg_tid := history.nxt_tid; 234 eject := False; 235 end if; 236 exit scan_loop when eject; 237 HB.retrieve_history (open_file => fd, history => history); 238 end loop scan_loop; 239 SortTid.Sort (tids); 240 for x in reverse 1 .. Natural (tids.Length) loop 241 declare 242 temptrx : constant String := "@@0x" & format_as_hex (tids (x)); 243 inode : DFS.inode_data; 244 result : Int32; 245 begin 246 DFS.stat (directory_path & temptrx, inode, result); 247 if result < 0 then 248 raise FILE_Failure; 249 end if; 250 if (inode.st_mode and DFS.S_IFDIR) = 0 then 251 raise FILE_Failure; 252 end if; 253 scan_transaction ( 254 directory_path => directory_path, 255 transaction => temptrx, 256 fkind => DIR.Directory, 257 ghost => ghosts_dir); 258 scan_transaction ( 259 directory_path => directory_path, 260 transaction => temptrx, 261 fkind => DIR.Ordinary_File, 262 ghost => ghosts_file); 263 exception 264 when FILE_Failure => null; 265 end; 266 end loop; 267 268 end scan_history_of_directory; 269 270 271 --------------------------- 272 -- directory_has_file -- 273 --------------------------- 274 275 function directory_has_files (directory_path : String) return Boolean 276 is 277 search : DIR.Search_Type; 278 filter : DIR.Filter_Type := (others => True); 279 data : DIR.Directory_Entry_Type; 280 result : Boolean := False; 281 begin 282 if not DIR.Exists (directory_path) then 283 return False; 284 end if; 285 DIR.Start_Search ( 286 Search => search, 287 Directory => directory_path, 288 Pattern => "", 289 Filter => filter); 290 while not result and DIR.More_Entries (search) loop 291 DIR.Get_Next_Entry (search, data); 292 declare 293 sname : constant String := DIR.Simple_Name (data); 294 okay : constant Boolean := not (sname = "." or sname = ".."); 295 begin 296 if okay then 297 result := True; 298 end if; 299 end; 300 end loop; 301 DIR.End_Search (search); 302 return result; 303 end directory_has_files; 304 305end DragonFly.HAMMER.Ghosts; 306