s4:subtree_delete - Make the initialisation of the child counter more clear
[ira/wip.git] / source4 / dsdb / samdb / ldb_modules / subtree_delete.c
1 /* 
2    ldb database library
3
4    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006-2007
5    Copyright (C) Stefan Metzmacher <metze@samba.org> 2007
6    Copyright (C) Simo Sorce <idra@samba.org> 2008
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 /*
23  *  Name: ldb
24  *
25  *  Component: ldb subtree delete (prevention) module
26  *
27  *  Description: Prevent deletion of a subtree in LDB
28  *
29  *  Author: Andrew Bartlett
30  */
31
32 #include "ldb_module.h"
33
34 struct subtree_delete_context {
35         struct ldb_module *module;
36         struct ldb_request *req;
37
38         int num_children;
39 };
40
41 static struct subtree_delete_context *subdel_ctx_init(struct ldb_module *module,
42                                                       struct ldb_request *req)
43 {
44         struct ldb_context *ldb;
45         struct subtree_delete_context *ac;
46
47         ldb = ldb_module_get_ctx(module);
48
49         ac = talloc_zero(req, struct subtree_delete_context);
50         if (ac == NULL) {
51                 ldb_oom(ldb);
52                 return NULL;
53         }
54
55         ac->module = module;
56         ac->req = req;
57
58         ac->num_children = 0;
59
60         return ac;
61 }
62
63 static int subtree_delete_search_callback(struct ldb_request *req,
64                                           struct ldb_reply *ares)
65 {
66         struct ldb_context *ldb;
67         struct subtree_delete_context *ac;
68         int ret;
69
70         ac = talloc_get_type(req->context, struct subtree_delete_context);
71         ldb = ldb_module_get_ctx(ac->module);
72
73         if (!ares) {
74                 ret = LDB_ERR_OPERATIONS_ERROR;
75                 goto done;
76         }
77         if (ares->error != LDB_SUCCESS) {
78                 return ldb_module_done(ac->req, ares->controls,
79                                         ares->response, ares->error);
80         }
81
82         switch (ares->type) {
83         case LDB_REPLY_ENTRY:
84                 /* count entry */
85                 ++(ac->num_children);
86
87                 talloc_free(ares);
88                 ret = LDB_SUCCESS;
89                 break;
90
91         case LDB_REPLY_REFERRAL:
92                 /* ignore */
93                 talloc_free(ares);
94                 ret = LDB_SUCCESS;
95                 break;
96
97         case LDB_REPLY_DONE:
98                 talloc_free(ares);
99
100                 if (ac->num_children > 0) {
101                         ldb_asprintf_errstring(ldb,
102                                 "Cannot delete %s, not a leaf node "
103                                 "(has %d children)\n",
104                                 ldb_dn_get_linearized(ac->req->op.del.dn),
105                                 ac->num_children);
106                         return ldb_module_done(ac->req, NULL, NULL,
107                                                LDB_ERR_NOT_ALLOWED_ON_NON_LEAF);
108                 }
109
110                 /* ok no children, let the original request through */
111                 ret = ldb_next_request(ac->module, ac->req);
112                 break;
113         }
114
115 done:
116         if (ret != LDB_SUCCESS) {
117                 return ldb_module_done(ac->req, NULL, NULL, ret);
118         }
119
120         return LDB_SUCCESS;
121 }
122
123 static int subtree_delete(struct ldb_module *module, struct ldb_request *req)
124 {
125         struct ldb_context *ldb;
126         static const char * const attrs[2] = { "distinguishedName", NULL };
127         struct ldb_request *search_req;
128         struct subtree_delete_context *ac;
129         int ret;
130
131         if (ldb_dn_is_special(req->op.rename.olddn)) {
132                 /* do not manipulate our control entries */
133                 return ldb_next_request(module, req);
134         }
135
136         ldb = ldb_module_get_ctx(module);
137
138         /* This gets complex:  We need to:
139            - Do a search for all entires under this entry 
140            - Wait for these results to appear
141            - In the callback for each result, count the children (if any)
142            - return an error if there are any
143         */
144
145         ac = subdel_ctx_init(module, req);
146         if (!ac) {
147                 return LDB_ERR_OPERATIONS_ERROR;
148         }
149
150         /* we do not really need to find all descendents,
151          * if there is even one single direct child, that's
152          * enough to bail out */
153         ret = ldb_build_search_req(&search_req, ldb, ac,
154                                    req->op.del.dn, LDB_SCOPE_ONELEVEL,
155                                    "(objectClass=*)", attrs,
156                                    req->controls,
157                                    ac, subtree_delete_search_callback,
158                                    req);
159         if (ret != LDB_SUCCESS) {
160                 return ret;
161         }
162
163         return ldb_next_request(module, search_req);
164 }
165
166 const struct ldb_module_ops ldb_subtree_delete_module_ops = {
167         .name              = "subtree_delete",
168         .del               = subtree_delete,
169 };