1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996,2008 Oracle.  All rights reserved.
5 *
6 * $Id: os_fid.c,v 12.20 2008/01/08 20:58:43 bostic Exp $
7 */
8
9#include "db_config.h"
10
11#include "db_int.h"
12
13/*
14 * __os_fileid --
15 *	Return a unique identifier for a file.
16 *
17 * PUBLIC: int __os_fileid __P((ENV *, const char *, int, u_int8_t *));
18 */
19int
20__os_fileid(env, fname, unique_okay, fidp)
21	ENV *env;
22	const char *fname;
23	int unique_okay;
24	u_int8_t *fidp;
25{
26	pid_t pid;
27	size_t i;
28	u_int32_t tmp;
29	u_int8_t *p;
30
31#ifdef HAVE_STAT
32	struct stat sb;
33	int ret;
34
35	/*
36	 * The structure of a fileid on a POSIX/UNIX system is:
37	 *
38	 *	ino[4] dev[4] unique-ID[4] serial-counter[4] empty[4].
39	 *
40	 * For real files, which have a backing inode and device, the first
41	 * 8 bytes are filled in and the following bytes are left 0.  For
42	 * temporary files, the following 12 bytes are filled in.
43	 *
44	 * Clear the buffer.
45	 */
46	memset(fidp, 0, DB_FILE_ID_LEN);
47	RETRY_CHK((stat(CHAR_STAR_CAST fname, &sb)), ret);
48	if (ret != 0) {
49		__db_syserr(env, ret, "stat: %s", fname);
50		return (__os_posix_err(ret));
51	}
52
53	/*
54	 * !!!
55	 * Nothing is ever big enough -- on Sparc V9, st_ino, st_dev and the
56	 * time_t types are all 8 bytes.  As DB_FILE_ID_LEN is only 20 bytes,
57	 * we convert to a (potentially) smaller fixed-size type and use it.
58	 *
59	 * We don't worry about byte sexing or the actual variable sizes.
60	 *
61	 * When this routine is called from the DB access methods, it's only
62	 * called once -- whatever ID is generated when a database is created
63	 * is stored in the database file's metadata, and that is what is
64	 * saved in the mpool region's information to uniquely identify the
65	 * file.
66	 *
67	 * When called from the mpool layer this routine will be called each
68	 * time a new thread of control wants to share the file, which makes
69	 * things tougher.  As far as byte sexing goes, since the mpool region
70	 * lives on a single host, there's no issue of that -- the entire
71	 * region is byte sex dependent.  As far as variable sizes go, we make
72	 * the simplifying assumption that 32-bit and 64-bit processes will
73	 * get the same 32-bit values if we truncate any returned 64-bit value
74	 * to a 32-bit value.  When we're called from the mpool layer, though,
75	 * we need to be careful not to include anything that isn't
76	 * reproducible for a given file, such as the timestamp or serial
77	 * number.
78	 */
79	tmp = (u_int32_t)sb.st_ino;
80	for (p = (u_int8_t *)&tmp, i = sizeof(u_int32_t); i > 0; --i)
81		*fidp++ = *p++;
82
83	tmp = (u_int32_t)sb.st_dev;
84	for (p = (u_int8_t *)&tmp, i = sizeof(u_int32_t); i > 0; --i)
85		*fidp++ = *p++;
86#else
87	 /*
88	  * Use the file name.
89	  *
90	  * XXX
91	  * Cast the first argument, the BREW ARM compiler is unhappy if
92	  * we don't.
93	  */
94	 (void)strncpy((char *)fidp, fname, DB_FILE_ID_LEN);
95#endif /* HAVE_STAT */
96
97	if (unique_okay) {
98		/* Add in 32-bits of (hopefully) unique number. */
99		__os_unique_id(env, &tmp);
100		for (p = (u_int8_t *)&tmp, i = sizeof(u_int32_t); i > 0; --i)
101			*fidp++ = *p++;
102
103		/*
104		 * Initialize/increment the serial number we use to help
105		 * avoid fileid collisions.  Note we don't bother with
106		 * locking; it's unpleasant to do from down in here, and
107		 * if we race on this no real harm will be done, since the
108		 * finished fileid has so many other components.
109		 *
110		 * We use the bottom 32-bits of the process ID, hoping they
111		 * are more random than the top 32-bits (should we be on a
112		 * machine with 64-bit process IDs).
113		 *
114		 * We increment by 100000 on each call as a simple way of
115		 * randomizing; simply incrementing seems potentially less
116		 * useful if pids are also simply incremented, since this
117		 * is process-local and we may be one of a set of processes
118		 * starting up.  100000 pushes us out of pid space on most
119		 * 32-bit platforms, and has few interesting properties in
120		 * base 2.
121		 */
122		if (DB_GLOBAL(fid_serial) == 0) {
123			__os_id(env->dbenv, &pid, NULL);
124			DB_GLOBAL(fid_serial) = (u_int32_t)pid;
125		} else
126			DB_GLOBAL(fid_serial) += 100000;
127
128		for (p = (u_int8_t *)
129		    &DB_GLOBAL(fid_serial), i = sizeof(u_int32_t); i > 0; --i)
130			*fidp++ = *p++;
131	}
132
133	return (0);
134}
135