open.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. #include "private.h"
  2. #include <ccan/hash/hash.h>
  3. #include <assert.h>
  4. /* all lock info, to detect double-opens (fcntl file don't nest!) */
  5. static struct tdb_file *files = NULL;
  6. static struct tdb_file *find_file(dev_t device, ino_t ino)
  7. {
  8. struct tdb_file *i;
  9. for (i = files; i; i = i->next) {
  10. if (i->device == device && i->inode == ino) {
  11. i->refcnt++;
  12. break;
  13. }
  14. }
  15. return i;
  16. }
  17. static bool read_all(int fd, void *buf, size_t len)
  18. {
  19. while (len) {
  20. ssize_t ret;
  21. ret = read(fd, buf, len);
  22. if (ret < 0)
  23. return false;
  24. if (ret == 0) {
  25. /* ETOOSHORT? */
  26. errno = EWOULDBLOCK;
  27. return false;
  28. }
  29. buf = (char *)buf + ret;
  30. len -= ret;
  31. }
  32. return true;
  33. }
  34. static uint64_t random_number(struct tdb_context *tdb)
  35. {
  36. int fd;
  37. uint64_t ret = 0;
  38. struct timeval now;
  39. fd = open("/dev/urandom", O_RDONLY);
  40. if (fd >= 0) {
  41. if (read_all(fd, &ret, sizeof(ret))) {
  42. close(fd);
  43. return ret;
  44. }
  45. close(fd);
  46. }
  47. /* FIXME: Untested! Based on Wikipedia protocol description! */
  48. fd = open("/dev/egd-pool", O_RDWR);
  49. if (fd >= 0) {
  50. /* Command is 1, next byte is size we want to read. */
  51. char cmd[2] = { 1, sizeof(uint64_t) };
  52. if (write(fd, cmd, sizeof(cmd)) == sizeof(cmd)) {
  53. char reply[1 + sizeof(uint64_t)];
  54. int r = read(fd, reply, sizeof(reply));
  55. if (r > 1) {
  56. /* Copy at least some bytes. */
  57. memcpy(&ret, reply+1, r - 1);
  58. if (reply[0] == sizeof(uint64_t)
  59. && r == sizeof(reply)) {
  60. close(fd);
  61. return ret;
  62. }
  63. }
  64. }
  65. close(fd);
  66. }
  67. /* Fallback: pid and time. */
  68. gettimeofday(&now, NULL);
  69. ret = getpid() * 100132289ULL + now.tv_sec * 1000000ULL + now.tv_usec;
  70. tdb_logerr(tdb, TDB_SUCCESS, TDB_LOG_WARNING,
  71. "tdb_open: random from getpid and time");
  72. return ret;
  73. }
  74. struct new_database {
  75. struct tdb_header hdr;
  76. struct tdb_freetable ftable;
  77. };
  78. /* initialise a new database */
  79. static enum TDB_ERROR tdb_new_database(struct tdb_context *tdb,
  80. struct tdb_attribute_seed *seed,
  81. struct tdb_header *hdr)
  82. {
  83. /* We make it up in memory, then write it out if not internal */
  84. struct new_database newdb;
  85. unsigned int magic_len;
  86. ssize_t rlen;
  87. enum TDB_ERROR ecode;
  88. /* Fill in the header */
  89. newdb.hdr.version = TDB_VERSION;
  90. if (seed)
  91. newdb.hdr.hash_seed = seed->seed;
  92. else
  93. newdb.hdr.hash_seed = random_number(tdb);
  94. newdb.hdr.hash_test = TDB_HASH_MAGIC;
  95. newdb.hdr.hash_test = tdb->hash_fn(&newdb.hdr.hash_test,
  96. sizeof(newdb.hdr.hash_test),
  97. newdb.hdr.hash_seed,
  98. tdb->hash_data);
  99. newdb.hdr.recovery = 0;
  100. newdb.hdr.features_used = newdb.hdr.features_offered = TDB_FEATURE_MASK;
  101. newdb.hdr.seqnum = 0;
  102. memset(newdb.hdr.reserved, 0, sizeof(newdb.hdr.reserved));
  103. /* Initial hashes are empty. */
  104. memset(newdb.hdr.hashtable, 0, sizeof(newdb.hdr.hashtable));
  105. /* Free is empty. */
  106. newdb.hdr.free_table = offsetof(struct new_database, ftable);
  107. memset(&newdb.ftable, 0, sizeof(newdb.ftable));
  108. ecode = set_header(NULL, &newdb.ftable.hdr, TDB_FTABLE_MAGIC, 0,
  109. sizeof(newdb.ftable) - sizeof(newdb.ftable.hdr),
  110. sizeof(newdb.ftable) - sizeof(newdb.ftable.hdr),
  111. 0);
  112. if (ecode != TDB_SUCCESS) {
  113. return ecode;
  114. }
  115. /* Magic food */
  116. memset(newdb.hdr.magic_food, 0, sizeof(newdb.hdr.magic_food));
  117. strcpy(newdb.hdr.magic_food, TDB_MAGIC_FOOD);
  118. /* This creates an endian-converted database, as if read from disk */
  119. magic_len = sizeof(newdb.hdr.magic_food);
  120. tdb_convert(tdb,
  121. (char *)&newdb.hdr + magic_len, sizeof(newdb) - magic_len);
  122. *hdr = newdb.hdr;
  123. if (tdb->flags & TDB_INTERNAL) {
  124. tdb->file->map_size = sizeof(newdb);
  125. tdb->file->map_ptr = malloc(tdb->file->map_size);
  126. if (!tdb->file->map_ptr) {
  127. return tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
  128. "tdb_new_database:"
  129. " failed to allocate");
  130. }
  131. memcpy(tdb->file->map_ptr, &newdb, tdb->file->map_size);
  132. return TDB_SUCCESS;
  133. }
  134. if (lseek(tdb->file->fd, 0, SEEK_SET) == -1) {
  135. return tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
  136. "tdb_new_database:"
  137. " failed to seek: %s", strerror(errno));
  138. }
  139. if (ftruncate(tdb->file->fd, 0) == -1) {
  140. return tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
  141. "tdb_new_database:"
  142. " failed to truncate: %s", strerror(errno));
  143. }
  144. rlen = write(tdb->file->fd, &newdb, sizeof(newdb));
  145. if (rlen != sizeof(newdb)) {
  146. if (rlen >= 0)
  147. errno = ENOSPC;
  148. return tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
  149. "tdb_new_database: %zi writing header: %s",
  150. rlen, strerror(errno));
  151. }
  152. return TDB_SUCCESS;
  153. }
  154. static enum TDB_ERROR tdb_new_file(struct tdb_context *tdb)
  155. {
  156. tdb->file = malloc(sizeof(*tdb->file));
  157. if (!tdb->file)
  158. return tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
  159. "tdb_open: cannot alloc tdb_file structure");
  160. tdb->file->num_lockrecs = 0;
  161. tdb->file->lockrecs = NULL;
  162. tdb->file->allrecord_lock.count = 0;
  163. tdb->file->refcnt = 1;
  164. return TDB_SUCCESS;
  165. }
  166. enum TDB_ERROR tdb_set_attribute(struct tdb_context *tdb,
  167. const union tdb_attribute *attr)
  168. {
  169. switch (attr->base.attr) {
  170. case TDB_ATTRIBUTE_LOG:
  171. tdb->log_fn = attr->log.fn;
  172. tdb->log_data = attr->log.data;
  173. break;
  174. case TDB_ATTRIBUTE_HASH:
  175. case TDB_ATTRIBUTE_SEED:
  176. case TDB_ATTRIBUTE_OPENHOOK:
  177. return tdb->last_error
  178. = tdb_logerr(tdb, TDB_ERR_EINVAL,
  179. TDB_LOG_USE_ERROR,
  180. "tdb_set_attribute:"
  181. " cannot set %s after opening",
  182. attr->base.attr == TDB_ATTRIBUTE_HASH
  183. ? "TDB_ATTRIBUTE_HASH"
  184. : attr->base.attr == TDB_ATTRIBUTE_SEED
  185. ? "TDB_ATTRIBUTE_SEED"
  186. : "TDB_ATTRIBUTE_OPENHOOK");
  187. case TDB_ATTRIBUTE_STATS:
  188. return tdb->last_error
  189. = tdb_logerr(tdb, TDB_ERR_EINVAL,
  190. TDB_LOG_USE_ERROR,
  191. "tdb_set_attribute:"
  192. " cannot set TDB_ATTRIBUTE_STATS");
  193. case TDB_ATTRIBUTE_FLOCK:
  194. tdb->lock_fn = attr->flock.lock;
  195. tdb->unlock_fn = attr->flock.unlock;
  196. tdb->lock_data = attr->flock.data;
  197. break;
  198. default:
  199. return tdb->last_error
  200. = tdb_logerr(tdb, TDB_ERR_EINVAL,
  201. TDB_LOG_USE_ERROR,
  202. "tdb_set_attribute:"
  203. " unknown attribute type %u",
  204. attr->base.attr);
  205. }
  206. return TDB_SUCCESS;
  207. }
  208. static uint64_t jenkins_hash(const void *key, size_t length, uint64_t seed,
  209. void *unused)
  210. {
  211. uint64_t ret;
  212. /* hash64_stable assumes lower bits are more important; they are a
  213. * slightly better hash. We use the upper bits first, so swap them. */
  214. ret = hash64_stable((const unsigned char *)key, length, seed);
  215. return (ret >> 32) | (ret << 32);
  216. }
  217. enum TDB_ERROR tdb_get_attribute(struct tdb_context *tdb,
  218. union tdb_attribute *attr)
  219. {
  220. switch (attr->base.attr) {
  221. case TDB_ATTRIBUTE_LOG:
  222. if (!tdb->log_fn)
  223. return tdb->last_error = TDB_ERR_NOEXIST;
  224. attr->log.fn = tdb->log_fn;
  225. attr->log.data = tdb->log_data;
  226. break;
  227. case TDB_ATTRIBUTE_HASH:
  228. attr->hash.fn = tdb->hash_fn;
  229. attr->hash.data = tdb->hash_data;
  230. break;
  231. case TDB_ATTRIBUTE_SEED:
  232. attr->seed.seed = tdb->hash_seed;
  233. break;
  234. case TDB_ATTRIBUTE_OPENHOOK:
  235. return tdb->last_error
  236. = tdb_logerr(tdb, TDB_ERR_EINVAL,
  237. TDB_LOG_USE_ERROR,
  238. "tdb_get_attribute:"
  239. " cannot get TDB_ATTRIBUTE_OPENHOOK");
  240. case TDB_ATTRIBUTE_STATS: {
  241. size_t size = attr->stats.size;
  242. if (size > tdb->stats.size)
  243. size = tdb->stats.size;
  244. memcpy(&attr->stats, &tdb->stats, size);
  245. break;
  246. }
  247. case TDB_ATTRIBUTE_FLOCK:
  248. attr->flock.lock = tdb->lock_fn;
  249. attr->flock.unlock = tdb->unlock_fn;
  250. attr->flock.data = tdb->lock_data;
  251. break;
  252. default:
  253. return tdb->last_error
  254. = tdb_logerr(tdb, TDB_ERR_EINVAL,
  255. TDB_LOG_USE_ERROR,
  256. "tdb_get_attribute:"
  257. " unknown attribute type %u",
  258. attr->base.attr);
  259. }
  260. attr->base.next = NULL;
  261. return TDB_SUCCESS;
  262. }
  263. void tdb_unset_attribute(struct tdb_context *tdb,
  264. enum tdb_attribute_type type)
  265. {
  266. switch (type) {
  267. case TDB_ATTRIBUTE_LOG:
  268. tdb->log_fn = NULL;
  269. break;
  270. case TDB_ATTRIBUTE_HASH:
  271. case TDB_ATTRIBUTE_SEED:
  272. case TDB_ATTRIBUTE_OPENHOOK:
  273. tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR,
  274. "tdb_unset_attribute: cannot unset %s after opening",
  275. type == TDB_ATTRIBUTE_HASH
  276. ? "TDB_ATTRIBUTE_HASH"
  277. : type == TDB_ATTRIBUTE_SEED
  278. ? "TDB_ATTRIBUTE_SEED"
  279. : "TDB_ATTRIBUTE_OPENHOOK");
  280. break;
  281. case TDB_ATTRIBUTE_STATS:
  282. tdb_logerr(tdb, TDB_ERR_EINVAL,
  283. TDB_LOG_USE_ERROR,
  284. "tdb_unset_attribute:"
  285. "cannot unset TDB_ATTRIBUTE_STATS");
  286. break;
  287. case TDB_ATTRIBUTE_FLOCK:
  288. tdb->lock_fn = tdb_fcntl_lock;
  289. tdb->unlock_fn = tdb_fcntl_unlock;
  290. break;
  291. default:
  292. tdb_logerr(tdb, TDB_ERR_EINVAL,
  293. TDB_LOG_USE_ERROR,
  294. "tdb_unset_attribute: unknown attribute type %u",
  295. type);
  296. }
  297. }
  298. struct tdb_context *tdb_open(const char *name, int tdb_flags,
  299. int open_flags, mode_t mode,
  300. union tdb_attribute *attr)
  301. {
  302. struct tdb_context *tdb;
  303. struct stat st;
  304. int saved_errno = 0;
  305. uint64_t hash_test;
  306. unsigned v;
  307. ssize_t rlen;
  308. struct tdb_header hdr;
  309. struct tdb_attribute_seed *seed = NULL;
  310. struct tdb_attribute_openhook *openhook = NULL;
  311. tdb_bool_err berr;
  312. enum TDB_ERROR ecode;
  313. tdb = malloc(sizeof(*tdb) + (name ? strlen(name) + 1 : 0));
  314. if (!tdb) {
  315. /* Can't log this */
  316. errno = ENOMEM;
  317. return NULL;
  318. }
  319. /* Set name immediately for logging functions. */
  320. if (name) {
  321. tdb->name = strcpy((char *)(tdb + 1), name);
  322. } else {
  323. tdb->name = NULL;
  324. }
  325. tdb->direct_access = 0;
  326. tdb->flags = tdb_flags;
  327. tdb->log_fn = NULL;
  328. tdb->transaction = NULL;
  329. tdb->access = NULL;
  330. tdb->last_error = TDB_SUCCESS;
  331. tdb->file = NULL;
  332. tdb->lock_fn = tdb_fcntl_lock;
  333. tdb->unlock_fn = tdb_fcntl_unlock;
  334. tdb->hash_fn = jenkins_hash;
  335. memset(&tdb->stats, 0, sizeof(tdb->stats));
  336. tdb->stats.base.attr = TDB_ATTRIBUTE_STATS;
  337. tdb->stats.size = sizeof(tdb->stats);
  338. tdb_io_init(tdb);
  339. while (attr) {
  340. switch (attr->base.attr) {
  341. case TDB_ATTRIBUTE_HASH:
  342. tdb->hash_fn = attr->hash.fn;
  343. tdb->hash_data = attr->hash.data;
  344. break;
  345. case TDB_ATTRIBUTE_SEED:
  346. seed = &attr->seed;
  347. break;
  348. case TDB_ATTRIBUTE_OPENHOOK:
  349. openhook = &attr->openhook;
  350. break;
  351. default:
  352. /* These are set as normal. */
  353. ecode = tdb_set_attribute(tdb, attr);
  354. if (ecode != TDB_SUCCESS)
  355. goto fail;
  356. }
  357. attr = attr->base.next;
  358. }
  359. if (tdb_flags & ~(TDB_INTERNAL | TDB_NOLOCK | TDB_NOMMAP | TDB_CONVERT
  360. | TDB_NOSYNC | TDB_SEQNUM | TDB_ALLOW_NESTING)) {
  361. ecode = tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR,
  362. "tdb_open: unknown flags %u", tdb_flags);
  363. goto fail;
  364. }
  365. if ((open_flags & O_ACCMODE) == O_WRONLY) {
  366. ecode = tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR,
  367. "tdb_open: can't open tdb %s write-only",
  368. name);
  369. goto fail;
  370. }
  371. if ((open_flags & O_ACCMODE) == O_RDONLY) {
  372. tdb->read_only = true;
  373. tdb->mmap_flags = PROT_READ;
  374. } else {
  375. tdb->read_only = false;
  376. tdb->mmap_flags = PROT_READ | PROT_WRITE;
  377. }
  378. /* internal databases don't need any of the rest. */
  379. if (tdb->flags & TDB_INTERNAL) {
  380. tdb->flags |= (TDB_NOLOCK | TDB_NOMMAP);
  381. ecode = tdb_new_file(tdb);
  382. if (ecode != TDB_SUCCESS) {
  383. goto fail;
  384. }
  385. tdb->file->fd = -1;
  386. ecode = tdb_new_database(tdb, seed, &hdr);
  387. if (ecode != TDB_SUCCESS) {
  388. goto fail;
  389. }
  390. tdb_convert(tdb, &hdr.hash_seed, sizeof(hdr.hash_seed));
  391. tdb->hash_seed = hdr.hash_seed;
  392. tdb_ftable_init(tdb);
  393. return tdb;
  394. }
  395. if (stat(name, &st) != -1)
  396. tdb->file = find_file(st.st_dev, st.st_ino);
  397. if (!tdb->file) {
  398. int fd;
  399. if ((fd = open(name, open_flags, mode)) == -1) {
  400. /* errno set by open(2) */
  401. saved_errno = errno;
  402. tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
  403. "tdb_open: could not open file %s: %s",
  404. name, strerror(errno));
  405. goto fail_errno;
  406. }
  407. /* on exec, don't inherit the fd */
  408. v = fcntl(fd, F_GETFD, 0);
  409. fcntl(fd, F_SETFD, v | FD_CLOEXEC);
  410. if (fstat(fd, &st) == -1) {
  411. saved_errno = errno;
  412. tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
  413. "tdb_open: could not stat open %s: %s",
  414. name, strerror(errno));
  415. goto fail_errno;
  416. }
  417. ecode = tdb_new_file(tdb);
  418. if (ecode != TDB_SUCCESS)
  419. goto fail;
  420. tdb->file->next = files;
  421. tdb->file->fd = fd;
  422. tdb->file->device = st.st_dev;
  423. tdb->file->inode = st.st_ino;
  424. tdb->file->map_ptr = NULL;
  425. tdb->file->map_size = sizeof(struct tdb_header);
  426. }
  427. /* ensure there is only one process initialising at once */
  428. ecode = tdb_lock_open(tdb, TDB_LOCK_WAIT|TDB_LOCK_NOCHECK);
  429. if (ecode != TDB_SUCCESS) {
  430. saved_errno = errno;
  431. goto fail_errno;
  432. }
  433. /* call their open hook if they gave us one. */
  434. if (openhook) {
  435. ecode = openhook->fn(tdb->file->fd, openhook->data);
  436. if (ecode != TDB_SUCCESS) {
  437. tdb_logerr(tdb, ecode, TDB_LOG_ERROR,
  438. "tdb_open: open hook failed");
  439. goto fail;
  440. }
  441. open_flags |= O_CREAT;
  442. }
  443. /* If they used O_TRUNC, read will return 0. */
  444. rlen = pread(tdb->file->fd, &hdr, sizeof(hdr), 0);
  445. if (rlen == 0 && (open_flags & O_CREAT)) {
  446. ecode = tdb_new_database(tdb, seed, &hdr);
  447. if (ecode != TDB_SUCCESS) {
  448. goto fail;
  449. }
  450. } else if (rlen < 0) {
  451. ecode = tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
  452. "tdb_open: error %s reading %s",
  453. strerror(errno), name);
  454. goto fail;
  455. } else if (rlen < sizeof(hdr)
  456. || strcmp(hdr.magic_food, TDB_MAGIC_FOOD) != 0) {
  457. ecode = tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
  458. "tdb_open: %s is not a tdb file", name);
  459. goto fail;
  460. }
  461. if (hdr.version != TDB_VERSION) {
  462. if (hdr.version == bswap_64(TDB_VERSION))
  463. tdb->flags |= TDB_CONVERT;
  464. else {
  465. /* wrong version */
  466. ecode = tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
  467. "tdb_open:"
  468. " %s is unknown version 0x%llx",
  469. name, (long long)hdr.version);
  470. goto fail;
  471. }
  472. }
  473. tdb_convert(tdb, &hdr, sizeof(hdr));
  474. tdb->hash_seed = hdr.hash_seed;
  475. hash_test = TDB_HASH_MAGIC;
  476. hash_test = tdb_hash(tdb, &hash_test, sizeof(hash_test));
  477. if (hdr.hash_test != hash_test) {
  478. /* wrong hash variant */
  479. ecode = tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
  480. "tdb_open:"
  481. " %s uses a different hash function",
  482. name);
  483. goto fail;
  484. }
  485. /* Clear any features we don't understand. */
  486. if ((open_flags & O_ACCMODE) != O_RDONLY) {
  487. hdr.features_used &= TDB_FEATURE_MASK;
  488. if (tdb_write_convert(tdb, offsetof(struct tdb_header,
  489. features_used),
  490. &hdr.features_used,
  491. sizeof(hdr.features_used)) == -1)
  492. goto fail;
  493. }
  494. tdb_unlock_open(tdb);
  495. /* This make sure we have current map_size and mmap. */
  496. tdb->methods->oob(tdb, tdb->file->map_size + 1, true);
  497. /* Now it's fully formed, recover if necessary. */
  498. berr = tdb_needs_recovery(tdb);
  499. if (unlikely(berr != false)) {
  500. if (berr < 0) {
  501. ecode = berr;
  502. goto fail;
  503. }
  504. ecode = tdb_lock_and_recover(tdb);
  505. if (ecode != TDB_SUCCESS) {
  506. goto fail;
  507. }
  508. }
  509. ecode = tdb_ftable_init(tdb);
  510. if (ecode != TDB_SUCCESS) {
  511. goto fail;
  512. }
  513. /* Add to linked list if we're new. */
  514. if (tdb->file->refcnt == 1)
  515. files = tdb->file;
  516. return tdb;
  517. fail:
  518. /* Map ecode to some logical errno. */
  519. switch (ecode) {
  520. case TDB_ERR_CORRUPT:
  521. case TDB_ERR_IO:
  522. saved_errno = EIO;
  523. break;
  524. case TDB_ERR_LOCK:
  525. saved_errno = EWOULDBLOCK;
  526. break;
  527. case TDB_ERR_OOM:
  528. saved_errno = ENOMEM;
  529. break;
  530. case TDB_ERR_EINVAL:
  531. saved_errno = EINVAL;
  532. break;
  533. default:
  534. saved_errno = EINVAL;
  535. break;
  536. }
  537. fail_errno:
  538. #ifdef TDB_TRACE
  539. close(tdb->tracefd);
  540. #endif
  541. if (tdb->file) {
  542. tdb_lock_cleanup(tdb);
  543. if (--tdb->file->refcnt == 0) {
  544. assert(tdb->file->num_lockrecs == 0);
  545. if (tdb->file->map_ptr) {
  546. if (tdb->flags & TDB_INTERNAL) {
  547. free(tdb->file->map_ptr);
  548. } else
  549. tdb_munmap(tdb->file);
  550. }
  551. if (close(tdb->file->fd) != 0)
  552. tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
  553. "tdb_open: failed to close tdb fd"
  554. " on error: %s", strerror(errno));
  555. free(tdb->file->lockrecs);
  556. free(tdb->file);
  557. }
  558. }
  559. free(tdb);
  560. errno = saved_errno;
  561. return NULL;
  562. }
  563. int tdb_close(struct tdb_context *tdb)
  564. {
  565. int ret = 0;
  566. tdb_trace(tdb, "tdb_close");
  567. if (tdb->transaction) {
  568. tdb_transaction_cancel(tdb);
  569. }
  570. if (tdb->file->map_ptr) {
  571. if (tdb->flags & TDB_INTERNAL)
  572. free(tdb->file->map_ptr);
  573. else
  574. tdb_munmap(tdb->file);
  575. }
  576. if (tdb->file) {
  577. struct tdb_file **i;
  578. tdb_lock_cleanup(tdb);
  579. if (--tdb->file->refcnt == 0) {
  580. ret = close(tdb->file->fd);
  581. /* Remove from files list */
  582. for (i = &files; *i; i = &(*i)->next) {
  583. if (*i == tdb->file) {
  584. *i = tdb->file->next;
  585. break;
  586. }
  587. }
  588. free(tdb->file->lockrecs);
  589. free(tdb->file);
  590. }
  591. }
  592. #ifdef TDB_TRACE
  593. close(tdb->tracefd);
  594. #endif
  595. free(tdb);
  596. return ret;
  597. }