1(* 2 * Copyright (C) 2006-2007 XenSource Ltd. 3 * Copyright (C) 2008 Citrix Ltd. 4 * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com> 5 * Author Thomas Gazagnaire <thomas.gazagnaire@eu.citrix.com> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU Lesser General Public License as published 9 * by the Free Software Foundation; version 2.1 only. with the special 10 * exception on linking described in file LICENSE. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU Lesser General Public License for more details. 16 *) 17open Stdext 18 19module SymbolMap = Map.Make(Symbol) 20 21module Node = struct 22 23 type t = { 24 name: Symbol.t; 25 perms: Perms.Node.t; 26 value: string; 27 children: t SymbolMap.t; 28 } 29 30 let create _name _perms _value = 31 { name = Symbol.of_string _name; perms = _perms; value = _value; children = SymbolMap.empty; } 32 33 let get_owner node = Perms.Node.get_owner node.perms 34 let get_children node = node.children 35 let get_value node = node.value 36 let get_perms node = node.perms 37 let get_name node = Symbol.to_string node.name 38 39 let set_value node nvalue = 40 if node.value = nvalue 41 then node 42 else { node with value = nvalue } 43 44 let set_perms node nperms = { node with perms = nperms } 45 46 let add_child node child = 47 let children = SymbolMap.add child.name child node.children in 48 { node with children } 49 50 let exists node childname = 51 let childname = Symbol.of_string childname in 52 SymbolMap.mem childname node.children 53 54 let find node childname = 55 let childname = Symbol.of_string childname in 56 SymbolMap.find childname node.children 57 58 let replace_child node child nchild = 59 { node with 60 children = SymbolMap.update child.name 61 (function None -> None | Some _ -> Some nchild) 62 node.children 63 } 64 65 let del_childname node childname = 66 let sym = Symbol.of_string childname in 67 { node with children = 68 SymbolMap.update sym 69 (function None -> raise Not_found | Some _ -> None) 70 node.children 71 } 72 73 let del_all_children node = 74 { node with children = SymbolMap.empty } 75 76 (* check if the current node can be accessed by the current connection with rperm permissions *) 77 let check_perm node connection request = 78 Perms.check connection request node.perms 79 80 (* check if the current node is owned by the current connection *) 81 let check_owner node connection = 82 if not (Perms.check_owner connection node.perms) 83 then begin 84 Logging.info "store|node" "Permission denied: Domain %d not owner" (get_owner node); 85 raise Define.Permission_denied; 86 end 87 88 let rec recurse fct node acc = 89 let acc = fct node acc in 90 SymbolMap.fold (fun _ -> recurse fct) node.children acc 91 92 (** [recurse_filter_map f tree] applies [f] on each node in the tree recursively, 93 possibly removing some nodes. 94 Note that the nodes removed this way won't generate watch events. 95 *) 96 let recurse_filter_map f = 97 let invalid = -1 in 98 let is_valid _ node = node.perms.owner <> invalid in 99 let rec walk node = 100 (* Map.filter_map is Ocaml 4.11+ only *) 101 let node = 102 { node with children = 103 SymbolMap.map walk node.children |> SymbolMap.filter is_valid } in 104 match f node with 105 | Some keep -> keep 106 | None -> { node with perms = {node.perms with owner = invalid } } 107 in 108 walk 109 110 let unpack node = (Symbol.to_string node.name, node.perms, node.value) 111 112end 113 114module Path = struct 115 116 (* represent a path in a store. 117 * [] -> "/" 118 * [ "local"; "domain"; "1" ] -> "/local/domain/1" 119 *) 120 type t = string list 121 122 let char_is_valid c = 123 (c >= 'a' && c <= 'z') || 124 (c >= 'A' && c <= 'Z') || 125 (c >= '0' && c <= '9') || 126 c = '_' || c = '-' || c = '@' 127 128 let name_is_valid name = 129 name <> "" && String.fold_left (fun accu c -> accu && char_is_valid c) true name 130 131 let is_valid path = 132 List.for_all name_is_valid path 133 134 let of_string s = 135 if s.[0] = '@' 136 then [s] 137 else if s = "/" 138 then [] 139 else match String.split '/' s with 140 | "" :: path when is_valid path -> path 141 | _ -> raise Define.Invalid_path 142 143 let of_path_and_name path name = 144 match path, name with 145 | [], "" -> [] 146 | _ -> path @ [name] 147 148 let create path connection_path = 149 of_string (Utils.path_validate path connection_path) 150 151 let to_string t = 152 "/" ^ (String.concat "/" t) 153 154 let to_string_list x = x 155 156 let get_parent t = 157 if t = [] then [] else List.rev (List.tl (List.rev t)) 158 159 let get_hierarchy path = 160 Utils.get_hierarchy path 161 162 let get_common_prefix p1 p2 = 163 let rec compare l1 l2 = 164 match l1, l2 with 165 | h1 :: tl1, h2 :: tl2 -> 166 if h1 = h2 then h1 :: (compare tl1 tl2) else [] 167 | _, [] | [], _ -> 168 (* if l1 or l2 is empty, we found the equal part already *) 169 [] 170 in 171 compare p1 p2 172 173 let rec lookup_modify node path fct = 174 match path with 175 | [] -> raise (Define.Invalid_path) 176 | h :: [] -> fct node h 177 | h :: l -> 178 let (n, c) = 179 if not (Node.exists node h) then 180 raise (Define.Lookup_Doesnt_exist h) 181 else 182 (node, Node.find node h) in 183 let nc = lookup_modify c l fct in 184 Node.replace_child n c nc 185 186 let apply_modify rnode path fct = 187 lookup_modify rnode path fct 188 189 let rec lookup_get node path = 190 match path with 191 | [] -> raise (Define.Invalid_path) 192 | h :: [] -> 193 (try 194 Node.find node h 195 with Not_found -> 196 raise Define.Doesnt_exist) 197 | h :: l -> let cnode = Node.find node h in lookup_get cnode l 198 199 let get_node rnode path = 200 if path = [] then 201 Some rnode 202 else ( 203 try Some (lookup_get rnode path) with Define.Doesnt_exist -> None 204 ) 205 206 (* get the deepest existing node for this path, return the node and a flag on the existence of the full path *) 207 let rec get_deepest_existing_node node = function 208 | [] -> node, true 209 | h :: t -> 210 try get_deepest_existing_node (Node.find node h) t 211 with Not_found -> node, false 212 213 let set_node rnode path nnode = 214 if path = [] then 215 nnode 216 else 217 let set_node node name = 218 try 219 let ent = Node.find node name in 220 Node.replace_child node ent nnode 221 with Not_found -> 222 Node.add_child node nnode 223 in 224 apply_modify rnode path set_node 225 226 (* read | ls | getperms use this *) 227 let rec lookup node path fct = 228 match path with 229 | [] -> raise (Define.Invalid_path) 230 | h :: [] -> fct node h 231 | h :: l -> let cnode = Node.find node h in lookup cnode l fct 232 233 let apply rnode path fct = 234 lookup rnode path fct 235 236 let introduce_domain = "@introduceDomain" 237 let release_domain = "@releaseDomain" 238 let specials = List.map of_string [ introduce_domain; release_domain ] 239 240end 241 242(* The Store.t type *) 243type t = 244 { 245 mutable stat_transaction_coalesce: int; 246 mutable stat_transaction_abort: int; 247 mutable root: Node.t; 248 mutable quota: Quota.t; 249 } 250 251let get_root store = store.root 252let set_root store root = store.root <- root 253 254let get_quota store = store.quota 255let set_quota store quota = store.quota <- quota 256 257(* modifying functions *) 258let path_mkdir store perm path = 259 let do_mkdir node name = 260 try 261 let ent = Node.find node name in 262 Node.check_perm ent perm Perms.WRITE; 263 raise Define.Already_exist 264 with Not_found -> 265 Node.check_perm node perm Perms.WRITE; 266 Node.add_child node (Node.create name node.Node.perms "") in 267 if path = [] then 268 store.root 269 else 270 Path.apply_modify store.root path do_mkdir 271 272let path_write store perm path value = 273 let node_created = ref false in 274 let do_write node name = 275 try 276 let ent = Node.find node name in 277 Node.check_perm ent perm Perms.WRITE; 278 let nent = Node.set_value ent value in 279 Node.replace_child node ent nent 280 with Not_found -> 281 node_created := true; 282 Node.check_perm node perm Perms.WRITE; 283 Node.add_child node (Node.create name node.Node.perms value) in 284 if path = [] then ( 285 Node.check_perm store.root perm Perms.WRITE; 286 Node.set_value store.root value, false 287 ) else 288 let root = Path.apply_modify store.root path do_write in 289 root, !node_created 290 291let path_rm store perm path = 292 let do_rm node name = 293 try 294 let ent = Node.find node name in 295 Node.check_perm ent perm Perms.WRITE; 296 Node.del_childname node name 297 with Not_found -> 298 raise Define.Doesnt_exist in 299 if path = [] then ( 300 Node.check_perm store.root perm Perms.WRITE; 301 Node.del_all_children store.root 302 ) else 303 Path.apply_modify store.root path do_rm 304 305let path_setperms store perm path perms = 306 if path = [] then ( 307 Node.check_perm store.root perm Perms.WRITE; 308 Node.set_perms store.root perms 309 ) else 310 let do_setperms node name = 311 let c = Node.find node name in 312 Node.check_owner c perm; 313 Node.check_perm c perm Perms.WRITE; 314 let nc = Node.set_perms c perms in 315 Node.replace_child node c nc 316 in 317 Path.apply_modify store.root path do_setperms 318 319(* accessing functions *) 320let get_node store path = 321 Path.get_node store.root path 322 323let get_deepest_existing_node store path = 324 Path.get_deepest_existing_node store.root path 325 326let read store perm path = 327 let do_read node name = 328 let ent = Node.find node name in 329 Node.check_perm ent perm Perms.READ; 330 ent.Node.value 331 in 332 if path = [] then ( 333 let ent = store.root in 334 Node.check_perm ent perm Perms.READ; 335 ent.Node.value 336 ) else 337 Path.apply store.root path do_read 338 339let ls store perm path = 340 let children = 341 if path = [] then ( 342 Node.check_perm store.root perm Perms.READ; 343 Node.get_children store.root 344 ) else 345 let do_ls node name = 346 let cnode = Node.find node name in 347 Node.check_perm cnode perm Perms.READ; 348 cnode.Node.children in 349 Path.apply store.root path do_ls in 350 SymbolMap.fold (fun k _ accu -> Symbol.to_string k :: accu) children [] 351 352let getperms store perm path = 353 if path = [] then ( 354 Node.check_perm store.root perm Perms.READ; 355 Node.get_perms store.root 356 ) else 357 let fct n name = 358 let c = Node.find n name in 359 Node.check_perm c perm Perms.READ; 360 c.Node.perms in 361 Path.apply store.root path fct 362 363let path_exists store path = 364 if path = [] then 365 true 366 else 367 try 368 let check_exist node name = 369 ignore(Node.find node name); 370 true in 371 Path.apply store.root path check_exist 372 with Not_found -> false 373 374 375(* others utils *) 376let traversal root_node f = 377 let rec _traversal path node = 378 f path node; 379 let node_path = Path.of_path_and_name path (Symbol.to_string node.Node.name) in 380 SymbolMap.iter (fun _ -> _traversal node_path) node.Node.children 381 in 382 _traversal [] root_node 383 384let dump_store_buf root_node = 385 let buf = Buffer.create 8192 in 386 let dump_node path node = 387 let pathstr = String.concat "/" path in 388 Printf.bprintf buf "%s/%s{%s}" pathstr (Symbol.to_string node.Node.name) 389 (String.escaped (Perms.Node.to_string (Node.get_perms node))); 390 if String.length node.Node.value > 0 then 391 Printf.bprintf buf " = %s\n" (String.escaped node.Node.value) 392 else 393 Printf.bprintf buf "\n"; 394 in 395 traversal root_node dump_node; 396 buf 397 398let dump_store chan root_node = 399 let buf = dump_store_buf root_node in 400 output_string chan (Buffer.contents buf); 401 Buffer.reset buf 402 403let dump_fct store f = traversal store.root f 404let dump store out_chan = dump_store out_chan store.root 405let dump_stdout store = dump_store stdout store.root 406let dump_buffer store = dump_store_buf store.root 407 408 409(* modifying functions with quota udpate *) 410let set_node store path node orig_quota mod_quota = 411 let root = Path.set_node store.root path node in 412 store.root <- root; 413 store.quota <- Quota.merge orig_quota mod_quota store.quota 414 415let write store perm path value = 416 let node, existing = get_deepest_existing_node store path in 417 let owner = Node.get_owner node in 418 if existing || (Perms.Connection.is_dom0 perm) then 419 (* Only check the string length limit *) 420 Quota.check store.quota (-1) (String.length value) 421 else 422 (* Check the domain entries limit too *) 423 Quota.check store.quota owner (String.length value); 424 let root, node_created = path_write store perm path value in 425 store.root <- root; 426 if node_created 427 then store.quota <- Quota.add_entry store.quota owner 428 429let mkdir store perm path = 430 let node, existing = get_deepest_existing_node store path in 431 let owner = Node.get_owner node in 432 (* It's upt to the mkdir logic to decide what to do with existing path *) 433 if not (existing || (Perms.Connection.is_dom0 perm)) then Quota.check store.quota owner 0; 434 store.root <- path_mkdir store perm path; 435 if not existing then 436 store.quota <- Quota.add_entry store.quota owner 437 438let rm store perm path = 439 let rmed_node = Path.get_node store.root path in 440 match rmed_node with 441 | None -> raise Define.Doesnt_exist 442 | Some rmed_node -> 443 store.root <- path_rm store perm path; 444 store.quota <- Node.recurse (fun node quota -> Quota.del_entry quota (Node.get_owner node)) rmed_node store.quota 445 446let setperms store perm path nperms = 447 match Path.get_node store.root path with 448 | None -> raise Define.Doesnt_exist 449 | Some node -> 450 let old_owner = Node.get_owner node in 451 let new_owner = Perms.Node.get_owner nperms in 452 if not ((old_owner = new_owner) || (Perms.Connection.is_dom0 perm)) then 453 raise Define.Permission_denied; 454 store.root <- path_setperms store perm path nperms; 455 store.quota <- 456 let quota = Quota.del_entry store.quota old_owner in 457 Quota.add_entry quota new_owner 458 459let reset_permissions store domid = 460 Logging.info "store|node" "Cleaning up xenstore ACLs for domid %d" domid; 461 store.root <- Node.recurse_filter_map (fun node -> 462 match Perms.Node.remove_domid ~domid node.perms with 463 | None -> None 464 | Some perms -> 465 if perms <> node.perms then 466 Logging.debug "store|node" "Changed permissions for node %s" (Node.get_name node); 467 Some { node with Node.perms } 468 ) store.root 469 470type ops = { 471 store: t; 472 write: Path.t -> string -> unit; 473 mkdir: Path.t -> unit; 474 rm: Path.t -> unit; 475 setperms: Path.t -> Perms.Node.t -> unit; 476 ls: Path.t -> string list; 477 read: Path.t -> string; 478 getperms: Path.t -> Perms.Node.t; 479 path_exists: Path.t -> bool; 480} 481 482let get_ops store perms = { 483 store = store; 484 write = write store perms; 485 mkdir = mkdir store perms; 486 rm = rm store perms; 487 setperms = setperms store perms; 488 ls = ls store perms; 489 read = read store perms; 490 getperms = getperms store perms; 491 path_exists = path_exists store; 492} 493 494let create () = { 495 stat_transaction_coalesce = 0; 496 stat_transaction_abort = 0; 497 root = Node.create "" Perms.Node.default0 ""; 498 quota = Quota.create (); 499} 500let copy store = { 501 stat_transaction_coalesce = store.stat_transaction_coalesce; 502 stat_transaction_abort = store.stat_transaction_abort; 503 root = store.root; 504 quota = Quota.copy store.quota; 505} 506 507let incr_transaction_coalesce store = 508 store.stat_transaction_coalesce <- store.stat_transaction_coalesce + 1 509let incr_transaction_abort store = 510 store.stat_transaction_abort <- store.stat_transaction_abort + 1 511 512let stats store = 513 let nb_nodes = ref 0 in 514 traversal store.root (fun _path _node -> 515 incr nb_nodes 516 ); 517 !nb_nodes, store.stat_transaction_abort, store.stat_transaction_coalesce 518