2 Unix SMB/CIFS implementation.
6 Copyright (C) Rusty Russell 2012
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.
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.
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/>.
22 #include "util_ntdb.h"
23 #include "lib/param/param.h"
25 #include "system/filesys.h"
28 * This handles NTDB_CLEAR_IF_FIRST.
30 * It's a bad idea for new code, but S3 uses it quite a bit.
32 static enum NTDB_ERROR
clear_if_first(int fd
, void *unused
)
34 /* We hold a lock offset 4 always, so we can tell if anyone else is. */
38 fl
.l_whence
= SEEK_SET
;
39 fl
.l_start
= 4; /* ACTIVE_LOCK */
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) {
49 if (fcntl(fd
, F_SETLKW
, &fl
) != 0) {
55 /* We only need these for the CLEAR_IF_FIRST lock. */
56 static int reacquire_cif_lock(struct ntdb_context
*ntdb
, bool *fail
)
59 union ntdb_attribute cif
;
61 cif
.openhook
.base
.attr
= NTDB_ATTRIBUTE_OPENHOOK
;
62 cif
.openhook
.base
.next
= NULL
;
64 if (ntdb_get_attribute(ntdb
, &cif
) != NTDB_SUCCESS
65 || cif
.openhook
.fn
!= clear_if_first
) {
69 /* We hold a lock offset 4 always, so we can tell if anyone else is. */
71 fl
.l_whence
= SEEK_SET
;
72 fl
.l_start
= 4; /* ACTIVE_LOCK */
74 if (fcntl(ntdb_fd(ntdb
), F_SETLKW
, &fl
) != 0) {
81 /* You only need this on databases with NTDB_CLEAR_IF_FIRST */
82 int ntdb_reopen(struct ntdb_context
*ntdb
)
85 return reacquire_cif_lock(ntdb
, &unused
);
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)
94 ntdb_foreach(reacquire_cif_lock
, &fail
);
100 static void *ntdb_talloc(const void *owner
, size_t len
, void *priv_data
)
102 return talloc_size(owner
, len
);
105 static void *ntdb_expand(void *old
, size_t newlen
, void *priv_data
)
107 return talloc_realloc_size(NULL
, old
, newlen
);
110 static void ntdb_free(void *old
, void *priv_data
)
115 static int ntdb_destroy(struct ntdb_context
*ntdb
)
121 static void ntdb_log(struct ntdb_context
*ntdb
,
122 enum ntdb_log_level level
,
123 enum NTDB_ERROR ecode
,
128 const char *name
= ntdb_name(ntdb
);
131 case NTDB_LOG_USE_ERROR
:
135 case NTDB_LOG_WARNING
:
142 DEBUG(dl
, ("ntdb(%s):%s: %s\n", name
? name
: "unnamed",
143 ntdb_errorstr(ecode
), message
));
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
)
152 union ntdb_attribute log_attr
, alloc_attr
, open_attr
;
153 struct ntdb_context
*ntdb
;
155 if (lp_ctx
&& !lpcfg_use_mmap(lp_ctx
)) {
156 ntdb_flags
|= NTDB_NOMMAP
;
159 /* Great hack for speeding testing! */
160 if (getenv("TDB_NO_FSYNC")) {
161 ntdb_flags
|= NTDB_NOSYNC
;
164 log_attr
.base
.next
= attr
;
165 log_attr
.base
.attr
= NTDB_ATTRIBUTE_LOG
;
166 log_attr
.log
.fn
= ntdb_log
;
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
;
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
;
182 ntdb
= ntdb_open(name
, ntdb_flags
, open_flags
, mode
, &alloc_attr
);
187 /* We can re-use the tdb's path name for the talloc name */
188 name
= ntdb_name(ntdb
);
190 talloc_set_name_const(ntdb
, name
);
192 talloc_set_name_const(ntdb
, "unnamed ntdb");
194 talloc_set_destructor(ntdb
, ntdb_destroy
);
196 return talloc_steal(ctx
, ntdb
);
199 enum NTDB_ERROR
ntdb_lock_bystring(struct ntdb_context
*ntdb
,
202 NTDB_DATA key
= string_term_ntdb_data(keyval
);
204 return ntdb_chainlock(ntdb
, key
);
207 void ntdb_unlock_bystring(struct ntdb_context
*ntdb
, const char *keyval
)
209 NTDB_DATA key
= string_term_ntdb_data(keyval
);
211 ntdb_chainunlock(ntdb
, key
);
214 enum NTDB_ERROR
ntdb_delete_bystring(struct ntdb_context
*ntdb
,
217 NTDB_DATA key
= string_term_ntdb_data(keystr
);
219 return ntdb_delete(ntdb
, key
);
222 enum NTDB_ERROR
ntdb_store_bystring(struct ntdb_context
*ntdb
,
224 NTDB_DATA data
, int nflags
)
226 NTDB_DATA key
= string_term_ntdb_data(keystr
);
228 return ntdb_store(ntdb
, key
, data
, nflags
);
231 enum NTDB_ERROR
ntdb_fetch_bystring(struct ntdb_context
*ntdb
,
235 NTDB_DATA key
= string_term_ntdb_data(keystr
);
237 return ntdb_fetch(ntdb
, key
, data
);
240 enum NTDB_ERROR
ntdb_fetch_int32(struct ntdb_context
*ntdb
,
241 const char *keystr
, int32_t *val
)
246 err
= ntdb_fetch(ntdb
, string_term_ntdb_data(keystr
), &data
);
247 if (err
== NTDB_SUCCESS
) {
248 if (data
.dsize
!= sizeof(*val
)) {
249 err
= NTDB_ERR_CORRUPT
;
251 *val
= IVAL(data
.dptr
,0);
253 talloc_free(data
.dptr
);
258 enum NTDB_ERROR
ntdb_store_int32(struct ntdb_context
*ntdb
,
259 const char *keystr
, int32_t val
)
264 SIVAL(&v_store
, 0, val
);
265 data
= ntdb_mkdata(&v_store
, sizeof(v_store
));
266 key
= string_term_ntdb_data(keystr
);
268 return ntdb_store(ntdb
, key
, data
, NTDB_REPLACE
);
271 enum NTDB_ERROR
ntdb_add_int32_atomic(struct ntdb_context
*ntdb
,
273 int32_t *oldval
, int32_t addval
)
278 err
= ntdb_lock_bystring(ntdb
, keystr
);
283 err
= ntdb_fetch_int32(ntdb
, keystr
, &val
);
285 if (err
== NTDB_ERR_NOEXIST
) {
286 /* Start with 'old' value */
292 /* It worked, set return value (oldval) to tdb data */
296 /* Increase value and store for next time. */
298 err
= ntdb_store_int32(ntdb
, keystr
, val
);
301 ntdb_unlock_bystring(ntdb
, keystr
);
305 NTSTATUS
map_nt_error_from_ntdb(enum NTDB_ERROR err
)
307 NTSTATUS result
= NT_STATUS_INTERNAL_ERROR
;
311 result
= NT_STATUS_OK
;
313 case NTDB_ERR_CORRUPT
:
314 result
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
317 result
= NT_STATUS_UNEXPECTED_IO_ERROR
;
320 result
= NT_STATUS_NO_MEMORY
;
322 case NTDB_ERR_EXISTS
:
323 result
= NT_STATUS_OBJECT_NAME_COLLISION
;
328 * NTDB_ERR_LOCK is very broad, we could for example
329 * distinguish between fcntl locks and invalid lock
330 * sequences. So NT_STATUS_FILE_LOCK_CONFLICT is a
333 result
= NT_STATUS_FILE_LOCK_CONFLICT
;
335 case NTDB_ERR_NOEXIST
:
336 result
= NT_STATUS_NOT_FOUND
;
338 case NTDB_ERR_EINVAL
:
339 result
= NT_STATUS_INVALID_PARAMETER
;
341 case NTDB_ERR_RDONLY
:
342 result
= NT_STATUS_ACCESS_DENIED
;