ad9e0a73d20c06b78851413813b424c9394e2a8e
[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 }
198
199 enum NTDB_ERROR ntdb_lock_bystring(struct ntdb_context *ntdb,
200                                    const char *keyval)
201 {
202         NTDB_DATA key = string_term_ntdb_data(keyval);
203
204         return ntdb_chainlock(ntdb, key);
205 }
206
207 void ntdb_unlock_bystring(struct ntdb_context *ntdb, const char *keyval)
208 {
209         NTDB_DATA key = string_term_ntdb_data(keyval);
210
211         ntdb_chainunlock(ntdb, key);
212 }
213
214 enum NTDB_ERROR ntdb_delete_bystring(struct ntdb_context *ntdb,
215                                      const char *keystr)
216 {
217         NTDB_DATA key = string_term_ntdb_data(keystr);
218
219         return ntdb_delete(ntdb, key);
220 }
221
222 enum NTDB_ERROR ntdb_store_bystring(struct ntdb_context *ntdb,
223                                     const char *keystr,
224                                     NTDB_DATA data, int nflags)
225 {
226         NTDB_DATA key = string_term_ntdb_data(keystr);
227
228         return ntdb_store(ntdb, key, data, nflags);
229 }
230
231 enum NTDB_ERROR ntdb_fetch_bystring(struct ntdb_context *ntdb,
232                                     const char *keystr,
233                                     NTDB_DATA *data)
234 {
235         NTDB_DATA key = string_term_ntdb_data(keystr);
236
237         return ntdb_fetch(ntdb, key, data);
238 }
239
240 NTSTATUS map_nt_error_from_ntdb(enum NTDB_ERROR err)
241 {
242         NTSTATUS result = NT_STATUS_INTERNAL_ERROR;
243
244         switch (err) {
245         case NTDB_SUCCESS:
246                 result = NT_STATUS_OK;
247                 break;
248         case NTDB_ERR_CORRUPT:
249                 result = NT_STATUS_INTERNAL_DB_CORRUPTION;
250                 break;
251         case NTDB_ERR_IO:
252                 result = NT_STATUS_UNEXPECTED_IO_ERROR;
253                 break;
254         case NTDB_ERR_OOM:
255                 result = NT_STATUS_NO_MEMORY;
256                 break;
257         case NTDB_ERR_EXISTS:
258                 result = NT_STATUS_OBJECT_NAME_COLLISION;
259                 break;
260
261         case NTDB_ERR_LOCK:
262                 /*
263                  * NTDB_ERR_LOCK is very broad, we could for example
264                  * distinguish between fcntl locks and invalid lock
265                  * sequences. So NT_STATUS_FILE_LOCK_CONFLICT is a
266                  * compromise.
267                  */
268                 result = NT_STATUS_FILE_LOCK_CONFLICT;
269                 break;
270         case NTDB_ERR_NOEXIST:
271                 result = NT_STATUS_NOT_FOUND;
272                 break;
273         case NTDB_ERR_EINVAL:
274                 result = NT_STATUS_INVALID_PARAMETER;
275                 break;
276         case NTDB_ERR_RDONLY:
277                 result = NT_STATUS_ACCESS_DENIED;
278                 break;
279         };
280         return result;
281 }