s3-auth: make_new_system_info_session() not query passdb
[Samba/vl.git] / lib / ntdb / io.c
blob11d5b1f3670ff28e04bd42c95f5a824da3fa38e1
1 /*
2 Unix SMB/CIFS implementation.
4 trivial database library
6 Copyright (C) Andrew Tridgell 1999-2005
7 Copyright (C) Paul `Rusty' Russell 2000
8 Copyright (C) Jeremy Allison 2000-2003
9 Copyright (C) Rusty Russell 2010
11 ** NOTE! The following LGPL license applies to the ntdb
12 ** library. This does NOT imply that all of Samba is released
13 ** under the LGPL
15 This library is free software; you can redistribute it and/or
16 modify it under the terms of the GNU Lesser General Public
17 License as published by the Free Software Foundation; either
18 version 3 of the License, or (at your option) any later version.
20 This library is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 Lesser General Public License for more details.
25 You should have received a copy of the GNU Lesser General Public
26 License along with this library; if not, see <http://www.gnu.org/licenses/>.
28 #include "private.h"
29 #include <ccan/likely/likely.h>
31 void ntdb_munmap(struct ntdb_file *file)
33 if (file->fd == -1)
34 return;
36 if (file->map_ptr) {
37 munmap(file->map_ptr, file->map_size);
38 file->map_ptr = NULL;
42 enum NTDB_ERROR ntdb_mmap(struct ntdb_context *ntdb)
44 int mmap_flags;
46 if (ntdb->flags & NTDB_INTERNAL)
47 return NTDB_SUCCESS;
49 #ifndef HAVE_INCOHERENT_MMAP
50 if (ntdb->flags & NTDB_NOMMAP)
51 return NTDB_SUCCESS;
52 #endif
54 if ((ntdb->open_flags & O_ACCMODE) == O_RDONLY)
55 mmap_flags = PROT_READ;
56 else
57 mmap_flags = PROT_READ | PROT_WRITE;
59 /* size_t can be smaller than off_t. */
60 if ((size_t)ntdb->file->map_size == ntdb->file->map_size) {
61 ntdb->file->map_ptr = mmap(NULL, ntdb->file->map_size,
62 mmap_flags,
63 MAP_SHARED, ntdb->file->fd, 0);
64 } else
65 ntdb->file->map_ptr = MAP_FAILED;
68 * NB. When mmap fails it returns MAP_FAILED *NOT* NULL !!!!
70 if (ntdb->file->map_ptr == MAP_FAILED) {
71 ntdb->file->map_ptr = NULL;
72 #ifdef HAVE_INCOHERENT_MMAP
73 /* Incoherent mmap means everyone must mmap! */
74 return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
75 "ntdb_mmap failed for size %lld (%s)",
76 (long long)ntdb->file->map_size,
77 strerror(errno));
78 #else
79 ntdb_logerr(ntdb, NTDB_SUCCESS, NTDB_LOG_WARNING,
80 "ntdb_mmap failed for size %lld (%s)",
81 (long long)ntdb->file->map_size, strerror(errno));
82 #endif
84 return NTDB_SUCCESS;
87 /* check for an out of bounds access - if it is out of bounds then
88 see if the database has been expanded by someone else and expand
89 if necessary
90 note that "len" is the minimum length needed for the db.
92 If probe is true, len being too large isn't a failure.
94 static enum NTDB_ERROR ntdb_normal_oob(struct ntdb_context *ntdb,
95 ntdb_off_t off, ntdb_len_t len,
96 bool probe)
98 struct stat st;
99 enum NTDB_ERROR ecode;
101 /* We can't hold pointers during this: we could unmap! */
102 assert(!ntdb->direct_access
103 || (ntdb->flags & NTDB_NOLOCK)
104 || ntdb_has_expansion_lock(ntdb));
106 if (len + off < len) {
107 if (probe)
108 return NTDB_SUCCESS;
110 return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
111 "ntdb_oob off %llu len %llu wrap\n",
112 (long long)off, (long long)len);
115 if (ntdb->flags & NTDB_INTERNAL) {
116 if (probe)
117 return NTDB_SUCCESS;
119 ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
120 "ntdb_oob len %lld beyond internal"
121 " alloc size %lld",
122 (long long)(off + len),
123 (long long)ntdb->file->map_size);
124 return NTDB_ERR_IO;
127 ecode = ntdb_lock_expand(ntdb, F_RDLCK);
128 if (ecode != NTDB_SUCCESS) {
129 return ecode;
132 if (fstat(ntdb->file->fd, &st) != 0) {
133 ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
134 "Failed to fstat file: %s", strerror(errno));
135 ntdb_unlock_expand(ntdb, F_RDLCK);
136 return NTDB_ERR_IO;
139 ntdb_unlock_expand(ntdb, F_RDLCK);
141 if (st.st_size < off + len) {
142 if (probe)
143 return NTDB_SUCCESS;
145 ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
146 "ntdb_oob len %llu beyond eof at %llu",
147 (long long)(off + len), (long long)st.st_size);
148 return NTDB_ERR_IO;
151 /* Unmap, update size, remap */
152 ntdb_munmap(ntdb->file);
154 ntdb->file->map_size = st.st_size;
155 return ntdb_mmap(ntdb);
158 /* Endian conversion: we only ever deal with 8 byte quantities */
159 void *ntdb_convert(const struct ntdb_context *ntdb, void *buf, ntdb_len_t size)
161 assert(size % 8 == 0);
162 if (unlikely((ntdb->flags & NTDB_CONVERT)) && buf) {
163 uint64_t i, *p = (uint64_t *)buf;
164 for (i = 0; i < size / 8; i++)
165 p[i] = bswap_64(p[i]);
167 return buf;
170 /* Return first non-zero offset in offset array, or end, or -ve error. */
171 /* FIXME: Return the off? */
172 uint64_t ntdb_find_nonzero_off(struct ntdb_context *ntdb,
173 ntdb_off_t base, uint64_t start, uint64_t end)
175 uint64_t i;
176 const uint64_t *val;
178 /* Zero vs non-zero is the same unconverted: minor optimization. */
179 val = ntdb_access_read(ntdb, base + start * sizeof(ntdb_off_t),
180 (end - start) * sizeof(ntdb_off_t), false);
181 if (NTDB_PTR_IS_ERR(val)) {
182 return NTDB_ERR_TO_OFF(NTDB_PTR_ERR(val));
185 for (i = 0; i < (end - start); i++) {
186 if (val[i])
187 break;
189 ntdb_access_release(ntdb, val);
190 return start + i;
193 /* Return first zero offset in num offset array, or num, or -ve error. */
194 uint64_t ntdb_find_zero_off(struct ntdb_context *ntdb, ntdb_off_t off,
195 uint64_t num)
197 uint64_t i;
198 const uint64_t *val;
200 /* Zero vs non-zero is the same unconverted: minor optimization. */
201 val = ntdb_access_read(ntdb, off, num * sizeof(ntdb_off_t), false);
202 if (NTDB_PTR_IS_ERR(val)) {
203 return NTDB_ERR_TO_OFF(NTDB_PTR_ERR(val));
206 for (i = 0; i < num; i++) {
207 if (!val[i])
208 break;
210 ntdb_access_release(ntdb, val);
211 return i;
214 enum NTDB_ERROR zero_out(struct ntdb_context *ntdb, ntdb_off_t off, ntdb_len_t len)
216 char buf[8192] = { 0 };
217 void *p = ntdb->io->direct(ntdb, off, len, true);
218 enum NTDB_ERROR ecode = NTDB_SUCCESS;
220 assert(!(ntdb->flags & NTDB_RDONLY));
221 if (NTDB_PTR_IS_ERR(p)) {
222 return NTDB_PTR_ERR(p);
224 if (p) {
225 memset(p, 0, len);
226 return ecode;
228 while (len) {
229 unsigned todo = len < sizeof(buf) ? len : sizeof(buf);
230 ecode = ntdb->io->twrite(ntdb, off, buf, todo);
231 if (ecode != NTDB_SUCCESS) {
232 break;
234 len -= todo;
235 off += todo;
237 return ecode;
240 /* write a lump of data at a specified offset */
241 static enum NTDB_ERROR ntdb_write(struct ntdb_context *ntdb, ntdb_off_t off,
242 const void *buf, ntdb_len_t len)
244 enum NTDB_ERROR ecode;
246 if (ntdb->flags & NTDB_RDONLY) {
247 return ntdb_logerr(ntdb, NTDB_ERR_RDONLY, NTDB_LOG_USE_ERROR,
248 "Write to read-only database");
251 ecode = ntdb_oob(ntdb, off, len, false);
252 if (ecode != NTDB_SUCCESS) {
253 return ecode;
256 if (ntdb->file->map_ptr) {
257 memcpy(off + (char *)ntdb->file->map_ptr, buf, len);
258 } else {
259 #ifdef HAVE_INCOHERENT_MMAP
260 return NTDB_ERR_IO;
261 #else
262 ssize_t ret;
263 ret = pwrite(ntdb->file->fd, buf, len, off);
264 if (ret != len) {
265 /* This shouldn't happen: we avoid sparse files. */
266 if (ret >= 0)
267 errno = ENOSPC;
269 return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
270 "ntdb_write: %zi at %zu len=%zu (%s)",
271 ret, (size_t)off, (size_t)len,
272 strerror(errno));
274 #endif
276 return NTDB_SUCCESS;
279 /* read a lump of data at a specified offset */
280 static enum NTDB_ERROR ntdb_read(struct ntdb_context *ntdb, ntdb_off_t off,
281 void *buf, ntdb_len_t len)
283 enum NTDB_ERROR ecode;
285 ecode = ntdb_oob(ntdb, off, len, false);
286 if (ecode != NTDB_SUCCESS) {
287 return ecode;
290 if (ntdb->file->map_ptr) {
291 memcpy(buf, off + (char *)ntdb->file->map_ptr, len);
292 } else {
293 #ifdef HAVE_INCOHERENT_MMAP
294 return NTDB_ERR_IO;
295 #else
296 ssize_t r = pread(ntdb->file->fd, buf, len, off);
297 if (r != len) {
298 return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
299 "ntdb_read failed with %zi at %zu "
300 "len=%zu (%s) map_size=%zu",
301 r, (size_t)off, (size_t)len,
302 strerror(errno),
303 (size_t)ntdb->file->map_size);
305 #endif
307 return NTDB_SUCCESS;
310 enum NTDB_ERROR ntdb_write_convert(struct ntdb_context *ntdb, ntdb_off_t off,
311 const void *rec, size_t len)
313 enum NTDB_ERROR ecode;
315 if (unlikely((ntdb->flags & NTDB_CONVERT))) {
316 void *conv = ntdb->alloc_fn(ntdb, len, ntdb->alloc_data);
317 if (!conv) {
318 return ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_ERROR,
319 "ntdb_write: no memory converting"
320 " %zu bytes", len);
322 memcpy(conv, rec, len);
323 ecode = ntdb->io->twrite(ntdb, off,
324 ntdb_convert(ntdb, conv, len), len);
325 ntdb->free_fn(conv, ntdb->alloc_data);
326 } else {
327 ecode = ntdb->io->twrite(ntdb, off, rec, len);
329 return ecode;
332 enum NTDB_ERROR ntdb_read_convert(struct ntdb_context *ntdb, ntdb_off_t off,
333 void *rec, size_t len)
335 enum NTDB_ERROR ecode = ntdb->io->tread(ntdb, off, rec, len);
336 ntdb_convert(ntdb, rec, len);
337 return ecode;
340 static void *_ntdb_alloc_read(struct ntdb_context *ntdb, ntdb_off_t offset,
341 ntdb_len_t len, unsigned int prefix)
343 unsigned char *buf;
344 enum NTDB_ERROR ecode;
346 /* some systems don't like zero length malloc */
347 buf = ntdb->alloc_fn(ntdb, prefix + len ? prefix + len : 1,
348 ntdb->alloc_data);
349 if (!buf) {
350 ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_USE_ERROR,
351 "ntdb_alloc_read alloc failed len=%zu",
352 (size_t)(prefix + len));
353 return NTDB_ERR_PTR(NTDB_ERR_OOM);
354 } else {
355 ecode = ntdb->io->tread(ntdb, offset, buf+prefix, len);
356 if (unlikely(ecode != NTDB_SUCCESS)) {
357 ntdb->free_fn(buf, ntdb->alloc_data);
358 return NTDB_ERR_PTR(ecode);
361 return buf;
364 /* read a lump of data, allocating the space for it */
365 void *ntdb_alloc_read(struct ntdb_context *ntdb, ntdb_off_t offset, ntdb_len_t len)
367 return _ntdb_alloc_read(ntdb, offset, len, 0);
370 static enum NTDB_ERROR fill(struct ntdb_context *ntdb,
371 const void *buf, size_t size,
372 ntdb_off_t off, ntdb_len_t len)
374 while (len) {
375 size_t n = len > size ? size : len;
376 ssize_t ret = pwrite(ntdb->file->fd, buf, n, off);
377 if (ret != n) {
378 if (ret >= 0)
379 errno = ENOSPC;
381 return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
382 "fill failed:"
383 " %zi at %zu len=%zu (%s)",
384 ret, (size_t)off, (size_t)len,
385 strerror(errno));
387 len -= n;
388 off += n;
390 return NTDB_SUCCESS;
393 /* expand a file. we prefer to use ftruncate, as that is what posix
394 says to use for mmap expansion */
395 static enum NTDB_ERROR ntdb_expand_file(struct ntdb_context *ntdb,
396 ntdb_len_t addition)
398 char buf[8192];
399 enum NTDB_ERROR ecode;
401 assert((ntdb->file->map_size + addition) % NTDB_PGSIZE == 0);
402 if (ntdb->flags & NTDB_RDONLY) {
403 return ntdb_logerr(ntdb, NTDB_ERR_RDONLY, NTDB_LOG_USE_ERROR,
404 "Expand on read-only database");
407 if (ntdb->flags & NTDB_INTERNAL) {
408 char *new = ntdb->expand_fn(ntdb->file->map_ptr,
409 ntdb->file->map_size + addition,
410 ntdb->alloc_data);
411 if (!new) {
412 return ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_ERROR,
413 "No memory to expand database");
415 ntdb->file->map_ptr = new;
416 ntdb->file->map_size += addition;
417 return NTDB_SUCCESS;
418 } else {
419 /* Unmap before trying to write; old NTDB claimed OpenBSD had
420 * problem with this otherwise. */
421 ntdb_munmap(ntdb->file);
423 /* If this fails, we try to fill anyway. */
424 if (ftruncate(ntdb->file->fd, ntdb->file->map_size + addition))
427 /* now fill the file with something. This ensures that the
428 file isn't sparse, which would be very bad if we ran out of
429 disk. This must be done with write, not via mmap */
430 memset(buf, 0x43, sizeof(buf));
431 ecode = fill(ntdb, buf, sizeof(buf), ntdb->file->map_size,
432 addition);
433 if (ecode != NTDB_SUCCESS)
434 return ecode;
435 ntdb->file->map_size += addition;
436 return ntdb_mmap(ntdb);
440 const void *ntdb_access_read(struct ntdb_context *ntdb,
441 ntdb_off_t off, ntdb_len_t len, bool convert)
443 void *ret = NULL;
445 if (likely(!(ntdb->flags & NTDB_CONVERT))) {
446 ret = ntdb->io->direct(ntdb, off, len, false);
448 if (NTDB_PTR_IS_ERR(ret)) {
449 return ret;
452 if (!ret) {
453 struct ntdb_access_hdr *hdr;
454 hdr = _ntdb_alloc_read(ntdb, off, len, sizeof(*hdr));
455 if (NTDB_PTR_IS_ERR(hdr)) {
456 return hdr;
458 hdr->next = ntdb->access;
459 ntdb->access = hdr;
460 ret = hdr + 1;
461 if (convert) {
462 ntdb_convert(ntdb, (void *)ret, len);
464 } else
465 ntdb->direct_access++;
467 return ret;
470 void *ntdb_access_write(struct ntdb_context *ntdb,
471 ntdb_off_t off, ntdb_len_t len, bool convert)
473 void *ret = NULL;
475 if (ntdb->flags & NTDB_RDONLY) {
476 ntdb_logerr(ntdb, NTDB_ERR_RDONLY, NTDB_LOG_USE_ERROR,
477 "Write to read-only database");
478 return NTDB_ERR_PTR(NTDB_ERR_RDONLY);
481 if (likely(!(ntdb->flags & NTDB_CONVERT))) {
482 ret = ntdb->io->direct(ntdb, off, len, true);
484 if (NTDB_PTR_IS_ERR(ret)) {
485 return ret;
489 if (!ret) {
490 struct ntdb_access_hdr *hdr;
491 hdr = _ntdb_alloc_read(ntdb, off, len, sizeof(*hdr));
492 if (NTDB_PTR_IS_ERR(hdr)) {
493 return hdr;
495 hdr->next = ntdb->access;
496 ntdb->access = hdr;
497 hdr->off = off;
498 hdr->len = len;
499 hdr->convert = convert;
500 ret = hdr + 1;
501 if (convert)
502 ntdb_convert(ntdb, (void *)ret, len);
503 } else
504 ntdb->direct_access++;
506 return ret;
509 static struct ntdb_access_hdr **find_hdr(struct ntdb_context *ntdb, const void *p)
511 struct ntdb_access_hdr **hp;
513 for (hp = &ntdb->access; *hp; hp = &(*hp)->next) {
514 if (*hp + 1 == p)
515 return hp;
517 return NULL;
520 void ntdb_access_release(struct ntdb_context *ntdb, const void *p)
522 struct ntdb_access_hdr *hdr, **hp = find_hdr(ntdb, p);
524 if (hp) {
525 hdr = *hp;
526 *hp = hdr->next;
527 ntdb->free_fn(hdr, ntdb->alloc_data);
528 } else
529 ntdb->direct_access--;
532 enum NTDB_ERROR ntdb_access_commit(struct ntdb_context *ntdb, void *p)
534 struct ntdb_access_hdr *hdr, **hp = find_hdr(ntdb, p);
535 enum NTDB_ERROR ecode;
537 if (hp) {
538 hdr = *hp;
539 if (hdr->convert)
540 ecode = ntdb_write_convert(ntdb, hdr->off, p, hdr->len);
541 else
542 ecode = ntdb_write(ntdb, hdr->off, p, hdr->len);
543 *hp = hdr->next;
544 ntdb->free_fn(hdr, ntdb->alloc_data);
545 } else {
546 ntdb->direct_access--;
547 ecode = NTDB_SUCCESS;
550 return ecode;
553 static void *ntdb_direct(struct ntdb_context *ntdb, ntdb_off_t off, size_t len,
554 bool write_mode)
556 enum NTDB_ERROR ecode;
558 if (unlikely(!ntdb->file->map_ptr))
559 return NULL;
561 ecode = ntdb_oob(ntdb, off, len, false);
562 if (unlikely(ecode != NTDB_SUCCESS))
563 return NTDB_ERR_PTR(ecode);
564 return (char *)ntdb->file->map_ptr + off;
567 static ntdb_off_t ntdb_read_normal_off(struct ntdb_context *ntdb,
568 ntdb_off_t off)
570 ntdb_off_t ret;
571 enum NTDB_ERROR ecode;
572 ntdb_off_t *p;
574 p = ntdb_direct(ntdb, off, sizeof(*p), false);
575 if (NTDB_PTR_IS_ERR(p)) {
576 return NTDB_ERR_TO_OFF(NTDB_PTR_ERR(p));
578 if (likely(p)) {
579 return *p;
582 ecode = ntdb_read(ntdb, off, &ret, sizeof(ret));
583 if (ecode != NTDB_SUCCESS) {
584 return NTDB_ERR_TO_OFF(ecode);
586 return ret;
589 static ntdb_off_t ntdb_read_convert_off(struct ntdb_context *ntdb,
590 ntdb_off_t off)
592 ntdb_off_t ret;
593 enum NTDB_ERROR ecode;
595 ecode = ntdb_read_convert(ntdb, off, &ret, sizeof(ret));
596 if (ecode != NTDB_SUCCESS) {
597 return NTDB_ERR_TO_OFF(ecode);
599 return ret;
602 static enum NTDB_ERROR ntdb_write_normal_off(struct ntdb_context *ntdb,
603 ntdb_off_t off, ntdb_off_t val)
605 ntdb_off_t *p;
607 p = ntdb_direct(ntdb, off, sizeof(*p), true);
608 if (NTDB_PTR_IS_ERR(p)) {
609 return NTDB_PTR_ERR(p);
611 if (likely(p)) {
612 *p = val;
613 return NTDB_SUCCESS;
615 return ntdb_write(ntdb, off, &val, sizeof(val));
618 static enum NTDB_ERROR ntdb_write_convert_off(struct ntdb_context *ntdb,
619 ntdb_off_t off, ntdb_off_t val)
621 return ntdb_write_convert(ntdb, off, &val, sizeof(val));
624 void ntdb_inc_seqnum(struct ntdb_context *ntdb)
626 ntdb_off_t seq;
628 if (likely(!(ntdb->flags & NTDB_CONVERT))) {
629 int64_t *direct;
631 direct = ntdb->io->direct(ntdb,
632 offsetof(struct ntdb_header, seqnum),
633 sizeof(*direct), true);
634 if (likely(direct)) {
635 /* Don't let it go negative, even briefly */
636 if (unlikely((*direct) + 1) < 0)
637 *direct = 0;
638 (*direct)++;
639 return;
643 seq = ntdb_read_off(ntdb, offsetof(struct ntdb_header, seqnum));
644 if (!NTDB_OFF_IS_ERR(seq)) {
645 seq++;
646 if (unlikely((int64_t)seq < 0))
647 seq = 0;
648 ntdb_write_off(ntdb, offsetof(struct ntdb_header, seqnum), seq);
652 static const struct ntdb_methods io_methods = {
653 ntdb_read,
654 ntdb_write,
655 ntdb_normal_oob,
656 ntdb_expand_file,
657 ntdb_direct,
658 ntdb_read_normal_off,
659 ntdb_write_normal_off,
662 static const struct ntdb_methods io_convert_methods = {
663 ntdb_read,
664 ntdb_write,
665 ntdb_normal_oob,
666 ntdb_expand_file,
667 ntdb_direct,
668 ntdb_read_convert_off,
669 ntdb_write_convert_off,
673 initialise the default methods table
675 void ntdb_io_init(struct ntdb_context *ntdb)
677 if (ntdb->flags & NTDB_CONVERT)
678 ntdb->io = &io_convert_methods;
679 else
680 ntdb->io = &io_methods;