import HEAD into svn+ssh://svn.samba.org/home/svn/samba/trunk
[metze/old/v3-2-winbind-ndr.git] / source / sam / idmap_tdb.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    idmap TDB backend
5
6    Copyright (C) Tim Potter 2000
7    Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
8    Copyright (C) Simo Sorce 2003
9    
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.
14    
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.
19    
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.
23 */
24
25 #include "includes.h"
26
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_IDMAP
29
30 /* High water mark keys */
31 #define HWM_GROUP  "GROUP HWM"
32 #define HWM_USER   "USER HWM"
33
34 /* idmap version determines auto-conversion */
35 #define IDMAP_VERSION 2
36
37 /* Globals */
38 static TDB_CONTEXT *idmap_tdb;
39
40 static struct idmap_state {
41
42         /* User and group id pool */
43
44         uid_t uid_low, uid_high;               /* Range of uids to allocate */
45         gid_t gid_low, gid_high;               /* Range of gids to allocate */
46 } idmap_state;
47
48 /**********************************************************************
49  allocate a new RID; We don't care if is a user or group
50 **********************************************************************/
51
52 static NTSTATUS db_allocate_rid(uint32 *rid, int rid_type)
53 {
54         uint32 lowrid, highrid;
55         uint32 tmp_rid;
56
57         /* can't handle group rids right now.  This is such a mess.... */
58
59         if ( rid_type == GROUP_RID_TYPE )
60                 return NT_STATUS_UNSUCCESSFUL;
61         
62         /* cannot fail since idmap is only called winbindd */
63         
64         get_free_rid_range( &lowrid, &highrid );
65         
66         tmp_rid = lowrid;
67         
68         if ( !tdb_change_uint32_atomic(idmap_tdb, "RID_COUNTER", &tmp_rid, RID_MULTIPLIER) ) {
69                 DEBUG(3,("db_allocate_rid: Failed to locate next rid record in idmap db\n"));
70                 return NT_STATUS_UNSUCCESSFUL;
71         }
72         
73         if ( tmp_rid > highrid ) {
74                 DEBUG(0, ("db_allocate_rid: no RIDs available!\n"));
75                 return NT_STATUS_UNSUCCESSFUL;
76         }
77         
78         *rid = tmp_rid;
79
80         return NT_STATUS_OK;
81 }
82
83 /**********************************************************************
84  Allocate either a user or group id from the pool 
85 **********************************************************************/
86  
87 static NTSTATUS db_allocate_id(unid_t *id, int id_type)
88 {
89         BOOL ret;
90         int hwm;
91
92         if (!id)
93                 return NT_STATUS_INVALID_PARAMETER;
94
95         /* Get current high water mark */
96         switch (id_type & ID_TYPEMASK) {
97                 case ID_USERID:
98
99                         if ((hwm = tdb_fetch_int32(idmap_tdb, HWM_USER)) == -1) {
100                                 return NT_STATUS_INTERNAL_DB_ERROR;
101                         }
102
103                         /* check it is in the range */
104                         if (hwm > idmap_state.uid_high) {
105                                 DEBUG(0, ("idmap Fatal Error: UID range full!! (max: %lu)\n", 
106                                           (unsigned long)idmap_state.uid_high));
107                                 return NT_STATUS_UNSUCCESSFUL;
108                         }
109
110                         /* fetch a new id and increment it */
111                         ret = tdb_change_uint32_atomic(idmap_tdb, HWM_USER, (unsigned int *)&hwm, 1);
112                         if (!ret) {
113                                 DEBUG(0, ("idmap_tdb: Fatal error while fetching a new id\n!"));
114                                 return NT_STATUS_UNSUCCESSFUL;
115                         }
116
117                         /* recheck it is in the range */
118                         if (hwm > idmap_state.uid_high) {
119                                 DEBUG(0, ("idmap Fatal Error: UID range full!! (max: %lu)\n", 
120                                           (unsigned long)idmap_state.uid_high));
121                                 return NT_STATUS_UNSUCCESSFUL;
122                         }
123                         
124                         (*id).uid = hwm;
125                         DEBUG(10,("db_allocate_id: ID_USERID (*id).uid = %d\n", (unsigned int)hwm));
126
127                         break;
128                 case ID_GROUPID:
129                         if ((hwm = tdb_fetch_int32(idmap_tdb, HWM_GROUP)) == -1) {
130                                 return NT_STATUS_INTERNAL_DB_ERROR;
131                         }
132
133                         /* check it is in the range */
134                         if (hwm > idmap_state.gid_high) {
135                                 DEBUG(0, ("idmap Fatal Error: GID range full!! (max: %lu)\n", 
136                                           (unsigned long)idmap_state.gid_high));
137                                 return NT_STATUS_UNSUCCESSFUL;
138                         }
139
140                         /* fetch a new id and increment it */
141                         ret = tdb_change_uint32_atomic(idmap_tdb, HWM_GROUP, (unsigned int *)&hwm, 1);
142
143                         if (!ret) {
144                                 DEBUG(0, ("idmap_tdb: Fatal error while fetching a new id\n!"));
145                                 return NT_STATUS_UNSUCCESSFUL;
146                         }
147
148                         /* recheck it is in the range */
149                         if (hwm > idmap_state.gid_high) {
150                                 DEBUG(0, ("idmap Fatal Error: GID range full!! (max: %lu)\n", 
151                                           (unsigned long)idmap_state.gid_high));
152                                 return NT_STATUS_UNSUCCESSFUL;
153                         }
154                         
155                         (*id).gid = hwm;
156                         DEBUG(10,("db_allocate_id: ID_GROUPID (*id).gid = %d\n", (unsigned int)hwm));
157                         
158                         break;
159                 default:
160                         return NT_STATUS_INVALID_PARAMETER;
161         }
162
163         return NT_STATUS_OK;
164 }
165
166 /* Get a sid from an id */
167 static NTSTATUS internal_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type)
168 {
169         TDB_DATA key, data;
170         fstring keystr;
171         NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
172
173         if (!sid)
174                 return NT_STATUS_INVALID_PARAMETER;
175
176         switch (id_type & ID_TYPEMASK) {
177                 case ID_USERID:
178                         slprintf(keystr, sizeof(keystr), "UID %lu", (unsigned long)id.uid);
179                         break;
180                 case ID_GROUPID:
181                         slprintf(keystr, sizeof(keystr), "GID %lu", (unsigned long)id.gid);
182                         break;
183                 default:
184                         return NT_STATUS_UNSUCCESSFUL;
185         }
186
187         key.dptr = keystr;
188         key.dsize = strlen(keystr) + 1;
189
190         DEBUG(10,("internal_get_sid_from_id: fetching record %s\n", keystr ));
191
192         data = tdb_fetch(idmap_tdb, key);
193
194         if (data.dptr) {
195                 if (string_to_sid(sid, data.dptr)) {
196                         DEBUG(10,("internal_get_sid_from_id: fetching record %s -> %s\n", keystr, data.dptr ));
197                         ret = NT_STATUS_OK;
198                 }
199                 SAFE_FREE(data.dptr);
200         }
201
202         return ret;
203 }
204
205 /* Error codes for get_id_from_sid */
206 enum getidfromsiderr { GET_ID_FROM_SID_OK = 0, GET_ID_FROM_SID_NOTFOUND, GET_ID_FROM_SID_WRONG_TYPE, GET_ID_FROM_SID_ERR };
207
208 static enum getidfromsiderr internal_get_id_from_sid(unid_t *id, int *id_type, const DOM_SID *sid) 
209 {
210         enum getidfromsiderr ret = GET_ID_FROM_SID_ERR;
211         fstring keystr;
212         TDB_DATA key, data;
213         int type = *id_type & ID_TYPEMASK;
214
215         /* Check if sid is present in database */
216         sid_to_string(keystr, sid);
217
218         key.dptr = keystr;
219         key.dsize = strlen(keystr) + 1;
220
221         DEBUG(10,("internal_get_id_from_sid: fetching record %s of type 0x%x\n", keystr, type ));
222
223         data = tdb_fetch(idmap_tdb, key);
224         if (!data.dptr) {
225                 DEBUG(10,("internal_get_id_from_sid: record %s not found\n", keystr ));
226                 return GET_ID_FROM_SID_NOTFOUND;
227         } else {
228                 DEBUG(10,("internal_get_id_from_sid: record %s -> %s\n", keystr, data.dptr ));
229         }
230
231         if (type == ID_EMPTY || type == ID_USERID) {
232                 fstring scanstr;
233                 /* Parse and return existing uid */
234                 fstrcpy(scanstr, "UID %d");
235                 
236                 if (sscanf(data.dptr, scanstr, &((*id).uid)) == 1) {
237                         /* uid ok? */
238                         if (type == ID_EMPTY) {
239                                 *id_type = ID_USERID;
240                         }
241                         DEBUG(10,("internal_get_id_from_sid: %s fetching record %s -> %s \n",
242                                                 (type == ID_EMPTY) ? "ID_EMPTY" : "ID_USERID",
243                                                 keystr, data.dptr ));
244                         ret = GET_ID_FROM_SID_OK;
245                 } else {
246                         ret = GET_ID_FROM_SID_WRONG_TYPE;
247                 }
248         }
249         
250         if ((ret != GET_ID_FROM_SID_OK) && (type == ID_EMPTY || type == ID_GROUPID)) {
251                 fstring scanstr;
252                 /* Parse and return existing gid */
253                 fstrcpy(scanstr, "GID %d");
254                 
255                 if (sscanf(data.dptr, scanstr, &((*id).gid)) == 1) {
256                         /* gid ok? */
257                         if (type == ID_EMPTY) {
258                                 *id_type = ID_GROUPID;
259                         }
260                         DEBUG(10,("internal_get_id_from_sid: %s fetching record %s -> %s \n",
261                                                 (type == ID_EMPTY) ? "ID_EMPTY" : "ID_GROUPID",
262                                                 keystr, data.dptr ));
263                         ret = GET_ID_FROM_SID_OK;
264                 } else {
265                         ret = GET_ID_FROM_SID_WRONG_TYPE;
266                 }
267         }
268         
269         SAFE_FREE(data.dptr);
270
271         return ret;
272 }
273
274 /* Get a sid from an id */
275 static NTSTATUS db_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type_in)
276 {
277         NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
278         enum getidfromsiderr iderr;
279         int id_type = id_type_in & ID_TYPEMASK;
280         unid_t id_tmp = id;
281         int id_type_tmp = id_type;
282
283         DEBUG(10,("db_get_sid_from_id: id_type_in = 0x%x\n", id_type_in));
284
285         ret = internal_get_sid_from_id(sid, id, id_type);
286         if (!NT_STATUS_IS_OK(ret)) {
287                 return ret;
288         }
289         
290         iderr = internal_get_id_from_sid(&id_tmp, &id_type_tmp, sid);
291         if (iderr != GET_ID_FROM_SID_OK) {
292                 return NT_STATUS_UNSUCCESSFUL;
293         }
294         if (id_type_tmp != id_type) {
295                 return NT_STATUS_UNSUCCESSFUL;
296         } else if (id_type == ID_USERID) { 
297                 if (id_tmp.uid != id.uid) {
298                         return NT_STATUS_UNSUCCESSFUL;
299                 }
300         } else if (id_type == ID_GROUPID) {
301                 if (id_tmp.gid != id.gid) {
302                         return NT_STATUS_UNSUCCESSFUL;
303                 }
304         } else {
305                 return NT_STATUS_UNSUCCESSFUL;
306         }
307         return ret;
308 }
309 /* Get an id from a sid */
310 static NTSTATUS db_get_id_from_sid(unid_t *id, int *id_type, const DOM_SID *sid)
311 {
312         NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
313         enum getidfromsiderr iderr;
314
315         DEBUG(10,("db_get_id_from_sid\n"));
316
317         if (!sid || !id || !id_type)
318                 return NT_STATUS_INVALID_PARAMETER;
319
320         iderr = internal_get_id_from_sid(id, id_type, sid);
321         if (iderr == GET_ID_FROM_SID_OK) {
322                 DOM_SID sid_tmp;
323                 ret = internal_get_sid_from_id(&sid_tmp, *id, *id_type);
324                 if (NT_STATUS_IS_OK(ret)) {
325                         if (!sid_equal(&sid_tmp, sid)) {
326                                 return NT_STATUS_UNSUCCESSFUL;
327                         }
328                 }
329         } else if (iderr == GET_ID_FROM_SID_WRONG_TYPE) {
330                 /* We found a record but not the type we wanted.
331                  * This is an error, not an opportunity to overwrite...
332                  * JRA.
333                  */
334                 return NT_STATUS_UNSUCCESSFUL;
335         }
336
337         if (!(*id_type & ID_QUERY_ONLY) && (iderr != GET_ID_FROM_SID_OK) &&
338                    (((*id_type & ID_TYPEMASK) == ID_USERID)
339                     || (*id_type & ID_TYPEMASK) == ID_GROUPID)) {
340                 TDB_DATA sid_data;
341                 TDB_DATA ugid_data;
342                 fstring sid_string;
343                 
344                 sid_to_string(sid_string, sid);
345                 
346                 sid_data.dptr = sid_string;
347                 sid_data.dsize = strlen(sid_string)+1;
348
349                 /* Lock the record for this SID. */
350                 if (tdb_chainlock(idmap_tdb, sid_data) != 0) {
351                         DEBUG(10,("db_get_id_from_sid: failed to lock record %s. Error %s\n",
352                                         sid_string, tdb_errorstr(idmap_tdb) ));
353                         return NT_STATUS_UNSUCCESSFUL;
354                 }
355
356                 do {
357                         fstring ugid_str;
358
359                         /* Allocate a new id for this sid */
360                         ret = db_allocate_id(id, *id_type);
361                         if (!NT_STATUS_IS_OK(ret))
362                                 break;
363                         
364                         /* Store the UID side */
365                         /* Store new id */
366                         if (*id_type & ID_USERID) {
367                                 slprintf(ugid_str, sizeof(ugid_str), "UID %lu", 
368                                          (unsigned long)((*id).uid));
369                         } else {
370                                 slprintf(ugid_str, sizeof(ugid_str), "GID %lu", 
371                                          (unsigned long)((*id).gid));
372                         }
373                         
374                         ugid_data.dptr = ugid_str;
375                         ugid_data.dsize = strlen(ugid_str) + 1;
376
377                         DEBUG(10,("db_get_id_from_sid: storing %s -> %s\n",
378                                         ugid_data.dptr, sid_data.dptr ));
379
380                         if (tdb_store(idmap_tdb, ugid_data, sid_data, TDB_INSERT) != -1) {
381                                 ret = NT_STATUS_OK;
382                                 break;
383                         }
384                         if (tdb_error(idmap_tdb) != TDB_ERR_EXISTS)
385                                 DEBUG(10,("db_get_id_from_sid: error %s\n", tdb_errorstr(idmap_tdb) ));
386                         ret = NT_STATUS_UNSUCCESSFUL;
387                 } while (tdb_error(idmap_tdb) == TDB_ERR_EXISTS);
388
389                 if (NT_STATUS_IS_OK(ret)) {
390
391                         DEBUG(10,("db_get_id_from_sid: storing %s -> %s\n",
392                                 sid_data.dptr, ugid_data.dptr ));
393
394                         if (tdb_store(idmap_tdb, sid_data, ugid_data, TDB_REPLACE) == -1) {
395                                 DEBUG(10,("db_get_id_from_sid: error %s\n", tdb_errorstr(idmap_tdb) ));
396                                 /* TODO: print tdb error !! */
397                                 tdb_chainunlock(idmap_tdb, sid_data);
398                                 return NT_STATUS_UNSUCCESSFUL;
399                         }
400                 }
401
402                 tdb_chainunlock(idmap_tdb, sid_data);
403         }
404         
405         return ret;
406 }
407
408 static NTSTATUS db_set_mapping(const DOM_SID *sid, unid_t id, int id_type)
409 {
410         TDB_DATA ksid, kid, data;
411         fstring ksidstr;
412         fstring kidstr;
413
414         DEBUG(10,("db_set_mapping: id_type = 0x%x\n", id_type));
415
416         if (!sid)
417                 return NT_STATUS_INVALID_PARAMETER;
418
419         sid_to_string(ksidstr, sid);
420
421         ksid.dptr = ksidstr;
422         ksid.dsize = strlen(ksidstr) + 1;
423
424         if (id_type & ID_USERID) {
425                 slprintf(kidstr, sizeof(kidstr), "UID %lu", (unsigned long)id.uid);
426         } else if (id_type & ID_GROUPID) {
427                 slprintf(kidstr, sizeof(kidstr), "GID %lu", (unsigned long)id.gid);
428         } else {
429                 return NT_STATUS_INVALID_PARAMETER;
430         }
431
432         kid.dptr = kidstr;
433         kid.dsize = strlen(kidstr) + 1;
434
435         /* *DELETE* prevoius mappings if any.
436          * This is done both SID and [U|G]ID passed in */
437         
438         /* Lock the record for this SID. */
439         if (tdb_chainlock(idmap_tdb, ksid) != 0) {
440                 DEBUG(10,("db_set_mapping: failed to lock record %s. Error %s\n",
441                                 ksidstr, tdb_errorstr(idmap_tdb) ));
442                 return NT_STATUS_UNSUCCESSFUL;
443         }
444
445         DEBUG(10,("db_set_mapping: fetching %s\n", ksid.dptr));
446
447         data = tdb_fetch(idmap_tdb, ksid);
448         if (data.dptr) {
449                 DEBUG(10,("db_set_mapping: deleting %s and %s\n", data.dptr, ksid.dptr ));
450                 tdb_delete(idmap_tdb, data);
451                 tdb_delete(idmap_tdb, ksid);
452                 SAFE_FREE(data.dptr);
453         }
454         data = tdb_fetch(idmap_tdb, kid);
455         if (data.dptr) {
456                 DEBUG(10,("db_set_mapping: deleting %s and %s\n", data.dptr, kid.dptr ));
457                 tdb_delete(idmap_tdb, data);
458                 tdb_delete(idmap_tdb, kid);
459                 SAFE_FREE(data.dptr);
460         }
461
462         if (tdb_store(idmap_tdb, ksid, kid, TDB_INSERT) == -1) {
463                 DEBUG(0, ("idb_set_mapping: tdb_store 1 error: %s\n", tdb_errorstr(idmap_tdb)));
464                 tdb_chainunlock(idmap_tdb, ksid);
465                 return NT_STATUS_UNSUCCESSFUL;
466         }
467         if (tdb_store(idmap_tdb, kid, ksid, TDB_INSERT) == -1) {
468                 DEBUG(0, ("idb_set_mapping: tdb_store 2 error: %s\n", tdb_errorstr(idmap_tdb)));
469                 tdb_chainunlock(idmap_tdb, ksid);
470                 return NT_STATUS_UNSUCCESSFUL;
471         }
472
473         tdb_chainunlock(idmap_tdb, ksid);
474         DEBUG(10,("db_set_mapping: stored %s -> %s and %s -> %s\n", ksid.dptr, kid.dptr, kid.dptr, ksid.dptr ));
475         return NT_STATUS_OK;
476 }
477
478 /*****************************************************************************
479  Initialise idmap database. 
480 *****************************************************************************/
481
482 static NTSTATUS db_idmap_init( char *params )
483 {
484         SMB_STRUCT_STAT stbuf;
485         char *tdbfile = NULL;
486         int32 version;
487         BOOL tdb_is_new = False;
488
489         /* use the old database if present */
490         tdbfile = strdup(lock_path("winbindd_idmap.tdb"));
491         if (!tdbfile) {
492                 DEBUG(0, ("idmap_init: out of memory!\n"));
493                 return NT_STATUS_NO_MEMORY;
494         }
495
496         if (!file_exist(tdbfile, &stbuf)) {
497                 tdb_is_new = True;
498         }
499
500         DEBUG(10,("db_idmap_init: Opening tdbfile %s\n", tdbfile ));
501
502         /* Open idmap repository */
503         if (!(idmap_tdb = tdb_open_log(tdbfile, 0,
504                                        TDB_DEFAULT, O_RDWR | O_CREAT,
505                                        0644))) {
506                 DEBUG(0, ("idmap_init: Unable to open idmap database\n"));
507                 SAFE_FREE(tdbfile);
508                 return NT_STATUS_UNSUCCESSFUL;
509         }
510
511         SAFE_FREE(tdbfile);
512
513         if (tdb_is_new) {
514                 /* the file didn't existed before opening it, let's
515                  * store idmap version as nobody else yet opened and
516                  * stored it. I do not like this method but didn't
517                  * found a way to understand if an opened tdb have
518                  * been just created or not --- SSS */
519                 tdb_store_int32(idmap_tdb, "IDMAP_VERSION", IDMAP_VERSION);
520         }
521
522         /* check against earlier versions */
523         version = tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION");
524         if (version != IDMAP_VERSION) {
525                 DEBUG(0, ("idmap_init: Unable to open idmap database, it's in an old format!\n"));
526                 return NT_STATUS_INTERNAL_DB_ERROR;
527         }
528
529         /* Create high water marks for group and user id */
530         if (!lp_idmap_uid(&idmap_state.uid_low, &idmap_state.uid_high)) {
531                 DEBUG(1, ("idmap uid range missing or invalid\n"));
532                 DEBUGADD(1, ("idmap will be unable to map foreign SIDs\n"));
533         } else {
534                 if (tdb_fetch_int32(idmap_tdb, HWM_USER) == -1) {
535                         if (tdb_store_int32(idmap_tdb, HWM_USER, idmap_state.uid_low) == -1) {
536                                 DEBUG(0, ("idmap_init: Unable to initialise user hwm in idmap database\n"));
537                                 return NT_STATUS_INTERNAL_DB_ERROR;
538                         }
539                 }
540         }
541
542         if (!lp_idmap_gid(&idmap_state.gid_low, &idmap_state.gid_high)) {
543                 DEBUG(1, ("idmap gid range missing or invalid\n"));
544                 DEBUGADD(1, ("idmap will be unable to map foreign SIDs\n"));
545         } else {
546                 if (tdb_fetch_int32(idmap_tdb, HWM_GROUP) == -1) {
547                         if (tdb_store_int32(idmap_tdb, HWM_GROUP, idmap_state.gid_low) == -1) {
548                                 DEBUG(0, ("idmap_init: Unable to initialise group hwm in idmap database\n"));
549                                 return NT_STATUS_INTERNAL_DB_ERROR;
550                         }
551                 }
552         }
553
554         return NT_STATUS_OK;
555 }
556
557 /* Close the tdb */
558 static NTSTATUS db_idmap_close(void)
559 {
560         if (idmap_tdb) {
561                 if (tdb_close(idmap_tdb) == 0) {
562                         return NT_STATUS_OK;
563                 } else {
564                         return NT_STATUS_UNSUCCESSFUL;
565                 }
566         }
567         return NT_STATUS_OK;
568 }
569
570
571 /* Dump status information to log file.  Display different stuff based on
572    the debug level:
573
574    Debug Level        Information Displayed
575    =================================================================
576    0                  Percentage of [ug]id range allocated
577    0                  High water marks (next allocated ids)
578 */
579
580 #define DUMP_INFO 0
581
582 static void db_idmap_status(void)
583 {
584         int user_hwm, group_hwm;
585
586         DEBUG(0, ("winbindd idmap status:\n"));
587
588         /* Get current high water marks */
589
590         if ((user_hwm = tdb_fetch_int32(idmap_tdb, HWM_USER)) == -1) {
591                 DEBUG(DUMP_INFO,
592                       ("\tCould not get userid high water mark!\n"));
593         }
594
595         if ((group_hwm = tdb_fetch_int32(idmap_tdb, HWM_GROUP)) == -1) {
596                 DEBUG(DUMP_INFO,
597                       ("\tCould not get groupid high water mark!\n"));
598         }
599
600         /* Display next ids to allocate */
601
602         if (user_hwm != -1) {
603                 DEBUG(DUMP_INFO,
604                       ("\tNext userid to allocate is %d\n", user_hwm));
605         }
606
607         if (group_hwm != -1) {
608                 DEBUG(DUMP_INFO,
609                       ("\tNext groupid to allocate is %d\n", group_hwm));
610         }
611
612         /* Display percentage of id range already allocated. */
613
614         if (user_hwm != -1) {
615                 int num_users = user_hwm - idmap_state.uid_low;
616                 int total_users =
617                     idmap_state.uid_high - idmap_state.uid_low;
618
619                 DEBUG(DUMP_INFO,
620                       ("\tUser id range is %d%% full (%d of %d)\n",
621                        num_users * 100 / total_users, num_users,
622                        total_users));
623         }
624
625         if (group_hwm != -1) {
626                 int num_groups = group_hwm - idmap_state.gid_low;
627                 int total_groups =
628                     idmap_state.gid_high - idmap_state.gid_low;
629
630                 DEBUG(DUMP_INFO,
631                       ("\tGroup id range is %d%% full (%d of %d)\n",
632                        num_groups * 100 / total_groups, num_groups,
633                        total_groups));
634         }
635
636         /* Display complete mapping of users and groups to rids */
637 }
638
639 /**********************************************************************
640  Return the TDB_CONTEXT* for winbindd_idmap.  I **really** feel
641  dirty doing this, but not so dirty that I want to create another 
642  tdb
643 ***********************************************************************/
644
645 TDB_CONTEXT *idmap_tdb_handle( void )
646 {
647         if ( idmap_tdb )
648                 return idmap_tdb;
649                 
650         /* go ahead an open it;  db_idmap_init() doesn't use any params 
651            right now */
652            
653         db_idmap_init( NULL );
654         if ( idmap_tdb )
655                 return idmap_tdb;
656                 
657         return NULL;
658 }
659
660 static struct idmap_methods db_methods = {
661
662         db_idmap_init,
663         db_allocate_rid,
664         db_allocate_id,
665         db_get_sid_from_id,
666         db_get_id_from_sid,
667         db_set_mapping,
668         db_idmap_close,
669         db_idmap_status
670
671 };
672
673 NTSTATUS idmap_tdb_init(void)
674 {
675         return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "tdb", &db_methods);
676 }