/src/samba/lib/util/server_id_db.c
Line | Count | Source |
1 | | /* |
2 | | * Map names to server_ids |
3 | | * |
4 | | * Copyright Volker Lendecke <vl@samba.org> 2014 |
5 | | * |
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. |
10 | | * |
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. |
15 | | * |
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/>. |
18 | | */ |
19 | | |
20 | | #include "replace.h" |
21 | | #include "system/filesys.h" |
22 | | #include "lib/util/server_id.h" |
23 | | #include "lib/util/server_id_db.h" |
24 | | #include "lib/tdb_wrap/tdb_wrap.h" |
25 | | #include "lib/util/strv.h" |
26 | | #include "lib/util/util_tdb.h" |
27 | | #include "lib/util/samba_util.h" |
28 | | |
29 | | static TDB_DATA talloc_tdb_data(void *ptr) |
30 | 0 | { |
31 | 0 | return (TDB_DATA) { .dptr = ptr, .dsize = talloc_get_size(ptr) }; |
32 | 0 | } |
33 | | |
34 | | struct server_id_db { |
35 | | struct server_id pid; |
36 | | struct tdb_wrap *tdb; |
37 | | char *names; |
38 | | }; |
39 | | |
40 | | static int server_id_db_destructor(struct server_id_db *db); |
41 | | |
42 | | struct server_id_db *server_id_db_init(TALLOC_CTX *mem_ctx, |
43 | | struct server_id pid, |
44 | | const char *base_path, |
45 | | int hash_size, int tdb_flags) |
46 | 0 | { |
47 | 0 | struct server_id_db *db; |
48 | 0 | size_t pathlen = strlen(base_path) + 11; |
49 | 0 | char path[pathlen]; |
50 | |
|
51 | 0 | db = talloc(mem_ctx, struct server_id_db); |
52 | 0 | if (db == NULL) { |
53 | 0 | return NULL; |
54 | 0 | } |
55 | 0 | db->pid = pid; |
56 | 0 | db->names = NULL; |
57 | |
|
58 | 0 | snprintf(path, pathlen, "%s/names.tdb", base_path); |
59 | |
|
60 | 0 | db->tdb = tdb_wrap_open(db, path, hash_size, tdb_flags, |
61 | 0 | O_RDWR|O_CREAT, 0660); |
62 | 0 | if (db->tdb == NULL) { |
63 | 0 | TALLOC_FREE(db); |
64 | 0 | return NULL; |
65 | 0 | } |
66 | | |
67 | 0 | talloc_set_destructor(db, server_id_db_destructor); |
68 | |
|
69 | 0 | return db; |
70 | 0 | } |
71 | | |
72 | | void server_id_db_reinit(struct server_id_db *db, struct server_id pid) |
73 | 0 | { |
74 | 0 | db->pid = pid; |
75 | 0 | TALLOC_FREE(db->names); |
76 | 0 | } |
77 | | |
78 | | struct server_id server_id_db_pid(struct server_id_db *db) |
79 | 0 | { |
80 | 0 | return db->pid; |
81 | 0 | } |
82 | | |
83 | | static int server_id_db_destructor(struct server_id_db *db) |
84 | 0 | { |
85 | 0 | char *name = NULL; |
86 | |
|
87 | 0 | while ((name = strv_next(db->names, name)) != NULL) { |
88 | 0 | server_id_db_remove(db, name); |
89 | 0 | } |
90 | |
|
91 | 0 | return 0; |
92 | 0 | } |
93 | | |
94 | | int server_id_db_add(struct server_id_db *db, const char *name) |
95 | 0 | { |
96 | 0 | struct tdb_context *tdb = db->tdb->tdb; |
97 | 0 | TDB_DATA key; |
98 | 0 | char *n; |
99 | 0 | int ret; |
100 | |
|
101 | 0 | n = strv_find(db->names, name); |
102 | 0 | if (n != NULL) { |
103 | 0 | return EEXIST; |
104 | 0 | } |
105 | | |
106 | 0 | ret = strv_add(db, &db->names, name); |
107 | 0 | if (ret != 0) { |
108 | 0 | return ret; |
109 | 0 | } |
110 | | |
111 | 0 | key = string_term_tdb_data(name); |
112 | |
|
113 | 0 | { |
114 | 0 | struct server_id_buf _buf; |
115 | 0 | char *idbuf = server_id_str_buf_unique(db->pid, &_buf); |
116 | 0 | size_t idlen = strlen(idbuf) + 1; |
117 | |
|
118 | 0 | ret = tdb_append( |
119 | 0 | tdb, key, |
120 | 0 | (TDB_DATA) { .dptr = (uint8_t *)idbuf, .dsize = idlen }); |
121 | 0 | } |
122 | |
|
123 | 0 | if (ret != 0) { |
124 | 0 | enum TDB_ERROR err = tdb_error(tdb); |
125 | 0 | strv_delete(&db->names, strv_find(db->names, name)); |
126 | 0 | return map_unix_error_from_tdb(err); |
127 | 0 | } |
128 | | |
129 | 0 | return 0; |
130 | 0 | } |
131 | | |
132 | | int server_id_db_prune_name(struct server_id_db *db, const char *name, |
133 | | struct server_id server) |
134 | 0 | { |
135 | 0 | struct tdb_context *tdb = db->tdb->tdb; |
136 | 0 | struct server_id_buf _buf; |
137 | 0 | char *idbuf = server_id_str_buf_unique(server, &_buf); |
138 | 0 | TDB_DATA key; |
139 | 0 | uint8_t *data; |
140 | 0 | size_t datalen; |
141 | 0 | char *ids, *id; |
142 | 0 | int ret; |
143 | |
|
144 | 0 | key = string_term_tdb_data(name); |
145 | |
|
146 | 0 | ret = tdb_chainlock(tdb, key); |
147 | 0 | if (ret == -1) { |
148 | 0 | enum TDB_ERROR err = tdb_error(tdb); |
149 | 0 | return map_unix_error_from_tdb(err); |
150 | 0 | } |
151 | | |
152 | 0 | ret = tdb_fetch_talloc(tdb, key, db, &data); |
153 | 0 | if (ret != 0) { |
154 | 0 | tdb_chainunlock(tdb, key); |
155 | 0 | return ret; |
156 | 0 | } |
157 | | |
158 | 0 | datalen = talloc_get_size(data); |
159 | 0 | if ((datalen == 0) || (data[datalen-1] != '\0')) { |
160 | 0 | tdb_chainunlock(tdb, key); |
161 | 0 | TALLOC_FREE(data); |
162 | 0 | return EINVAL; |
163 | 0 | } |
164 | | |
165 | 0 | ids = (char *)data; |
166 | |
|
167 | 0 | id = strv_find(ids, idbuf); |
168 | 0 | if (id == NULL) { |
169 | 0 | tdb_chainunlock(tdb, key); |
170 | 0 | TALLOC_FREE(data); |
171 | 0 | return ENOENT; |
172 | 0 | } |
173 | | |
174 | 0 | strv_delete(&ids, id); |
175 | |
|
176 | 0 | if (talloc_get_size(ids) == 0) { |
177 | 0 | ret = tdb_delete(tdb, key); |
178 | 0 | } else { |
179 | 0 | ret = tdb_store(tdb, key, talloc_tdb_data(ids), TDB_MODIFY); |
180 | 0 | } |
181 | 0 | TALLOC_FREE(data); |
182 | |
|
183 | 0 | tdb_chainunlock(tdb, key); |
184 | |
|
185 | 0 | if (ret == -1) { |
186 | 0 | enum TDB_ERROR err = tdb_error(tdb); |
187 | 0 | return map_unix_error_from_tdb(err); |
188 | 0 | } |
189 | | |
190 | 0 | return 0; |
191 | 0 | } |
192 | | |
193 | | int server_id_db_remove(struct server_id_db *db, const char *name) |
194 | 0 | { |
195 | 0 | char *n; |
196 | 0 | int ret; |
197 | |
|
198 | 0 | n = strv_find(db->names, name); |
199 | 0 | if (n == NULL) { |
200 | 0 | return ENOENT; |
201 | 0 | } |
202 | | |
203 | 0 | ret = server_id_db_prune_name(db, name, db->pid); |
204 | 0 | if (ret != 0) { |
205 | 0 | return ret; |
206 | 0 | } |
207 | | |
208 | 0 | strv_delete(&db->names, n); |
209 | 0 | return 0; |
210 | 0 | } |
211 | | |
212 | | int server_id_db_lookup(struct server_id_db *db, const char *name, |
213 | | TALLOC_CTX *mem_ctx, unsigned *pnum_servers, |
214 | | struct server_id **pservers) |
215 | 0 | { |
216 | 0 | struct tdb_context *tdb = db->tdb->tdb; |
217 | 0 | TDB_DATA key; |
218 | 0 | uint8_t *data; |
219 | 0 | size_t datalen; |
220 | 0 | char *ids, *id; |
221 | 0 | unsigned num_servers; |
222 | 0 | struct server_id *servers; |
223 | 0 | int i, ret; |
224 | |
|
225 | 0 | key = string_term_tdb_data(name); |
226 | |
|
227 | 0 | ret = tdb_fetch_talloc(tdb, key, mem_ctx, &data); |
228 | 0 | if (ret != 0) { |
229 | 0 | return ret; |
230 | 0 | } |
231 | | |
232 | 0 | datalen = talloc_get_size(data); |
233 | 0 | if ((datalen == 0) || (data[datalen-1] != '\0')) { |
234 | 0 | TALLOC_FREE(data); |
235 | 0 | return EINVAL; |
236 | 0 | } |
237 | | |
238 | 0 | ids = (char *)data; |
239 | 0 | num_servers = strv_count(ids); |
240 | |
|
241 | 0 | servers = talloc_array(mem_ctx, struct server_id, num_servers); |
242 | 0 | if (servers == NULL) { |
243 | 0 | TALLOC_FREE(data); |
244 | 0 | return ENOMEM; |
245 | 0 | } |
246 | | |
247 | 0 | i = 0; |
248 | |
|
249 | 0 | for (id = ids; id != NULL; id = strv_next(ids, id)) { |
250 | 0 | servers[i++] = server_id_from_string(NONCLUSTER_VNN, id); |
251 | 0 | } |
252 | |
|
253 | 0 | TALLOC_FREE(data); |
254 | |
|
255 | 0 | *pnum_servers = num_servers; |
256 | 0 | *pservers = servers; |
257 | |
|
258 | 0 | return 0; |
259 | 0 | } |
260 | | |
261 | | bool server_id_db_lookup_one(struct server_id_db *db, const char *name, |
262 | | struct server_id *server) |
263 | 0 | { |
264 | 0 | int ret; |
265 | 0 | unsigned num_servers; |
266 | 0 | struct server_id *servers; |
267 | |
|
268 | 0 | ret = server_id_db_lookup(db, name, db, &num_servers, &servers); |
269 | 0 | if (ret != 0) { |
270 | 0 | return false; |
271 | 0 | } |
272 | 0 | if (num_servers == 0) { |
273 | 0 | TALLOC_FREE(servers); |
274 | 0 | return false; |
275 | 0 | } |
276 | 0 | *server = servers[0]; |
277 | 0 | TALLOC_FREE(servers); |
278 | 0 | return true; |
279 | 0 | } |
280 | | |
281 | | struct server_id_db_traverse_state { |
282 | | TALLOC_CTX *mem_ctx; |
283 | | int (*fn)(const char *name, |
284 | | unsigned num_servers, |
285 | | const struct server_id *servers, |
286 | | void *private_data); |
287 | | void *private_data; |
288 | | }; |
289 | | |
290 | | static int server_id_db_traverse_fn(struct tdb_context *tdb, |
291 | | TDB_DATA key, TDB_DATA data, |
292 | | void *private_data) |
293 | 0 | { |
294 | 0 | struct server_id_db_traverse_state *state = private_data; |
295 | 0 | const char *name; |
296 | 0 | char *ids, *id; |
297 | 0 | unsigned num_servers; |
298 | 0 | struct server_id *servers; |
299 | 0 | int i, ret; |
300 | |
|
301 | 0 | if (key.dsize == 0) { |
302 | 0 | return 0; |
303 | 0 | } |
304 | 0 | if (key.dptr[key.dsize-1] != '\0') { |
305 | 0 | return 0; |
306 | 0 | } |
307 | 0 | name = (const char *)key.dptr; |
308 | |
|
309 | 0 | ids = (char *)talloc_memdup(state->mem_ctx, data.dptr, data.dsize); |
310 | 0 | if (ids == NULL) { |
311 | 0 | return 0; |
312 | 0 | } |
313 | | |
314 | 0 | num_servers = strv_count(ids); |
315 | 0 | servers = talloc_array(ids, struct server_id, num_servers); |
316 | |
|
317 | 0 | i = 0; |
318 | |
|
319 | 0 | for (id = ids; id != NULL; id = strv_next(ids, id)) { |
320 | 0 | servers[i++] = server_id_from_string(NONCLUSTER_VNN, id); |
321 | 0 | } |
322 | |
|
323 | 0 | ret = state->fn(name, num_servers, servers, state->private_data); |
324 | |
|
325 | 0 | TALLOC_FREE(ids); |
326 | |
|
327 | 0 | return ret; |
328 | 0 | } |
329 | | |
330 | | int server_id_db_traverse_read(struct server_id_db *db, |
331 | | int (*fn)(const char *name, |
332 | | unsigned num_servers, |
333 | | const struct server_id *servers, |
334 | | void *private_data), |
335 | | void *private_data) |
336 | 0 | { |
337 | 0 | struct server_id_db_traverse_state state; |
338 | 0 | int ret; |
339 | |
|
340 | 0 | state = (struct server_id_db_traverse_state) { |
341 | 0 | .fn = fn, .private_data = private_data, |
342 | 0 | .mem_ctx = talloc_new(db) |
343 | 0 | }; |
344 | |
|
345 | 0 | if (state.mem_ctx == NULL) { |
346 | 0 | return ENOMEM; |
347 | 0 | } |
348 | | |
349 | 0 | ret = tdb_traverse_read(db->tdb->tdb, server_id_db_traverse_fn, |
350 | 0 | &state); |
351 | | TALLOC_FREE(state.mem_ctx); |
352 | 0 | return ret; |
353 | 0 | } |