some more code
[Samba.git] / source3 / sam / idmap_tdb.c
blob3aaab3ac428bdaf366d945e79a09ef4171280b73
1 /*
2 Unix SMB/CIFS implementation.
4 idmap TDB backend
6 Copyright (C) Tim Potter 2000
7 Copyright (C) Anthony Liguori 2003
8 Copyright (C) Simo Sorce 2003
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include "includes.h"
26 #include "idmap.h"
28 #undef DBGC_CLASS
29 #define DBGC_CLASS DBGC_IDMAP
31 /* High water mark keys */
32 #define HWM_GROUP "GROUP HWM"
33 #define HWM_USER "USER HWM"
35 /* idmap version determines auto-conversion */
36 #define IDMAP_VERSION 2
38 /* Globals */
39 static TDB_CONTEXT *idmap_tdb;
41 /* FIXME: let handle conversions when all things work ok.
42 I think it is better to handle the conversion at
43 upgrade time and leave the old db intact.
44 That would also make easier to go back to 2.2 if needed
45 ---SSS */
46 #if 0
48 /* convert one record to the new format */
49 static int tdb_convert_fn(TDB_CONTEXT * tdb, TDB_DATA key, TDB_DATA data,
50 void *ignored)
52 struct winbindd_domain *domain;
53 char *p;
54 DOM_SID sid;
55 uint32 rid;
56 fstring keystr;
57 fstring dom_name;
58 TDB_DATA key2;
60 p = strchr(key.dptr, '/');
61 if (!p)
62 return 0;
64 *p = 0;
65 fstrcpy(dom_name, key.dptr);
66 *p++ = '/';
68 domain = find_domain_from_name(dom_name);
69 if (!domain) {
70 /* We must delete the old record. */
71 DEBUG(0,
72 ("winbindd: tdb_convert_fn : Unable to find domain %s\n",
73 dom_name));
74 DEBUG(0,
75 ("winbindd: tdb_convert_fn : deleting record %s\n",
76 key.dptr));
77 tdb_delete(idmap_tdb, key);
78 return 0;
81 rid = atoi(p);
83 sid_copy(&sid, &domain->sid);
84 sid_append_rid(&sid, rid);
86 sid_to_string(keystr, &sid);
87 key2.dptr = keystr;
88 key2.dsize = strlen(keystr) + 1;
90 if (tdb_store(idmap_tdb, key2, data, TDB_INSERT) != 0) {
91 /* not good! */
92 DEBUG(0,
93 ("winbindd: tdb_convert_fn : Unable to update record %s\n",
94 key2.dptr));
95 DEBUG(0,
96 ("winbindd: tdb_convert_fn : conversion failed - idmap corrupt ?\n"));
97 return -1;
100 if (tdb_store(idmap_tdb, data, key2, TDB_REPLACE) != 0) {
101 /* not good! */
102 DEBUG(0,
103 ("winbindd: tdb_convert_fn : Unable to update record %s\n",
104 data.dptr));
105 DEBUG(0,
106 ("winbindd: tdb_convert_fn : conversion failed - idmap corrupt ?\n"));
107 return -1;
110 tdb_delete(idmap_tdb, key);
112 return 0;
115 /*****************************************************************************
116 Convert the idmap database from an older version.
117 *****************************************************************************/
118 static BOOL tdb_idmap_convert(const char *idmap_name)
120 int32 vers = tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION");
121 BOOL bigendianheader =
122 (idmap_tdb->flags & TDB_BIGENDIAN) ? True : False;
124 if (vers == IDMAP_VERSION)
125 return True;
127 if (((vers == -1) && bigendianheader)
128 || (IREV(vers) == IDMAP_VERSION)) {
129 /* Arrggghh ! Bytereversed or old big-endian - make order independent ! */
131 * high and low records were created on a
132 * big endian machine and will need byte-reversing.
135 int32 wm;
137 wm = tdb_fetch_int32(idmap_tdb, HWM_USER);
139 if (wm != -1) {
140 wm = IREV(wm);
141 } else
142 wm = server_state.uid_low;
144 if (tdb_store_int32(idmap_tdb, HWM_USER, wm) == -1) {
145 DEBUG(0,
146 ("tdb_idmap_convert: Unable to byteswap user hwm in idmap database\n"));
147 return False;
150 wm = tdb_fetch_int32(idmap_tdb, HWM_GROUP);
151 if (wm != -1) {
152 wm = IREV(wm);
153 } else
154 wm = server_state.gid_low;
156 if (tdb_store_int32(idmap_tdb, HWM_GROUP, wm) == -1) {
157 DEBUG(0,
158 ("tdb_idmap_convert: Unable to byteswap group hwm in idmap database\n"));
159 return False;
163 /* the old format stored as DOMAIN/rid - now we store the SID direct */
164 tdb_traverse(idmap_tdb, tdb_convert_fn, NULL);
166 if (tdb_store_int32(idmap_tdb, "IDMAP_VERSION", IDMAP_VERSION) ==
167 -1) {
168 DEBUG(0,
169 ("tdb_idmap_convert: Unable to byteswap group hwm in idmap database\n"));
170 return False;
173 return True;
175 #endif
177 /* Allocate either a user or group id from the pool */
178 static NTSTATUS tdb_allocate_id(id_t *id, int id_type)
180 int hwm;
182 if (!id) return NT_STATUS_INVALID_PARAMETER;
184 /* Get current high water mark */
185 switch (id_type) {
186 case ID_USERID:
187 if ((hwm = tdb_fetch_int32(idmap_tdb, HWM_USER)) == -1) {
188 return NT_STATUS_INTERNAL_DB_ERROR;
191 if (hwm > server_state.uid_high) {
192 DEBUG(0, ("idmap Fatal Error: UID range full!!\n"));
193 return NT_STATUS_UNSUCCESSFUL;
196 *id.uid = hwm++;
198 /* Store new high water mark */
199 tdb_store_int32(idmap_tdb, HWM_USER, hwm);
200 break;
201 case ID_GROUPID:
202 if ((hwm = tdb_fetch_int32(idmap_tdb, HWM_GROUP)) == -1) {
203 return NT_STATUS_INTERNAL_DB_ERROR;
206 if (hwm > server_state.gid_high) {
207 DEBUG(0, ("idmap Fatal Error: GID range full!!\n"));
208 return NT_STATUS_UNSUCCESSFUL;
211 *id.gid = hwm++;
213 /* Store new high water mark */
214 tdb_store_int32(idmap_tdb, HWM_GROUP, hwm);
215 break;
216 default:
217 return NT_STATUS_INVALID_PARAMETER;
220 return NT_STATUS_OK;
223 /* Get a sid from an id */
224 static NTSTATUS tdb_get_sid_from_id(DOM_SID *sid, id_t id, int id_type)
226 TDB_DATA key, data;
227 fstring keystr;
228 NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
230 if (!sid) return NT_STATUS_INVALID_PARAMETER;
232 switch (id_type) {
233 case ID_USERID:
234 slprintf(keystr, sizeof(keystr), "UID %d", id.uid);
235 break;
236 case ID_GROUPID:
237 slprintf(keystr, sizeof(keystr), "GID %d", id.gid);
238 break;
239 default:
240 return NT_STATUS_UNSUCCESSFUL;
243 key.dptr = keystr;
244 key.dsize = strlen(keystr) + 1;
246 data = tdb_fetch(idmap_tdb, key);
248 if (data.dptr) {
249 if (string_to_sid(sid, data.dptr)) {
250 ret = NT_STATUS_OK;
252 SAFE_FREE(data.dptr);
255 return ret;
258 /* Get an id from a sid */
259 static NTSTATUS tdb_get_id_from_sid(id_t *id, int *id_type, DOM_SID *sid)
261 TDB_DATA data, key;
262 fstring keystr;
263 NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
265 if (!sid || !id || !id_type) return NT_STATUS_INVALID_PARAMETER;
267 /* Check if sid is present in database */
268 sid_to_string(keystr, sid);
270 key.dptr = keystr;
271 key.dsize = strlen(keystr) + 1;
273 data = tdb_fetch(idmap_tdb, key);
275 if (data.dptr) {
276 fstring scanstr;
278 if (*id_type == ID_EMPTY || *id_type == ID_USERID) {
279 /* Parse and return existing uid */
280 fstrcpy(scanstr, "UID %d");
282 if (sscanf(data.dptr, scanstr, *id.uid) == 1) {
283 /* uid ok? */
284 if (*id_type == ID_EMPTY) {
285 *id_type = ID_USERID;
287 ret = NT_STATUS_OK;
288 goto idok;
292 if (*id_type == ID_EMPTY || *id_type == ID_GROUPID) {
293 /* Parse and return existing gid */
294 fstrcpy(scanstr, "GID %d");
296 if (sscanf(data.dptr, scanstr, *id.gid) == 1) {
297 /* gid ok? */
298 if (*id_type == ID_EMPTY) {
299 *id_type = ID_GROUPID;
301 ret = NT_STATUS_OK;
304 idok:
305 SAFE_FREE(data.dptr);
307 } else if (*id_type == ID_USERID || *id_type == ID_GROUPID) {
309 /* Allocate a new id for this sid */
310 ret = tdb_allocate_id(id, id_type);
311 if (NT_STATUS_IS_OK(ret)) {
312 fstring keystr2;
314 /* Store new id */
315 slprintf(keystr2, sizeof(keystr2), "%s %d",
316 *id_type ? "GID" : "UID", *id);
318 data.dptr = keystr2;
319 data.dsize = strlen(keystr2) + 1;
321 if (tdb_store(idmap_tdb, key, data, TDB_INSERT) == -1)
322 return NT_STATUS_UNSUCCESSFUL;
323 if (tdb_store(idmap_tdb, data, key, TDB_INSERT) == -1)
324 return NT_STATUS_UNSUCCESSFUL;
326 ret = NT_STATUS_OK;
330 return ret;
333 /*****************************************************************************
334 Initialise idmap database.
335 *****************************************************************************/
336 static NTSTATUS tdb_idmap_init(void)
338 /* Open tdb cache */
339 if (!(idmap_tdb = tdb_open_log(lock_path("idmap.tdb"), 0,
340 TDB_DEFAULT, O_RDWR | O_CREAT,
341 0600))) {
342 DEBUG(0, ("idmap_init: Unable to open idmap database\n"));
343 return NT_STATUS_UNSUCCESSFUL;
346 #if 0
347 /* possibly convert from an earlier version */
348 if (!tdb_idmap_convert(lock_path("winbind_idmap.tdb"))) {
349 DEBUG(0,
350 ("idmap_init: Unable to open old idmap database\n"));
351 return False;
353 #endif
355 /* Create high water marks for group and user id */
356 if (tdb_fetch_int32(idmap_tdb, HWM_USER) == -1) {
357 if (tdb_store_int32(idmap_tdb, HWM_USER, server_state.uid_low) == -1) {
358 DEBUG(0, ("idmap_init: Unable to initialise user hwm in idmap database\n"));
359 return NT_STATUS_INTERNAL_DB_ERROR;
363 if (tdb_fetch_int32(idmap_tdb, HWM_GROUP) == -1) {
364 if (tdb_store_int32(idmap_tdb, HWM_GROUP, server_state.gid_low) == -1) {
365 DEBUG(0, ("idmap_init: Unable to initialise group hwm in idmap database\n"));
366 return NT_STATUS_INTERNAL_DB_ERROR;
370 return NT_STATUS_OK;
373 /* Close the tdb */
374 static NTSTATUS tdb_idmap_close(void)
376 if (idmap_tdb)
377 if (tdb_close(idmap_tdb) == 0)
378 return NT_STATUS_OK;
379 else
380 retrun NT_STATUS_UNSUCCESSFUL;
381 return NT_STATUS_OK;
385 /* Dump status information to log file. Display different stuff based on
386 the debug level:
388 Debug Level Information Displayed
389 =================================================================
390 0 Percentage of [ug]id range allocated
391 0 High water marks (next allocated ids)
394 #define DUMP_INFO 0
396 static void tdb_idmap_status(void)
398 int user_hwm, group_hwm;
400 DEBUG(0, ("winbindd idmap status:\n"));
402 /* Get current high water marks */
404 if ((user_hwm = tdb_fetch_int32(idmap_tdb, HWM_USER)) == -1) {
405 DEBUG(DUMP_INFO,
406 ("\tCould not get userid high water mark!\n"));
409 if ((group_hwm = tdb_fetch_int32(idmap_tdb, HWM_GROUP)) == -1) {
410 DEBUG(DUMP_INFO,
411 ("\tCould not get groupid high water mark!\n"));
414 /* Display next ids to allocate */
416 if (user_hwm != -1) {
417 DEBUG(DUMP_INFO,
418 ("\tNext userid to allocate is %d\n", user_hwm));
421 if (group_hwm != -1) {
422 DEBUG(DUMP_INFO,
423 ("\tNext groupid to allocate is %d\n", group_hwm));
426 /* Display percentage of id range already allocated. */
428 if (user_hwm != -1) {
429 int num_users = user_hwm - server_state.uid_low;
430 int total_users =
431 server_state.uid_high - server_state.uid_low;
433 DEBUG(DUMP_INFO,
434 ("\tUser id range is %d%% full (%d of %d)\n",
435 num_users * 100 / total_users, num_users,
436 total_users));
439 if (group_hwm != -1) {
440 int num_groups = group_hwm - server_state.gid_low;
441 int total_groups =
442 server_state.gid_high - server_state.gid_low;
444 DEBUG(DUMP_INFO,
445 ("\tGroup id range is %d%% full (%d of %d)\n",
446 num_groups * 100 / total_groups, num_groups,
447 total_groups));
450 /* Display complete mapping of users and groups to rids */
453 struct idmap_methods tdb_idmap_methods = {
455 tdb_idmap_init,
456 tdb_get_sid_from_id,
457 tdb_get_id_from_sid,
458 tdb_idmap_close,
459 tdb_idmap_status
463 NTSTATUS idmap_reg_tdb(struct idmap_methods **meth)
465 *meth = &tdb_idmap_methods;
467 return NTSTATUS_OK;