s3:net registry: fix violation of coding conventions
[Samba.git] / lib / ntdb / tools / ntdbtool.c
blob7c1ef7df7aac7180480d10ffaf0709427f39ebd9
1 /*
2 Unix SMB/CIFS implementation.
3 Samba database functions
4 Copyright (C) Andrew Tridgell 1999-2000
5 Copyright (C) Paul `Rusty' Russell 2000
6 Copyright (C) Jeremy Allison 2000
7 Copyright (C) Andrew Esh 2001
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "config.h"
24 #include "ntdb.h"
25 #ifdef HAVE_LIBREPLACE
26 #include <replace.h>
27 #include <system/filesys.h>
28 #include <system/time.h>
29 #include <system/locale.h>
30 #else
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <ctype.h>
34 #include <sys/time.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
38 #include <errno.h>
39 #include <string.h>
40 #include <stdarg.h>
41 #endif
43 static int do_command(void);
44 const char *cmdname;
45 char *arg1, *arg2;
46 size_t arg1len, arg2len;
47 int bIterate = 0;
48 char *line;
49 NTDB_DATA iterate_kbuf;
50 char cmdline[1024];
51 static int disable_mmap;
53 enum commands {
54 CMD_CREATE_NTDB,
55 CMD_OPEN_NTDB,
56 CMD_TRANSACTION_START,
57 CMD_TRANSACTION_COMMIT,
58 CMD_TRANSACTION_CANCEL,
59 CMD_ERASE,
60 CMD_DUMP,
61 CMD_INSERT,
62 CMD_MOVE,
63 CMD_STORE,
64 CMD_SHOW,
65 CMD_KEYS,
66 CMD_HEXKEYS,
67 CMD_DELETE,
68 #if 0
69 CMD_LIST_HASH_FREE,
70 CMD_LIST_FREE,
71 #endif
72 CMD_INFO,
73 CMD_MMAP,
74 CMD_SPEED,
75 CMD_FIRST,
76 CMD_NEXT,
77 CMD_SYSTEM,
78 CMD_CHECK,
79 CMD_QUIT,
80 CMD_HELP
83 typedef struct {
84 const char *name;
85 enum commands cmd;
86 } COMMAND_TABLE;
88 COMMAND_TABLE cmd_table[] = {
89 {"create", CMD_CREATE_NTDB},
90 {"open", CMD_OPEN_NTDB},
91 #if 0
92 {"transaction_start", CMD_TRANSACTION_START},
93 {"transaction_commit", CMD_TRANSACTION_COMMIT},
94 {"transaction_cancel", CMD_TRANSACTION_CANCEL},
95 #endif
96 {"erase", CMD_ERASE},
97 {"dump", CMD_DUMP},
98 {"insert", CMD_INSERT},
99 {"move", CMD_MOVE},
100 {"store", CMD_STORE},
101 {"show", CMD_SHOW},
102 {"keys", CMD_KEYS},
103 {"hexkeys", CMD_HEXKEYS},
104 {"delete", CMD_DELETE},
105 #if 0
106 {"list", CMD_LIST_HASH_FREE},
107 {"free", CMD_LIST_FREE},
108 #endif
109 {"info", CMD_INFO},
110 {"speed", CMD_SPEED},
111 {"mmap", CMD_MMAP},
112 {"first", CMD_FIRST},
113 {"1", CMD_FIRST},
114 {"next", CMD_NEXT},
115 {"n", CMD_NEXT},
116 {"check", CMD_CHECK},
117 {"quit", CMD_QUIT},
118 {"q", CMD_QUIT},
119 {"!", CMD_SYSTEM},
120 {NULL, CMD_HELP}
123 struct timeval tp1,tp2;
125 static void _start_timer(void)
127 gettimeofday(&tp1,NULL);
130 static double _end_timer(void)
132 gettimeofday(&tp2,NULL);
133 return((tp2.tv_sec - tp1.tv_sec) +
134 (tp2.tv_usec - tp1.tv_usec)*1.0e-6);
137 static void ntdb_log(struct ntdb_context *ntdb,
138 enum ntdb_log_level level,
139 enum NTDB_ERROR ecode,
140 const char *message,
141 void *data)
143 fprintf(stderr, "ntdb:%s:%s:%s\n",
144 ntdb_name(ntdb), ntdb_errorstr(ecode), message);
147 /* a ntdb tool for manipulating a ntdb database */
149 static struct ntdb_context *ntdb;
151 static int print_rec(struct ntdb_context *the_ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state);
152 static int print_key(struct ntdb_context *the_ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state);
153 static int print_hexkey(struct ntdb_context *the_ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state);
155 static void print_asc(const char *buf,int len)
157 int i;
159 /* We're probably printing ASCII strings so don't try to display
160 the trailing NULL character. */
162 if (buf[len - 1] == 0)
163 len--;
165 for (i=0;i<len;i++)
166 printf("%c",isprint(buf[i])?buf[i]:'.');
169 static void print_data(const char *buf,int len)
171 int i=0;
172 if (len<=0) return;
173 printf("[%03X] ",i);
174 for (i=0;i<len;) {
175 printf("%02X ",(int)((unsigned char)buf[i]));
176 i++;
177 if (i%8 == 0) printf(" ");
178 if (i%16 == 0) {
179 print_asc(&buf[i-16],8); printf(" ");
180 print_asc(&buf[i-8],8); printf("\n");
181 if (i<len) printf("[%03X] ",i);
184 if (i%16) {
185 int n;
187 n = 16 - (i%16);
188 printf(" ");
189 if (n>8) printf(" ");
190 while (n--) printf(" ");
192 n = i%16;
193 if (n > 8) n = 8;
194 print_asc(&buf[i-(i%16)],n); printf(" ");
195 n = (i%16) - n;
196 if (n>0) print_asc(&buf[i-n],n);
197 printf("\n");
201 static void help(void)
203 printf("\n"
204 "tdbtool: \n"
205 " create dbname : create a database\n"
206 " open dbname : open an existing database\n"
207 " openjh dbname : open an existing database (jenkins hash)\n"
208 " transaction_start : start a transaction\n"
209 " transaction_commit : commit a transaction\n"
210 " transaction_cancel : cancel a transaction\n"
211 " erase : erase the database\n"
212 " dump : dump the database as strings\n"
213 " keys : dump the database keys as strings\n"
214 " hexkeys : dump the database keys as hex values\n"
215 " info : print summary info about the database\n"
216 " insert key data : insert a record\n"
217 " move key file : move a record to a destination ntdb\n"
218 " store key data : store a record (replace)\n"
219 " show key : show a record by key\n"
220 " delete key : delete a record by key\n"
221 #if 0
222 " list : print the database hash table and freelist\n"
223 " free : print the database freelist\n"
224 #endif
225 " check : check the integrity of an opened database\n"
226 " speed : perform speed tests on the database\n"
227 " ! command : execute system command\n"
228 " 1 | first : print the first record\n"
229 " n | next : print the next record\n"
230 " q | quit : terminate\n"
231 " \\n : repeat 'next' command\n"
232 "\n");
235 static void terror(enum NTDB_ERROR err, const char *why)
237 if (err != NTDB_SUCCESS)
238 printf("%s:%s\n", ntdb_errorstr(err), why);
239 else
240 printf("%s\n", why);
243 static void create_ntdb(const char *tdbname)
245 union ntdb_attribute log_attr;
246 log_attr.base.attr = NTDB_ATTRIBUTE_LOG;
247 log_attr.base.next = NULL;
248 log_attr.log.fn = ntdb_log;
250 if (ntdb) ntdb_close(ntdb);
251 ntdb = ntdb_open(tdbname, (disable_mmap?NTDB_NOMMAP:0),
252 O_RDWR | O_CREAT | O_TRUNC, 0600, &log_attr);
253 if (!ntdb) {
254 printf("Could not create %s: %s\n", tdbname, strerror(errno));
258 static void open_ntdb(const char *tdbname)
260 union ntdb_attribute log_attr;
261 log_attr.base.attr = NTDB_ATTRIBUTE_LOG;
262 log_attr.base.next = NULL;
263 log_attr.log.fn = ntdb_log;
265 if (ntdb) ntdb_close(ntdb);
266 ntdb = ntdb_open(tdbname, disable_mmap?NTDB_NOMMAP:0, O_RDWR, 0600,
267 &log_attr);
268 if (!ntdb) {
269 printf("Could not open %s: %s\n", tdbname, strerror(errno));
273 static void insert_ntdb(char *keyname, size_t keylen, char* data, size_t datalen)
275 NTDB_DATA key, dbuf;
276 enum NTDB_ERROR ecode;
278 if ((keyname == NULL) || (keylen == 0)) {
279 terror(NTDB_SUCCESS, "need key");
280 return;
283 key.dptr = (unsigned char *)keyname;
284 key.dsize = keylen;
285 dbuf.dptr = (unsigned char *)data;
286 dbuf.dsize = datalen;
288 ecode = ntdb_store(ntdb, key, dbuf, NTDB_INSERT);
289 if (ecode) {
290 terror(ecode, "insert failed");
294 static void store_ntdb(char *keyname, size_t keylen, char* data, size_t datalen)
296 NTDB_DATA key, dbuf;
297 enum NTDB_ERROR ecode;
299 if ((keyname == NULL) || (keylen == 0)) {
300 terror(NTDB_SUCCESS, "need key");
301 return;
304 if ((data == NULL) || (datalen == 0)) {
305 terror(NTDB_SUCCESS, "need data");
306 return;
309 key.dptr = (unsigned char *)keyname;
310 key.dsize = keylen;
311 dbuf.dptr = (unsigned char *)data;
312 dbuf.dsize = datalen;
314 printf("Storing key:\n");
315 print_rec(ntdb, key, dbuf, NULL);
317 ecode = ntdb_store(ntdb, key, dbuf, NTDB_REPLACE);
318 if (ecode) {
319 terror(ecode, "store failed");
323 static void show_ntdb(char *keyname, size_t keylen)
325 NTDB_DATA key, dbuf;
326 enum NTDB_ERROR ecode;
328 if ((keyname == NULL) || (keylen == 0)) {
329 terror(NTDB_SUCCESS, "need key");
330 return;
333 key.dptr = (unsigned char *)keyname;
334 key.dsize = keylen;
336 ecode = ntdb_fetch(ntdb, key, &dbuf);
337 if (ecode) {
338 terror(ecode, "fetch failed");
339 return;
342 print_rec(ntdb, key, dbuf, NULL);
344 free( dbuf.dptr );
347 static void delete_ntdb(char *keyname, size_t keylen)
349 NTDB_DATA key;
350 enum NTDB_ERROR ecode;
352 if ((keyname == NULL) || (keylen == 0)) {
353 terror(NTDB_SUCCESS, "need key");
354 return;
357 key.dptr = (unsigned char *)keyname;
358 key.dsize = keylen;
360 ecode = ntdb_delete(ntdb, key);
361 if (ecode) {
362 terror(ecode, "delete failed");
366 static void move_rec(char *keyname, size_t keylen, char* tdbname)
368 NTDB_DATA key, dbuf;
369 struct ntdb_context *dst_ntdb;
370 enum NTDB_ERROR ecode;
372 if ((keyname == NULL) || (keylen == 0)) {
373 terror(NTDB_SUCCESS, "need key");
374 return;
377 if ( !tdbname ) {
378 terror(NTDB_SUCCESS, "need destination ntdb name");
379 return;
382 key.dptr = (unsigned char *)keyname;
383 key.dsize = keylen;
385 ecode = ntdb_fetch(ntdb, key, &dbuf);
386 if (ecode) {
387 terror(ecode, "fetch failed");
388 return;
391 print_rec(ntdb, key, dbuf, NULL);
393 dst_ntdb = ntdb_open(tdbname, 0, O_RDWR, 0600, NULL);
394 if ( !dst_ntdb ) {
395 terror(NTDB_SUCCESS, "unable to open destination ntdb");
396 return;
399 ecode = ntdb_store( dst_ntdb, key, dbuf, NTDB_REPLACE);
400 if (ecode)
401 terror(ecode, "failed to move record");
402 else
403 printf("record moved\n");
405 ntdb_close( dst_ntdb );
408 static int print_rec(struct ntdb_context *the_ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state)
410 printf("\nkey %d bytes\n", (int)key.dsize);
411 print_asc((const char *)key.dptr, key.dsize);
412 printf("\ndata %d bytes\n", (int)dbuf.dsize);
413 print_data((const char *)dbuf.dptr, dbuf.dsize);
414 return 0;
417 static int print_key(struct ntdb_context *the_ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state)
419 printf("key %d bytes: ", (int)key.dsize);
420 print_asc((const char *)key.dptr, key.dsize);
421 printf("\n");
422 return 0;
425 static int print_hexkey(struct ntdb_context *the_ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state)
427 printf("key %d bytes\n", (int)key.dsize);
428 print_data((const char *)key.dptr, key.dsize);
429 printf("\n");
430 return 0;
433 static int total_bytes;
435 static int traverse_fn(struct ntdb_context *the_ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state)
437 total_bytes += dbuf.dsize;
438 return 0;
441 static void info_ntdb(void)
443 enum NTDB_ERROR ecode;
444 char *summary;
446 ecode = ntdb_summary(ntdb, NTDB_SUMMARY_HISTOGRAMS, &summary);
448 if (ecode) {
449 terror(ecode, "Getting summary");
450 } else {
451 printf("%s", summary);
452 free(summary);
456 static void speed_ntdb(const char *tlimit)
458 unsigned timelimit = tlimit?atoi(tlimit):0;
459 double t;
460 int ops;
461 if (timelimit == 0) timelimit = 5;
463 ops = 0;
464 printf("Testing store speed for %u seconds\n", timelimit);
465 _start_timer();
466 do {
467 long int r = random();
468 NTDB_DATA key, dbuf;
469 key = ntdb_mkdata("store test", strlen("store test"));
470 dbuf.dptr = (unsigned char *)&r;
471 dbuf.dsize = sizeof(r);
472 ntdb_store(ntdb, key, dbuf, NTDB_REPLACE);
473 t = _end_timer();
474 ops++;
475 } while (t < timelimit);
476 printf("%10.3f ops/sec\n", ops/t);
478 ops = 0;
479 printf("Testing fetch speed for %u seconds\n", timelimit);
480 _start_timer();
481 do {
482 long int r = random();
483 NTDB_DATA key, dbuf;
484 key = ntdb_mkdata("store test", strlen("store test"));
485 dbuf.dptr = (unsigned char *)&r;
486 dbuf.dsize = sizeof(r);
487 ntdb_fetch(ntdb, key, &dbuf);
488 t = _end_timer();
489 ops++;
490 } while (t < timelimit);
491 printf("%10.3f ops/sec\n", ops/t);
493 ops = 0;
494 printf("Testing transaction speed for %u seconds\n", timelimit);
495 _start_timer();
496 do {
497 long int r = random();
498 NTDB_DATA key, dbuf;
499 key = ntdb_mkdata("transaction test", strlen("transaction test"));
500 dbuf.dptr = (unsigned char *)&r;
501 dbuf.dsize = sizeof(r);
502 ntdb_transaction_start(ntdb);
503 ntdb_store(ntdb, key, dbuf, NTDB_REPLACE);
504 ntdb_transaction_commit(ntdb);
505 t = _end_timer();
506 ops++;
507 } while (t < timelimit);
508 printf("%10.3f ops/sec\n", ops/t);
510 ops = 0;
511 printf("Testing traverse speed for %u seconds\n", timelimit);
512 _start_timer();
513 do {
514 ntdb_traverse(ntdb, traverse_fn, NULL);
515 t = _end_timer();
516 ops++;
517 } while (t < timelimit);
518 printf("%10.3f ops/sec\n", ops/t);
521 static void toggle_mmap(void)
523 disable_mmap = !disable_mmap;
524 if (disable_mmap) {
525 printf("mmap is disabled\n");
526 } else {
527 printf("mmap is enabled\n");
531 static char *ntdb_getline(const char *prompt)
533 static char thisline[1024];
534 char *p;
535 fputs(prompt, stdout);
536 thisline[0] = 0;
537 p = fgets(thisline, sizeof(thisline)-1, stdin);
538 if (p) p = strchr(p, '\n');
539 if (p) *p = 0;
540 return p?thisline:NULL;
543 static int do_delete_fn(struct ntdb_context *the_ntdb, NTDB_DATA key, NTDB_DATA dbuf,
544 void *state)
546 return ntdb_delete(the_ntdb, key);
549 static void first_record(struct ntdb_context *the_ntdb, NTDB_DATA *pkey)
551 NTDB_DATA dbuf;
552 enum NTDB_ERROR ecode;
553 ecode = ntdb_firstkey(the_ntdb, pkey);
554 if (!ecode)
555 ecode = ntdb_fetch(the_ntdb, *pkey, &dbuf);
556 if (ecode) terror(ecode, "fetch failed");
557 else {
558 print_rec(the_ntdb, *pkey, dbuf, NULL);
562 static void next_record(struct ntdb_context *the_ntdb, NTDB_DATA *pkey)
564 NTDB_DATA dbuf;
565 enum NTDB_ERROR ecode;
566 ecode = ntdb_nextkey(the_ntdb, pkey);
568 if (!ecode)
569 ecode = ntdb_fetch(the_ntdb, *pkey, &dbuf);
570 if (ecode)
571 terror(ecode, "fetch failed");
572 else
573 print_rec(the_ntdb, *pkey, dbuf, NULL);
576 static void check_db(struct ntdb_context *the_ntdb)
578 if (!the_ntdb) {
579 printf("Error: No database opened!\n");
580 } else {
581 if (ntdb_check(the_ntdb, NULL, NULL) != 0)
582 printf("Integrity check for the opened database failed.\n");
583 else
584 printf("Database integrity is OK.\n");
588 static int do_command(void)
590 COMMAND_TABLE *ctp = cmd_table;
591 enum commands mycmd = CMD_HELP;
592 int cmd_len;
594 if (cmdname && strlen(cmdname) == 0) {
595 mycmd = CMD_NEXT;
596 } else {
597 while (ctp->name) {
598 cmd_len = strlen(ctp->name);
599 if (strncmp(ctp->name,cmdname,cmd_len) == 0) {
600 mycmd = ctp->cmd;
601 break;
603 ctp++;
607 switch (mycmd) {
608 case CMD_CREATE_NTDB:
609 bIterate = 0;
610 create_ntdb(arg1);
611 return 0;
612 case CMD_OPEN_NTDB:
613 bIterate = 0;
614 open_ntdb(arg1);
615 return 0;
616 case CMD_SYSTEM:
617 /* Shell command */
618 if (system(arg1) == -1) {
619 terror(NTDB_SUCCESS, "system() call failed\n");
621 return 0;
622 case CMD_QUIT:
623 return 1;
624 default:
625 /* all the rest require a open database */
626 if (!ntdb) {
627 bIterate = 0;
628 terror(NTDB_SUCCESS, "database not open");
629 help();
630 return 0;
632 switch (mycmd) {
633 case CMD_TRANSACTION_START:
634 bIterate = 0;
635 ntdb_transaction_start(ntdb);
636 return 0;
637 case CMD_TRANSACTION_COMMIT:
638 bIterate = 0;
639 ntdb_transaction_commit(ntdb);
640 return 0;
641 case CMD_TRANSACTION_CANCEL:
642 bIterate = 0;
643 ntdb_transaction_cancel(ntdb);
644 return 0;
645 case CMD_ERASE:
646 bIterate = 0;
647 ntdb_traverse(ntdb, do_delete_fn, NULL);
648 return 0;
649 case CMD_DUMP:
650 bIterate = 0;
651 ntdb_traverse(ntdb, print_rec, NULL);
652 return 0;
653 case CMD_INSERT:
654 bIterate = 0;
655 insert_ntdb(arg1, arg1len,arg2,arg2len);
656 return 0;
657 case CMD_MOVE:
658 bIterate = 0;
659 move_rec(arg1,arg1len,arg2);
660 return 0;
661 case CMD_STORE:
662 bIterate = 0;
663 store_ntdb(arg1,arg1len,arg2,arg2len);
664 return 0;
665 case CMD_SHOW:
666 bIterate = 0;
667 show_ntdb(arg1, arg1len);
668 return 0;
669 case CMD_KEYS:
670 ntdb_traverse(ntdb, print_key, NULL);
671 return 0;
672 case CMD_HEXKEYS:
673 ntdb_traverse(ntdb, print_hexkey, NULL);
674 return 0;
675 case CMD_DELETE:
676 bIterate = 0;
677 delete_ntdb(arg1,arg1len);
678 return 0;
679 #if 0
680 case CMD_LIST_HASH_FREE:
681 ntdb_dump_all(ntdb);
682 return 0;
683 case CMD_LIST_FREE:
684 ntdb_printfreelist(ntdb);
685 return 0;
686 #endif
687 case CMD_INFO:
688 info_ntdb();
689 return 0;
690 case CMD_SPEED:
691 speed_ntdb(arg1);
692 return 0;
693 case CMD_MMAP:
694 toggle_mmap();
695 return 0;
696 case CMD_FIRST:
697 bIterate = 1;
698 first_record(ntdb, &iterate_kbuf);
699 return 0;
700 case CMD_NEXT:
701 if (bIterate)
702 next_record(ntdb, &iterate_kbuf);
703 return 0;
704 case CMD_CHECK:
705 check_db(ntdb);
706 return 0;
707 case CMD_HELP:
708 help();
709 return 0;
710 case CMD_CREATE_NTDB:
711 case CMD_OPEN_NTDB:
712 case CMD_SYSTEM:
713 case CMD_QUIT:
715 * unhandled commands. cases included here to avoid compiler
716 * warnings.
718 return 0;
722 return 0;
725 static char *convert_string(char *instring, size_t *sizep)
727 size_t length = 0;
728 char *outp, *inp;
729 char temp[3];
731 outp = inp = instring;
733 while (*inp) {
734 if (*inp == '\\') {
735 inp++;
736 if (*inp && strchr("0123456789abcdefABCDEF",(int)*inp)) {
737 temp[0] = *inp++;
738 temp[1] = '\0';
739 if (*inp && strchr("0123456789abcdefABCDEF",(int)*inp)) {
740 temp[1] = *inp++;
741 temp[2] = '\0';
743 *outp++ = (char)strtol((const char *)temp,NULL,16);
744 } else {
745 *outp++ = *inp++;
747 } else {
748 *outp++ = *inp++;
750 length++;
752 *sizep = length;
753 return instring;
756 int main(int argc, char *argv[])
758 cmdname = "";
759 arg1 = NULL;
760 arg1len = 0;
761 arg2 = NULL;
762 arg2len = 0;
764 if (argv[1]) {
765 cmdname = "open";
766 arg1 = argv[1];
767 do_command();
768 cmdname = "";
769 arg1 = NULL;
772 switch (argc) {
773 case 1:
774 case 2:
775 /* Interactive mode */
776 while ((cmdname = ntdb_getline("ntdb> "))) {
777 arg2 = arg1 = NULL;
778 if ((arg1 = strchr((const char *)cmdname,' ')) != NULL) {
779 arg1++;
780 arg2 = arg1;
781 while (*arg2) {
782 if (*arg2 == ' ') {
783 *arg2++ = '\0';
784 break;
786 if ((*arg2++ == '\\') && (*arg2 == ' ')) {
787 arg2++;
791 if (arg1) arg1 = convert_string(arg1,&arg1len);
792 if (arg2) arg2 = convert_string(arg2,&arg2len);
793 if (do_command()) break;
795 break;
796 case 5:
797 arg2 = convert_string(argv[4],&arg2len);
798 case 4:
799 arg1 = convert_string(argv[3],&arg1len);
800 case 3:
801 cmdname = argv[2];
802 default:
803 do_command();
804 break;
807 if (ntdb) ntdb_close(ntdb);
809 return 0;