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