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