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