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