BRL-CAD
db_open.c
Go to the documentation of this file.
1 /* D B _ O P E N . C
2  * BRL-CAD
3  *
4  * Copyright (c) 1988-2014 United States Government as represented by
5  * the U.S. Army Research Laboratory.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * version 2.1 as published by the Free Software Foundation.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this file; see the file named COPYING for more
18  * information.
19  */
20 /** @addtogroup dbio */
21 /** @{ */
22 /** @file librt/db_open.c
23  *
24  * Routines for opening, creating, and replicating BRL-CAD geometry
25  * database files. BRL-CAD geometry database files are managed in a
26  * given application through a "database instance" (dbi). An
27  * application may maintain multiple database instances open to a
28  * given geometry database file.
29  *
30  */
31 
32 #include "common.h"
33 
34 #include <string.h>
35 #ifdef HAVE_SYS_TYPES_H
36 # include <sys/types.h>
37 #endif
38 #ifdef HAVE_SYS_STAT_H
39 # include <sys/stat.h>
40 #endif
41 #include "bio.h"
42 
43 #include "bu/parallel.h"
44 #include "vmath.h"
45 #include "raytrace.h"
46 #include "db.h"
47 #include "wdb.h"
48 #include "mater.h"
49 
50 
51 #ifndef SEEK_SET
52 # define SEEK_SET 0
53 #endif
54 
55 #define DEFAULT_DB_TITLE "Untitled BRL-CAD Database"
56 
57 
58 struct db_i *
59 db_open(const char *name, const char *mode)
60 {
61  register struct db_i *dbip = DBI_NULL;
62  register int i;
63  char **argv;
64 
65  if (name == NULL) return DBI_NULL;
66 
67  if (RT_G_DEBUG & DEBUG_DB) {
68  bu_log("db_open(%s, %s)\n", name, mode);
69  }
70 
71  if (mode && mode[0] == 'r' && mode[1] == '\0') {
72  /* Read-only mode */
73 
74  struct bu_mapped_file *mfp;
75 
76  mfp = bu_open_mapped_file(name, "db_i");
77  if (mfp == NULL) {
78  if (RT_G_DEBUG & DEBUG_DB) {
79  bu_log("db_open(%s) FAILED, unable to open as a mapped file\n", name);
80  }
81  return DBI_NULL;
82  }
83 
84  /* Is this a re-use of a previously mapped file? */
85  if (mfp->apbuf) {
86  dbip = (struct db_i *)mfp->apbuf;
87  RT_CK_DBI(dbip);
88  dbip->dbi_uses++;
89 
90  /*
91  * decrement the mapped file reference counter by 1,
92  * references are already counted in dbip->dbi_uses
93  */
95 
96  if (RT_G_DEBUG & DEBUG_DB) {
97  bu_log("db_open(%s) dbip=%p: reused previously mapped file\n", name, (void *)dbip);
98  }
99 
100  return dbip;
101  }
102 
103  BU_ALLOC(dbip, struct db_i);
104  dbip->dbi_mf = mfp;
105  dbip->dbi_eof = (off_t)mfp->buflen;
106  dbip->dbi_inmem = mfp->buf;
107  dbip->dbi_mf->apbuf = (void *)dbip;
108 
109  /* Do this too, so we can seek around on the file */
110  if ((dbip->dbi_fp = fopen(name, "rb")) == NULL) {
111  if (RT_G_DEBUG & DEBUG_DB) {
112  bu_log("db_open(%s) FAILED, unable to open file for reading\n", name);
113  }
114  bu_free((char *)dbip, "struct db_i");
115  return DBI_NULL;
116  }
117 
118  dbip->dbi_read_only = 1;
119  } else {
120  /* Read-write mode */
121 
122  BU_ALLOC(dbip, struct db_i);
123  dbip->dbi_eof = (off_t)-1L;
124 
125  if ((dbip->dbi_fp = fopen(name, "r+b")) == NULL) {
126  if (RT_G_DEBUG & DEBUG_DB) {
127  bu_log("db_open(%s) FAILED, unable to open file for reading/writing\n", name);
128  }
129  bu_free((char *)dbip, "struct db_i");
130  return DBI_NULL;
131  }
132 
133  dbip->dbi_read_only = 0;
134  }
135 
136  /* Initialize fields */
137  for (i = 0; i < RT_DBNHASH; i++)
138  dbip->dbi_Head[i] = RT_DIR_NULL;
139 
140  dbip->dbi_local2base = 1.0; /* mm */
141  dbip->dbi_base2local = 1.0;
142  dbip->dbi_title = (char *)0;
143  dbip->dbi_uses = 1;
144 
145  /* FIXME: At some point, expand argv search paths with
146  * getenv("BRLCAD_FILE_PATH") paths
147  */
148 
149  /* intentionally acquiring dynamic memory here since we set
150  * dbip->dbi_filepath to argv. arg values and array memory are
151  * released during db_close.
152  */
153  argv = (char **)bu_malloc(3 * sizeof(char *), "dbi_filepath[3]");
154  argv[0] = bu_strdup(".");
155  argv[1] = bu_dirname(name);
156  argv[2] = NULL;
157  dbip->dbi_filepath = argv;
158 
159 #if !defined(_WIN32) || defined(__CYGWIN__)
160  /* If not a full path */
161  if (argv[1][0] != '/') {
162  struct bu_vls fullpath = BU_VLS_INIT_ZERO;
163 
164  bu_free((void *)argv[1], "db_open: argv[1]");
165  argv[1] = getcwd((char *)NULL, (size_t)MAXPATHLEN);
166 
167  /* Something went wrong and we didn't get the CWD. So,
168  * free up any memory allocated here and return DBI_NULL */
169  if (argv[1] == NULL) {
170  if (dbip->dbi_mf) {
173  dbip->dbi_mf = (struct bu_mapped_file *)NULL;
174  }
175 
176  if (dbip->dbi_fp) {
177  fclose(dbip->dbi_fp);
178  }
179 
180  bu_free((void *)argv[0], "db_open: argv[0]");
181  bu_free((void *)argv, "db_open: argv");
182  bu_free((char *)dbip, "struct db_i");
183 
184  return DBI_NULL;
185  }
186 
187  bu_vls_printf(&fullpath, "%s/%s", argv[1], name);
188  dbip->dbi_filename = bu_strdup(bu_vls_addr(&fullpath));
189  bu_vls_free(&fullpath);
190  } else {
191  /* Record the filename and file path */
192  dbip->dbi_filename = bu_strdup(name);
193  }
194 #else
195  /* Record the filename and file path */
196  dbip->dbi_filename = bu_strdup(name);
197 #endif
198 
199  bu_ptbl_init(&dbip->dbi_clients, 128, "dbi_clients[]");
200  dbip->dbi_magic = DBI_MAGIC; /* Now it's valid */
201 
202  /* determine version */
203  dbip->dbi_version = 0; /* make db_version() calculate */
204  dbip->dbi_version = db_version(dbip);
205 
206  if (dbip->dbi_version < 5) {
207  if (rt_db_flip_endian(dbip)) {
208  if (dbip->dbi_version > 0)
209  dbip->dbi_version *= -1;
210  dbip->dbi_read_only = 1;
211  bu_log("WARNING: Binary-incompatible v4 geometry database detected.\n");
212  bu_log(" Endianness flipped. Converting to READ ONLY.\n");
213  }
214  }
215 
216  if (RT_G_DEBUG & DEBUG_DB) {
217  bu_log("db_open(%s) dbip=%p version=%d\n", dbip->dbi_filename, (void *)dbip, dbip->dbi_version);
218  }
219 
220  return dbip;
221 }
222 
223 
224 struct db_i *
225 db_create(const char *name, int version)
226 {
227  FILE *fp;
228  struct db_i *dbip;
229  int result;
230 
231  if (name == NULL) return DBI_NULL;
232 
233  if (RT_G_DEBUG & DEBUG_DB)
234  bu_log("db_create(%s, %d)\n", name, version);
235 
236  fp = fopen(name, "w+b");
237 
238  if (fp == NULL) {
239  perror(name);
240  return DBI_NULL;
241  }
242 
243  if (version == 5) {
244  result = db5_fwrite_ident(fp, DEFAULT_DB_TITLE, 1.0);
245  } else if (version == 4) {
246  result = db_fwrite_ident(fp, DEFAULT_DB_TITLE, 1.0);
247  } else {
248  bu_log("WARNING: db_create() was provided an unrecognized version number: %d\n", version);
249  result = db5_fwrite_ident(fp, DEFAULT_DB_TITLE, 1.0);
250  }
251 
252  (void)fclose(fp);
253 
254  if (result < 0)
255  return DBI_NULL;
256 
257  if ((dbip = db_open(name, DB_OPEN_READWRITE)) == DBI_NULL)
258  return DBI_NULL;
259 
260  /* Do a quick scan to determine version, find _GLOBAL, etc. */
261  if (db_dirbuild(dbip) < 0)
262  return DBI_NULL;
263 
264  return dbip;
265 }
266 
267 
268 void
269 db_close_client(struct db_i *dbip, long int *client)
270 {
271  if (!dbip)
272  return;
273 
274  RT_CK_DBI(dbip);
275 
276  if (client) {
277  (void)bu_ptbl_rm(&dbip->dbi_clients, client);
278  }
279 
280  db_close(dbip);
281 }
282 
283 
284 void
285 db_close(register struct db_i *dbip)
286 {
287  register int i;
288  register struct directory *dp, *nextdp;
289 
290  if (!dbip)
291  return;
292 
293  RT_CK_DBI(dbip);
294  if (RT_G_DEBUG&DEBUG_DB) bu_log("db_close(%s) %p uses=%d\n",
295  dbip->dbi_filename, (void *)dbip, dbip->dbi_uses);
296 
298  if ((--dbip->dbi_uses) > 0) {
300  /* others are still using this database */
301  return;
302  }
304 
305  /* ready to free the database -- use count is now zero */
306 
307  /* free up any mapped files */
308  if (dbip->dbi_mf) {
309  /*
310  * We're using an instance of a memory mapped file.
311  * We have two choices:
312  * Either dissociate from the memory mapped file
313  * by clearing dbi_mf->apbuf, or
314  * keeping our already-scanned dbip ready for
315  * further use, with our dbi_uses counter at 0.
316  * For speed of re-open, at the price of some address space,
317  * the second choice is taken.
318  */
321  dbip->dbi_mf = (struct bu_mapped_file *)NULL;
322  }
323 
324  /* try to ensure/encourage that the file is written out */
325  db_sync(dbip);
326 
327  if (dbip->dbi_fp) {
328  fclose(dbip->dbi_fp);
329  }
330 
331  if (dbip->dbi_title)
332  bu_free(dbip->dbi_title, "dbi_title");
333  if (dbip->dbi_filename)
334  bu_free(dbip->dbi_filename, "dbi_filename");
335 
336  db_free_anim(dbip);
337  rt_color_free(); /* Free MaterHead list */
338 
339  /* Release map of database holes */
340  rt_mempurge(&(dbip->dbi_freep));
341  rt_memclose();
342 
343  dbip->dbi_inmem = NULL; /* sanity */
344 
345  bu_ptbl_free(&dbip->dbi_clients);
346 
347  /* Free all directory entries */
348  for (i = 0; i < RT_DBNHASH; i++) {
349  for (dp = dbip->dbi_Head[i]; dp != RT_DIR_NULL;) {
350  RT_CK_DIR(dp);
351  nextdp = dp->d_forw;
352  RT_DIR_FREE_NAMEP(dp); /* frees d_namep */
353 
354  if ((dp->d_flags & RT_DIR_INMEM) && (dp->d_un.ptr != NULL)) {
355  bu_free(dp->d_un.ptr, "db_close d_un.ptr");
356  dp->d_un.ptr = NULL;
357  dp->d_len = 0;
358  }
359 
360  /* Put 'dp' back on the freelist */
363 
364  /* null'ing the forward pointer here is a huge
365  * memory leak as it causes the loss of all
366  * nodes on the freelist except the first.
367  * (so don't do it)
368  */
369 
370  dp = nextdp;
371  }
372  dbip->dbi_Head[i] = RT_DIR_NULL; /* sanity*/
373  }
374 
375  if (dbip->dbi_filepath != NULL) {
376  bu_free_argv(2, dbip->dbi_filepath);
377  dbip->dbi_filepath = NULL; /* sanity */
378  }
379 
380  bu_free((char *)dbip, "struct db_i");
381 }
382 
383 int
384 db_dump(struct rt_wdb *wdbp, struct db_i *dbip)
385 /* output */
386 /* input */
387 {
388  register int i;
389  register struct directory *dp;
390  struct bu_external ext;
391 
392  RT_CK_DBI(dbip);
393  RT_CK_WDB(wdbp);
394 
395  /* just in case since we don't actually handle it below */
396  if (db_version(dbip) != db_version(wdbp->dbip)) {
397  bu_log("Internal Error: dumping a v%d database into a v%d database is untested\n", db_version(dbip), db_version(wdbp->dbip));
398  return -1;
399  }
400 
401  /* Output all directory entries */
402  for (i = 0; i < RT_DBNHASH; i++) {
403  for (dp = dbip->dbi_Head[i]; dp != RT_DIR_NULL; dp = dp->d_forw) {
404  RT_CK_DIR(dp);
405  /* XXX Need to go to internal form, if database versions don't match */
406  if (db_get_external(&ext, dp, dbip) < 0) {
407  bu_log("db_dump() read failed on %s, skipping\n", dp->d_namep);
408  continue;
409  }
410  if (wdb_export_external(wdbp, &ext, dp->d_namep, dp->d_flags & ~(RT_DIR_INMEM), dp->d_minor_type) < 0) {
411  bu_log("db_dump() write failed on %s, aborting\n", dp->d_namep);
412  bu_free_external(&ext);
413  return -1;
414  }
415  bu_free_external(&ext);
416  }
417  }
418  return 0;
419 }
420 
421 struct db_i *
422 db_clone_dbi(struct db_i *dbip, long int *client)
423 {
424  RT_CK_DBI(dbip);
425 
426  dbip->dbi_uses++;
427  if (client) {
428  bu_ptbl_ins_unique(&dbip->dbi_clients, client);
429  }
430  return dbip;
431 }
432 
433 void
434 db_sync(struct db_i *dbip)
435 {
436  RT_CK_DBI(dbip);
437 
439 
440  /* make sure we have something to do */
441  if (!dbip->dbi_fp) {
443  return;
444  }
445 
446  /* flush the file */
447  (void)fflush(dbip->dbi_fp);
448 
449 #if defined(HAVE_FSYNC) && !defined(STRICT_FLAGS)
450  /* make sure it's written out */
451  (void)fsync(fileno(dbip->dbi_fp));
452 #else
453 # if defined(HAVE_SYNC) && !defined(STRICT_FLAGS)
454  /* try the whole filesystem if sans fsync() */
455  sync();
456 # endif
457 #endif
458 
460 }
461 
462 
463 /** @} */
464 /*
465  * Local Variables:
466  * mode: C
467  * tab-width: 8
468  * indent-tabs-mode: t
469  * c-file-style: "stroustrup"
470  * End:
471  * ex: shiftwidth=4 tabstop=8
472  */
void db_free_anim(struct db_i *dbip)
Definition: db_anim.c:223
char * d_namep
pointer to name string
Definition: raytrace.h:859
void rt_mempurge(struct mem_map **pp)
Definition: memalloc.c:316
Definition: raytrace.h:800
void bu_log(const char *,...) _BU_ATTR_PRINTF12
Definition: log.c:176
#define RT_DBNHASH
hash table is an array of linked lists with this many array pointer elements (Memory use for 32-bit: ...
Definition: raytrace.h:755
int dbi_uses
PRIVATE: # of uses of this struct.
Definition: raytrace.h:818
#define BU_SEM_LISTS
Definition: parallel.h:179
#define RT_DIR_FREE_NAMEP(_dp)
Definition: raytrace.h:914
size_t d_len
of db granules used
Definition: raytrace.h:867
void bu_free_argv(int argc, char *argv[])
Definition: argv.c:165
#define RT_DIR_INMEM
object is in memory (only)
Definition: raytrace.h:889
struct db_i * db_open(const char *name, const char *mode)
Definition: db_open.c:59
#define DBI_MAGIC
Definition: magic.h:202
struct db_i * dbip
Definition: raytrace.h:1266
Definition: clone.c:90
void bu_ptbl_init(struct bu_ptbl *b, size_t len, const char *str)
Definition: ptbl.c:32
void bu_semaphore_acquire(unsigned int i)
Definition: semaphore.c:180
int db5_fwrite_ident(FILE *, const char *, double)
Definition: attributes.c:440
int db_version(struct db_i *dbip)
Definition: db5_scan.c:414
int bu_ptbl_rm(struct bu_ptbl *b, const long *p)
char ** dbi_filepath
search path for aux file opens (convenience var)
Definition: raytrace.h:810
void bu_free_external(struct bu_external *ep)
Header file for the BRL-CAD common definitions.
int bu_ptbl_ins_unique(struct bu_ptbl *b, long *p)
struct db_i * db_clone_dbi(struct db_i *dbip, long int *client)
Definition: db_open.c:422
struct directory * d_forw
link to next dir entry
Definition: raytrace.h:864
struct bu_mapped_file * dbi_mf
PRIVATE: Only in read-only mode.
Definition: raytrace.h:822
void * bu_malloc(size_t siz, const char *str)
Definition: malloc.c:314
#define RT_CK_WDB(_p)
Definition: raytrace.h:1292
#define DB_OPEN_READWRITE
Definition: raytrace.h:3555
void bu_vls_free(struct bu_vls *vp)
Definition: vls.c:248
unsigned char d_minor_type
object minor type
Definition: raytrace.h:871
struct resource rt_uniresource
default. Defined in librt/globals.c
Definition: globals.c:41
#define RT_G_DEBUG
Definition: raytrace.h:1718
char * dbi_title
title from IDENT rec
Definition: raytrace.h:809
int db_fwrite_ident(FILE *fp, const char *title, double local2mm)
Definition: db_scan.c:446
int db_get_external(struct bu_external *ep, const struct directory *dp, const struct db_i *dbip)
#define BU_ALLOC(_ptr, _type)
Definition: malloc.h:223
#define RT_CK_DIR(_dp)
Definition: raytrace.h:876
off_t dbi_eof
PRIVATE: End+1 pos after db_scan()
Definition: raytrace.h:816
int wdb_export_external(struct rt_wdb *wdbp, struct bu_external *ep, const char *name, int flags, unsigned char minor_type)
Definition: wdb.c:105
int rt_db_flip_endian(struct db_i *dbip)
Definition: db_corrupt.c:120
struct bu_ptbl dbi_clients
PRIVATE: List of rtip's using this db_i.
Definition: raytrace.h:823
void * dbi_inmem
PRIVATE: ptr to in-memory copy.
Definition: raytrace.h:820
struct db_i * db_create(const char *name, int version)
Definition: db_open.c:225
struct mem_map * dbi_freep
PRIVATE: map of free granules.
Definition: raytrace.h:819
struct bu_mapped_file * bu_open_mapped_file(const char *name, const char *appl)
Definition: mappedfile.c:56
char * bu_vls_addr(const struct bu_vls *vp)
Definition: vls.c:111
FILE * dbi_fp
PRIVATE: object hash table.
Definition: raytrace.h:815
double dbi_base2local
unit conversion factors
Definition: raytrace.h:808
int db_dump(struct rt_wdb *wdbp, struct db_i *dbip)
Definition: db_open.c:384
void bu_semaphore_release(unsigned int i)
Definition: semaphore.c:218
void bu_ptbl_free(struct bu_ptbl *b)
Definition: ptbl.c:226
#define RT_CK_DBI(_p)
Definition: raytrace.h:829
int dbi_read_only
!0 => read only file
Definition: raytrace.h:806
union directory::@8 d_un
struct directory * dbi_Head[RT_DBNHASH]
Definition: raytrace.h:814
#define DEBUG_DB
5 Database debugging
Definition: raytrace.h:88
#define BU_SEM_SYSCALL
Definition: parallel.h:178
#define DBI_NULL
Definition: raytrace.h:827
void bu_free_mapped_files(int verbose)
Definition: mappedfile.c:373
struct directory * re_directory_hd
Definition: raytrace.h:1481
#define DEFAULT_DB_TITLE
Definition: db_open.c:55
void bu_vls_printf(struct bu_vls *vls, const char *fmt,...) _BU_ATTR_PRINTF23
Definition: vls.c:694
#define RT_DIR_NULL
Definition: raytrace.h:875
void * ptr
ptr to in-memory-only obj
Definition: raytrace.h:862
void bu_close_mapped_file(struct bu_mapped_file *mp)
Definition: mappedfile.c:339
int dbi_version
PRIVATE: use db_version()
Definition: raytrace.h:824
void db_close_client(struct db_i *dbip, long int *client)
Definition: db_open.c:269
void db_close(register struct db_i *dbip)
Definition: db_open.c:285
void bu_free(void *ptr, const char *str)
Definition: malloc.c:328
double dbi_local2base
local2mm
Definition: raytrace.h:807
int db_dirbuild(struct db_i *dbip)
Definition: db5_scan.c:301
void db_sync(struct db_i *dbip)
Definition: db_open.c:434
#define BU_VLS_INIT_ZERO
Definition: vls.h:84
int d_flags
flags
Definition: raytrace.h:869
Definition: vls.h:56
char * dbi_filename
file name
Definition: raytrace.h:805
void rt_memclose(void)
Definition: memalloc.c:348
void rt_color_free(void)
Definition: mater.c:294
char * bu_dirname(const char *path)
Definition: dirname.c:30
uint32_t dbi_magic
magic number
Definition: raytrace.h:801
#define bu_strdup(s)
Definition: str.h:71
#define MAXPATHLEN
Definition: defines.h:113