10d60c2b84d3a29d99326948414125e10f0efcad
[ddiss/samba.git] / lib / util / util_ntdb.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    ntdb utility functions
5
6    Copyright (C) Rusty Russell 2012
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 #include "includes.h"
22 #include "util_ntdb.h"
23 #include "lib/param/param.h"
24 #include "replace.h"
25 #include "system/filesys.h"
26
27 /*
28  * This handles NTDB_CLEAR_IF_FIRST.
29  *
30  * It's a bad idea for new code, but S3 uses it quite a bit.
31  */
32 static enum NTDB_ERROR clear_if_first(int fd, void *unused)
33 {
34         /* We hold a lock offset 4 always, so we can tell if anyone else is. */
35         struct flock fl;
36
37         fl.l_type = F_WRLCK;
38         fl.l_whence = SEEK_SET;
39         fl.l_start = 4; /* ACTIVE_LOCK */
40         fl.l_len = 1;
41
42         if (fcntl(fd, F_SETLK, &fl) == 0) {
43                 /* We must be first ones to open it w/ NTDB_CLEAR_IF_FIRST! */
44                 if (ftruncate(fd, 0) != 0) {
45                         return NTDB_ERR_IO;
46                 }
47         }
48         fl.l_type = F_RDLCK;
49         if (fcntl(fd, F_SETLKW, &fl) != 0) {
50                 return NTDB_ERR_IO;
51         }
52         return NTDB_SUCCESS;
53 }
54
55 /* We only need these for the CLEAR_IF_FIRST lock. */
56 static int reacquire_cif_lock(struct ntdb_context *ntdb, bool *fail)
57 {
58         struct flock fl;
59         union ntdb_attribute cif;
60
61         cif.openhook.base.attr = NTDB_ATTRIBUTE_OPENHOOK;
62         cif.openhook.base.next = NULL;
63
64         if (ntdb_get_attribute(ntdb, &cif) != NTDB_SUCCESS
65             || cif.openhook.fn != clear_if_first) {
66                 return 0;
67         }
68
69         /* We hold a lock offset 4 always, so we can tell if anyone else is. */
70         fl.l_type = F_RDLCK;
71         fl.l_whence = SEEK_SET;
72         fl.l_start = 4; /* ACTIVE_LOCK */
73         fl.l_len = 1;
74         if (fcntl(ntdb_fd(ntdb), F_SETLKW, &fl) != 0) {
75                 *fail = true;
76                 return -1;
77         }
78         return 0;
79 }
80
81 /* You only need this on databases with NTDB_CLEAR_IF_FIRST */
82 int ntdb_reopen(struct ntdb_context *ntdb)
83 {
84         bool unused;
85         return reacquire_cif_lock(ntdb, &unused);
86 }
87
88 /* You only need to do this if you have NTDB_CLEAR_IF_FIRST databases, and
89  * the parent will go away before this child. */
90 int ntdb_reopen_all(void)
91 {
92         bool fail = false;
93
94         ntdb_foreach(reacquire_cif_lock, &fail);
95         if (fail)
96                 return -1;
97         return 0;
98 }
99
100 static void *ntdb_talloc(const void *owner, size_t len, void *priv_data)
101 {
102         return talloc_size(owner, len);
103 }
104
105 static void *ntdb_expand(void *old, size_t newlen, void *priv_data)
106 {
107         return talloc_realloc_size(NULL, old, newlen);
108 }
109
110 static void ntdb_free(void *old, void *priv_data)
111 {
112         talloc_free(old);
113 }
114
115 static int ntdb_destroy(struct ntdb_context *ntdb)
116 {
117         ntdb_close(ntdb);
118         return 0;
119 }
120
121 static void ntdb_log(struct ntdb_context *ntdb,
122                      enum ntdb_log_level level,
123                      enum NTDB_ERROR ecode,
124                      const char *message,
125                      void *unused)
126 {
127         int dl;
128         const char *name = ntdb_name(ntdb);
129
130         switch (level) {
131         case NTDB_LOG_USE_ERROR:
132         case NTDB_LOG_ERROR:
133                 dl = 0;
134                 break;
135         case NTDB_LOG_WARNING:
136                 dl = 2;
137                 break;
138         default:
139                 dl = 0;
140         }
141
142         DEBUG(dl, ("ntdb(%s):%s: %s\n", name ? name : "unnamed",
143                    ntdb_errorstr(ecode), message));
144 }
145
146 struct ntdb_context *ntdb_new(TALLOC_CTX *ctx,
147                               const char *name, int ntdb_flags,
148                               int open_flags, mode_t mode,
149                               union ntdb_attribute *attr,
150                               struct loadparm_context *lp_ctx)
151 {
152         union ntdb_attribute log_attr, alloc_attr, open_attr;
153         struct ntdb_context *ntdb;
154
155         if (lp_ctx && !lpcfg_use_mmap(lp_ctx)) {
156                 ntdb_flags |= NTDB_NOMMAP;
157         }
158
159         /* Great hack for speeding testing! */
160         if (getenv("TDB_NO_FSYNC")) {
161                 ntdb_flags |= NTDB_NOSYNC;
162         }
163
164         log_attr.base.next = attr;
165         log_attr.base.attr = NTDB_ATTRIBUTE_LOG;
166         log_attr.log.fn = ntdb_log;
167
168         alloc_attr.base.next = &log_attr;
169         alloc_attr.base.attr = NTDB_ATTRIBUTE_ALLOCATOR;
170         alloc_attr.alloc.alloc = ntdb_talloc;
171         alloc_attr.alloc.expand = ntdb_expand;
172         alloc_attr.alloc.free = ntdb_free;
173
174         if (ntdb_flags & NTDB_CLEAR_IF_FIRST) {
175                 log_attr.base.next = &open_attr;
176                 open_attr.openhook.base.attr = NTDB_ATTRIBUTE_OPENHOOK;
177                 open_attr.openhook.base.next = attr;
178                 open_attr.openhook.fn = clear_if_first;
179                 ntdb_flags &= ~NTDB_CLEAR_IF_FIRST;
180         }
181
182         ntdb = ntdb_open(name, ntdb_flags, open_flags, mode, &alloc_attr);
183         if (!ntdb) {
184                 return NULL;
185         }
186
187         /* We can re-use the tdb's path name for the talloc name */
188         name = ntdb_name(ntdb);
189         if (name) {
190                 talloc_set_name_const(ntdb, name);
191         } else {
192                 talloc_set_name_const(ntdb, "unnamed ntdb");
193         }
194         talloc_set_destructor(ntdb, ntdb_destroy);
195
196         return talloc_steal(ctx, ntdb);
197 }