open Utils
+open Slave_types
+
+(* See struct/field description in .mli file. *)
type t = {
view : GTree.view;
model : GTree.tree_store;
- hash : (int, hdata) Hashtbl.t; (* hash from index_col -> hdata *)
+ hash : (int, hdata) Hashtbl.t;
index_col : int GTree.column;
mode_col : string GTree.column;
name_col : string GTree.column;
- size_col : int64 GTree.column;
+ size_col : string GTree.column;
date_col : string GTree.column;
- link_col : string GTree.column;
- mutable status : (string -> unit) option;
}
-and hdata = state_t * content_t
+and hdata = {
+ mutable state : state_t;
+ content : content_t;
+ mutable visited : bool;
+ mutable hiveh : Hivex.t option;
+}
-(* The type of the hidden column used to implement on-demand loading.
- * All rows are classified as either nodes or leafs (eg. a "node" might
- * be a directory, or a top-level operating system, or anything else
- * which the user could open and look inside).
- *)
and state_t =
- | IsLeaf (* there are no children *)
- | NodeNotStarted (* user has not tried to open this *)
- | NodeLoading (* user tried to open it, still loading *)
- | IsNode (* we've loaded the children of this directory *)
+ | IsLeaf
+ | NodeNotStarted
+ | NodeLoading
+ | IsNode
-(* The actual content of a row. *)
and content_t =
- | Loading (* special "loading ..." node *)
- | ErrorMessage of string (* error message node *)
- | Top of Slave.source (* top level OS or volume node *)
- | Directory of Slave.direntry (* a directory *)
- | File of Slave.direntry (* a file inc. special files *)
+ | Loading
+ | ErrorMessage of string
+ | Info of string
+ | Top of source
+ | TopWinReg of source * string * string * string
+ | Directory of direntry
+ | File of direntry
+ | RegKey of Hivex.node
+ | RegValue of Hivex.value
(* Store hdata into a row. *)
let store_hdata {model = model; hash = hash; index_col = index_col} row hdata =
try Hashtbl.find hash index
with Not_found -> assert false
+(* Iterate over children of node, looking for matching hdata. *)
+let find_child_node_by_content ({ model = model } as t) row c =
+ let rec loop row =
+ if (get_hdata t row).content = c then
+ row
+ else if model#iter_next row then
+ loop row
+ else
+ raise Not_found
+ in
+
+ if not (model#iter_has_child row) then
+ raise Not_found;
+
+ let first_child = model#iter_children (Some row) in
+ loop first_child
+
(* Search up to the top of the tree so we know if this directory
* comes from an OS or a volume, and the full path to here.
*
* \_ Directory
* \_ Directory
* \_ Loading <--- you are here
+ *
+ * Note this function cannot be called on registry keys. See
+ * {!get_registry_path} for that.
*)
let rec get_pathname ({ model = model } as t) row =
let hdata = get_hdata t row in
let parent = model#iter_parent row in
match hdata, parent with
- | (IsLeaf, Loading), Some parent ->
+ | { state=IsLeaf; content=(Loading|ErrorMessage _|Info _) }, Some parent ->
get_pathname t parent
- | (IsLeaf, Loading), None ->
+ | { state=IsLeaf; content=(Loading|ErrorMessage _|Info _) }, None ->
assert false
- | (_, Directory { Slave.dent_name = name }), Some parent
- | (_, File { Slave.dent_name = name }), Some parent ->
+ | { content=Directory { dent_name = name }}, Some parent
+ | { content=File { dent_name = name }}, Some parent ->
let src, parent_name = get_pathname t parent in
let path =
if parent_name = "/" then "/" ^ name
else parent_name ^ "/" ^ name in
src, path
- | (_, Top src), _ -> src, "/"
- | (_, Directory _), None -> assert false
- | (_, File _), None -> assert false
- | (_, Loading), _ -> assert false
- | (_, ErrorMessage _), _ -> assert false
-
-(* Update the status bar. *)
-let update_status { status = f } msg =
- match f with
- | None -> () (* user didn't give us a [status] function to call *)
- | Some f -> f msg
+ | { content=Top src }, _ -> src, "/"
+ | { content=Directory _ }, None -> assert false
+ | { content=File _ }, None -> assert false
+ | { content=Loading }, _ -> assert false
+ | { content=ErrorMessage _ }, _ -> assert false
+ | { content=Info _ }, _ -> assert false
+ | { content=TopWinReg _ }, _ -> assert false
+ | { content=RegKey _ }, _ -> assert false
+ | { content=RegValue _ }, _ -> assert false
+
+(* Search up to the top of the tree from a registry key.
+ *
+ * The path up the tree will always look something like:
+ * TopWinReg
+ * \_ RegKey
+ * \_ RegKey <--- you are here
+ * \_ Loading <--- or here
+ *
+ * Note this function cannot be called on ordinary paths. Use
+ * {!get_pathname} for that.
+ *)
+let rec get_registry_path ({ model = model } as t) row =
+ let hdata = get_hdata t row in
+ let parent = model#iter_parent row in
+
+ match hdata, parent with
+ | { state=IsLeaf; content=(Loading|ErrorMessage _|Info _) }, Some parent ->
+ get_registry_path t parent
+ | { state=IsLeaf; content=(Loading|ErrorMessage _|Info _) }, None ->
+ assert false
+ | { content=RegKey node; hiveh = Some h }, Some parent ->
+ let top, path = get_registry_path t parent in
+ let path = Hivex.node_name h node :: path in
+ top, path
+ | { content=TopWinReg (a,b,c,d) }, None -> (a,b,c,d), []
+ | { content=TopWinReg _ }, _ -> assert false
+ | { content=RegKey _}, _ -> assert false
+ | { content=Top _ }, _ -> assert false
+ | { content=Directory _ }, _ -> assert false
+ | { content=File _ }, _ -> assert false
+ | { content=Loading }, _ -> assert false
+ | { content=ErrorMessage _ }, _ -> assert false
+ | { content=Info _ }, _ -> assert false
+ | { content=RegValue _ }, _ -> assert false
+
+let rec cache_registry_file ?fail t path src remotefile cachefile cb =
+ Slave.download_file_if_not_exist ?fail src remotefile cachefile
+ (when_cached_registry ?fail t path cb)
+
+and when_cached_registry ?fail ({ model = model } as t) path cb () =
+ debug "when_cached_registry";
+ let row = model#get_iter path in
+ let hdata = get_hdata t row in
+
+ match hdata with
+ | { hiveh=Some _; content=TopWinReg _ } ->
+ (* Hive handle already opened. *)
+ cb ()
+
+ | { hiveh=None; content=TopWinReg (src, rootkey, remotefile, cachefile) } ->
+ (* Hive handle not opened, open it and save it in the handle. *)
+ (try
+ let flags = if verbose () then [ Hivex.OPEN_VERBOSE ] else [] in
+ let h = Hivex.open_file cachefile flags in
+ hdata.hiveh <- Some h;
+ cb ()
+ with
+ Hivex.Error _ as exn ->
+ match fail with
+ | Some fail -> fail exn
+ | None -> raise exn
+ )
+ | _ -> assert false