smbd: Protect ea-reading on symlinks
[Samba.git] / source3 / lib / adt_tree.c
blob5c2b2667cf49b363341abcbeb7523d4dba6c1303
1 /*
2 * Unix SMB/CIFS implementation.
3 * Generic Abstract Data Types
4 * Copyright (C) Gerald Carter 2002.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
20 #include "replace.h"
21 #include <talloc.h>
22 #include "lib/util/debug.h"
23 #include "lib/util/samba_util.h"
24 #include "util/charset/charset.h"
25 #include "source3/include/smb_macros.h"
26 #include "adt_tree.h"
28 struct tree_node {
29 struct tree_node *parent;
30 struct tree_node **children;
31 int num_children;
32 char *key;
33 void *data_p;
36 struct sorted_tree {
37 struct tree_node *root;
40 /**************************************************************************
41 *************************************************************************/
43 static bool trim_tree_keypath( char *path, char **base, char **new_path )
45 char *p;
47 *new_path = *base = NULL;
49 if ( !path )
50 return false;
52 *base = path;
54 p = strchr( path, '\\' );
56 if ( p ) {
57 *p = '\0';
58 *new_path = p+1;
61 return true;
64 /**************************************************************************
65 Initialize the tree's root.
66 *************************************************************************/
68 struct sorted_tree *pathtree_init(void *data_p)
70 struct sorted_tree *tree = NULL;
72 tree = talloc_zero(NULL, struct sorted_tree);
73 if (tree == NULL) {
74 return NULL;
77 tree->root = talloc_zero(tree, struct tree_node);
78 if (tree->root == NULL) {
79 TALLOC_FREE( tree );
80 return NULL;
83 tree->root->data_p = data_p;
85 return tree;
89 /**************************************************************************
90 Find the next child given a key string
91 *************************************************************************/
93 static struct tree_node *pathtree_birth_child(struct tree_node *node,
94 char* key )
96 struct tree_node *infant = NULL;
97 struct tree_node **siblings;
98 int i;
100 infant = talloc_zero(node, struct tree_node);
101 if (infant == NULL) {
102 return NULL;
105 infant->key = talloc_strdup( infant, key );
106 infant->parent = node;
108 siblings = talloc_realloc(node, node->children, struct tree_node *,
109 node->num_children+1);
111 if ( siblings )
112 node->children = siblings;
114 node->num_children++;
116 /* first child */
118 if ( node->num_children == 1 ) {
119 DEBUG(11,("pathtree_birth_child: First child of node [%s]! [%s]\n",
120 node->key ? node->key : "NULL", infant->key ));
121 node->children[0] = infant;
123 else
126 * multiple siblings .... (at least 2 children)
128 * work from the end of the list forward
129 * The last child is not set at this point
130 * Insert the new infanct in ascending order
131 * from left to right
134 for ( i = node->num_children-1; i>=1; i-- )
136 DEBUG(11,("pathtree_birth_child: Looking for crib; infant -> [%s], child -> [%s]\n",
137 infant->key, node->children[i-1]->key));
139 /* the strings should never match assuming that we
140 have called pathtree_find_child() first */
142 if ( strcasecmp_m( infant->key, node->children[i-1]->key ) > 0 ) {
143 DEBUG(11,("pathtree_birth_child: storing infant in i == [%d]\n",
144 i));
145 node->children[i] = infant;
146 break;
149 /* bump everything towards the end on slot */
151 node->children[i] = node->children[i-1];
154 DEBUG(11,("pathtree_birth_child: Exiting loop (i == [%d])\n", i ));
156 /* if we haven't found the correct slot yet, the child
157 will be first in the list */
159 if ( i == 0 )
160 node->children[0] = infant;
163 return infant;
166 /**************************************************************************
167 Find the next child given a key string
168 *************************************************************************/
170 static struct tree_node *pathtree_find_child(struct tree_node *node,
171 char *key )
173 struct tree_node *next = NULL;
174 int i, result;
176 if ( !node ) {
177 DEBUG(0,("pathtree_find_child: NULL node passed into function!\n"));
178 return NULL;
181 if ( !key ) {
182 DEBUG(0,("pathtree_find_child: NULL key string passed into function!\n"));
183 return NULL;
186 for ( i=0; i<node->num_children; i++ )
188 DEBUG(11,("pathtree_find_child: child key => [%s]\n",
189 node->children[i]->key));
191 result = strcasecmp_m( node->children[i]->key, key );
193 if ( result == 0 )
194 next = node->children[i];
196 /* if result > 0 then we've gone to far because
197 the list of children is sorted by key name
198 If result == 0, then we have a match */
200 if ( result > 0 )
201 break;
204 DEBUG(11,("pathtree_find_child: %s [%s]\n",
205 next ? "Found" : "Did not find", key ));
207 return next;
210 /**************************************************************************
211 Add a new node into the tree given a key path and a blob of data
212 *************************************************************************/
214 bool pathtree_add(struct sorted_tree *tree, const char *path, void *data_p)
216 char *str, *base, *path2;
217 struct tree_node *current, *next;
218 bool ret = true;
220 DEBUG(8,("pathtree_add: Enter\n"));
222 if ( !path || *path != '\\' ) {
223 DEBUG(0,("pathtree_add: Attempt to add a node with a bad path [%s]\n",
224 path ? path : "NULL" ));
225 return false;
228 if ( !tree ) {
229 DEBUG(0,("pathtree_add: Attempt to add a node to an uninitialized tree!\n"));
230 return false;
233 /* move past the first '\\' */
235 path++;
236 path2 = SMB_STRDUP( path );
237 if ( !path2 ) {
238 DEBUG(0,("pathtree_add: strdup() failed on string [%s]!?!?!\n", path));
239 return false;
244 * this works sort of like a 'mkdir -p' call, possibly
245 * creating an entire path to the new node at once
246 * The path should be of the form /<key1>/<key2>/...
249 base = path2;
250 str = path2;
251 current = tree->root;
253 do {
254 /* break off the remaining part of the path */
256 str = strchr( str, '\\' );
257 if ( str )
258 *str = '\0';
260 /* iterate to the next child--birth it if necessary */
262 next = pathtree_find_child( current, base );
263 if ( !next ) {
264 next = pathtree_birth_child( current, base );
265 if ( !next ) {
266 DEBUG(0,("pathtree_add: Failed to create new child!\n"));
267 ret = false;
268 goto done;
271 current = next;
273 /* setup the next part of the path */
275 base = str;
276 if ( base ) {
277 *base = '\\';
278 base++;
279 str = base;
282 } while ( base != NULL );
284 current->data_p = data_p;
286 DEBUG(10,("pathtree_add: Successfully added node [%s] to tree\n",
287 path ));
289 DEBUG(8,("pathtree_add: Exit\n"));
291 done:
292 SAFE_FREE( path2 );
293 return ret;
297 /**************************************************************************
298 Recursive routine to print out all children of a struct tree_node
299 *************************************************************************/
301 static void pathtree_print_children(TALLOC_CTX *ctx,
302 struct tree_node *node,
303 int debug,
304 const char *path )
306 int i;
307 int num_children;
308 char *path2 = NULL;
310 if ( !node )
311 return;
313 if ( node->key )
314 DEBUG(debug,("%s: [%s] (%s)\n", path ? path : "NULL", node->key,
315 node->data_p ? "data" : "NULL" ));
317 if ( path ) {
318 path2 = talloc_strdup(ctx, path);
319 if (!path2) {
320 return;
324 path2 = talloc_asprintf(ctx,
325 "%s%s/",
326 path ? path : "",
327 node->key ? node->key : "NULL");
328 if (!path2) {
329 return;
332 num_children = node->num_children;
333 for ( i=0; i<num_children; i++ ) {
334 pathtree_print_children(ctx, node->children[i], debug, path2 );
338 /**************************************************************************
339 Dump the kys for a tree to the log file
340 *************************************************************************/
342 void pathtree_print_keys(struct sorted_tree *tree, int debug )
344 int i;
345 int num_children = tree->root->num_children;
347 if ( tree->root->key )
348 DEBUG(debug,("ROOT/: [%s] (%s)\n", tree->root->key,
349 tree->root->data_p ? "data" : "NULL" ));
351 for ( i=0; i<num_children; i++ ) {
352 TALLOC_CTX *ctx = talloc_stackframe();
353 pathtree_print_children(ctx, tree->root->children[i], debug,
354 tree->root->key ? tree->root->key : "ROOT/" );
355 TALLOC_FREE(ctx);
360 /**************************************************************************
361 return the data_p for for the node in tree matching the key string
362 The key string is the full path. We must break it apart and walk
363 the tree
364 *************************************************************************/
366 void* pathtree_find(struct sorted_tree *tree, char *key )
368 char *keystr, *base = NULL, *str = NULL, *p;
369 struct tree_node *current;
370 void *result = NULL;
372 DEBUG(10,("pathtree_find: Enter [%s]\n", key ? key : "NULL" ));
374 /* sanity checks first */
376 if ( !key ) {
377 DEBUG(0,("pathtree_find: Attempt to search tree using NULL search string!\n"));
378 return NULL;
381 if ( !tree ) {
382 DEBUG(0,("pathtree_find: Attempt to search an uninitialized tree using string [%s]!\n",
383 key ? key : "NULL" ));
384 return NULL;
387 if ( !tree->root )
388 return NULL;
390 /* make a copy to play with */
392 if ( *key == '\\' )
393 keystr = SMB_STRDUP( key+1 );
394 else
395 keystr = SMB_STRDUP( key );
397 if ( !keystr ) {
398 DEBUG(0,("pathtree_find: strdup() failed on string [%s]!?!?!\n", key));
399 return NULL;
402 /* start breaking the path apart */
404 p = keystr;
405 current = tree->root;
407 if ( tree->root->data_p )
408 result = tree->root->data_p;
412 /* break off the remaining part of the path */
414 trim_tree_keypath( p, &base, &str );
416 DEBUG(11,("pathtree_find: [loop] base => [%s], new_path => [%s]\n",
417 base ? base : "",
418 str ? str : ""));
420 /* iterate to the next child */
422 current = pathtree_find_child( current, base );
425 * the idea is that the data_p for a parent should
426 * be inherited by all children, but allow it to be
427 * overridden farther down
430 if ( current && current->data_p )
431 result = current->data_p;
433 /* reset the path pointer 'p' to the remaining part of the key string */
435 p = str;
437 } while ( str && current );
439 /* result should be the data_p from the lowest match node in the tree */
440 if ( result )
441 DEBUG(11,("pathtree_find: Found data_p!\n"));
443 SAFE_FREE( keystr );
445 DEBUG(10,("pathtree_find: Exit\n"));
447 return result;