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 17with Ada.Text_IO; 18with Ada.Strings.Fixed; 19with Ada.Calendar.Formatting; 20with Ada.Calendar.Conversions; 21with Ada.Directories; 22with Ada.Strings; 23with Ada.Strings.Fixed; 24with DragonFly.FileStatus; 25 26package body DragonFly.HAMMER.History is 27 28 package TIO renames Ada.Text_IO; 29 package CFM renames Ada.Calendar.Formatting; 30 package CCV renames Ada.Calendar.Conversions; 31 package DIR renames Ada.Directories; 32 package SFX renames Ada.Strings.Fixed; 33 package STR renames Ada.Strings; 34 package DFS renames DragonFly.FileStatus; 35 36 37 -------------------- 38 -- scan_history -- 39 -------------------- 40 41 procedure scan_history (path : in String; result : out scan_result) 42 is 43 descriptor : file_descriptor; 44 history : HB.hammer_ioc_history; 45 formhack : TraxTime; 46 begin 47 result.state := clean; 48 result.path_check := not_found; 49 50 declare 51 begin 52 descriptor := HB.open_file_for_reading (path); 53 result.path_check := found; 54 exception 55 when FILE_Failure => null; 56 end; 57 if result.path_check = not_found then 58 -- If we are here, then descriptor was never opened 59 if DIR.Exists (path) then 60 -- The file exists, but couldn't be opened. No read perms! 61 result.path_check := secret; 62 return; 63 end if; 64 declare 65 dname : Trax; 66 delname : Trax; 67 delstamp : uInt32; 68 begin 69 dname := match_against_directory_trax (path); 70 descriptor := HB.open_file_for_reading (path => path & dname); 71 collect_history (fd => descriptor, filename => path, 72 suffix => delname, timestamp => delstamp); 73 HB.close_file (descriptor); 74 if delname = failed_search then 75 raise Match_Failure; 76 end if; 77 descriptor := HB.open_file_for_reading (path => path & delname); 78 HB.close_file (descriptor); 79 formhack := format_timestamp (delstamp); 80 result.path_check := deleted; 81 result.history.Append (New_Item => (delname, formhack)); 82 exception 83 when FILE_Failure => raise Match_Failure; 84 end; 85 end if; 86 87 88 if result.path_check = found then 89 history := HB.initialize_history; 90 HB.retrieve_history (open_file => descriptor, history => history); 91 if (history.head.flags and HB.HAMMER_IOC_HISTORY_UNSYNCED) > 0 then 92 result.state := dirty; 93 end if; 94 History_Loop : 95 loop 96 for j in 0 .. history.count - 1 loop 97 formhack := format_timestamp (history.hist_ary (j).time32); 98 declare 99 testfd : file_descriptor; 100 fname : constant Trax := "@@0x" & 101 format_as_hex (history.hist_ary (j).tid); 102 inode : DFS.inode_data; 103 statres : Int32; 104 begin 105 DFS.stat (path & fname, inode, statres); 106 if statres < 0 then 107 raise FILE_Failure; 108 end if; 109 if (inode.st_mode and DFS.S_IFIFO) > 0 then 110 raise Fake_Transaction; 111 end if; 112 testfd := HB.open_file_for_reading (path & fname); 113 HB.close_file (testfd); 114 result.history.Append (New_Item => (fname, formhack)); 115 exception 116 when FILE_Failure => null; 117 when Fake_Transaction => null; 118 end; 119 end loop; 120 exit History_Loop when 121 (history.head.flags and HB.HAMMER_IOC_HISTORY_EOF) > 0; 122 exit History_Loop when 123 (history.head.flags and HB.HAMMER_IOC_HISTORY_NEXT_KEY) > 0; 124 exit History_Loop when 125 (history.head.flags and HB.HAMMER_IOC_HISTORY_NEXT_TID) = 0; 126 history.beg_tid := history.nxt_tid; 127 HB.retrieve_history ( 128 open_file => descriptor, 129 history => history); 130 end loop History_Loop; 131 HB.close_file (descriptor); 132 end if; 133 134 exception 135 when IOCTL_Failure | Match_Failure => null; 136 when Error : others => 137 TIO.Put_Line (Ada.Exceptions.Exception_Information (Error)); 138 end scan_history; 139 140 141 ------------------------------------ 142 -- match_against_directory_trax -- 143 ------------------------------------ 144 145 function match_against_directory_trax (filename : String) return Trax is 146 fd : file_descriptor; 147 slash : Natural; 148 result : String := failed_search; 149 dontcare : uInt32; 150 begin 151 slash := SFX.Index ( 152 Source => filename, 153 Pattern => "/", 154 Going => STR.Backward); 155 if slash = 0 then 156 fd := HB.open_file_for_reading ("."); 157 else 158 fd := HB.open_file_for_reading ( 159 filename (filename'First .. slash - 1)); 160 end if; 161 if fd > 0 then 162 collect_history (fd => fd, filename => filename, suffix => result, 163 timestamp => dontcare); 164 HB.close_file (fd); 165 end if; 166 if result = failed_search then 167 raise Match_Failure; 168 end if; 169 return result; -- except will be caught upstream if fd < 0 170 end match_against_directory_trax; 171 172 173 ----------------------- 174 -- collect_history -- 175 ----------------------- 176 177 procedure collect_history ( 178 fd : in file_descriptor; 179 filename : in String; 180 suffix : out Trax; 181 timestamp : out uInt32) 182 is 183 history : HB.hammer_ioc_history; 184 top_tid : HB.hammer_tid := 0; 185 eject : Boolean; 186 begin 187 suffix := failed_search; 188 history := HB.initialize_history; 189 history.nxt_key := HB.HAMMER_MAX_KEY; 190 history.head.flags := HB.HAMMER_IOC_HISTORY_ATKEY; 191 HB.retrieve_history (open_file => fd, history => history); 192 loop 193 eject := True; 194 for j in 0 .. history.count - 1 loop 195 if history.hist_ary (j).tid > top_tid then 196 declare 197 temptrx : constant String := "@@0x" & 198 format_as_hex (history.hist_ary (j).tid); 199 testfd : file_descriptor; 200 inode : DFS.inode_data; 201 result : Int32; 202 begin 203 DFS.stat (filename & temptrx, inode, result); 204 if result < 0 then 205 raise FILE_Failure; 206 end if; 207 if (inode.st_mode and DFS.S_IFIFO) > 0 then 208 raise Fake_Transaction; 209 end if; 210 testfd := HB.open_file_for_reading (filename & temptrx); 211 if testfd > 0 then 212 top_tid := history.hist_ary (j).tid; 213 timestamp := history.hist_ary (j).time32; 214 suffix := temptrx; 215 HB.close_file (testfd); 216 end if; 217 exception 218 when FILE_Failure => null; 219 when Fake_Transaction => null; 220 end; 221 end if; 222 end loop; 223 exit when (history.head.flags and HB.HAMMER_IOC_HISTORY_EOF) > 0; 224 if (history.head.flags and HB.HAMMER_IOC_HISTORY_NEXT_KEY) > 0 and 225 history.key /= history.nxt_key 226 then 227 history.key := history.nxt_key; 228 history.nxt_key := HB.HAMMER_MAX_KEY; 229 eject := False; 230 end if; 231 if (history.head.flags and HB.HAMMER_IOC_HISTORY_NEXT_TID) > 0 and 232 history.beg_tid /= history.nxt_tid 233 then 234 history.beg_tid := history.nxt_tid; 235 eject := False; 236 end if; 237 exit when eject; 238 HB.retrieve_history (open_file => fd, history => history); 239 end loop; 240 end collect_history; 241 242 243 ------------------------------------------ 244 -- Format %016x : format_as_hex (tid) -- 245 ------------------------------------------ 246 247 function format_as_hex (bignum : HB.hammer_tid) return String is 248 begin 249 return zeropad_hex (uInt64 (bignum)); 250 end format_as_hex; 251 252 253 ------------------------ 254 -- format_timestamp -- 255 ------------------------ 256 257 function format_timestamp (raw : uInt32) return TraxTime is 258 hack : HB.IC.long := HB.IC.long (raw); 259 hack_time : CAL.Time; 260 begin 261 hack_time := CCV.To_Ada_Time (Unix_Time => hack); 262 return CFM.Image (Date => hack_time, Time_Zone => tz_offset); 263 end format_timestamp; 264 265 266 ------------------------------ 267 -- modification_timestamp -- 268 ------------------------------ 269 270 function modification_timestamp (path : in String) return TraxTime is 271 modtime : Ada.Calendar.Time; 272 begin 273 modtime := DIR.Modification_Time (path); 274 return CFM.Image (Date => modtime, Time_Zone => tz_offset); 275 exception 276 when others => 277 return "1776-07-04 16:00:00"; 278 end modification_timestamp; 279 280end DragonFly.HAMMER.History; 281