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