ptg10805159
Section 20.8 Source Code 759
121 if ((oflag & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC)) {
122 /*
123 * If the database was created, we have to initialize
124 * it. Write lock the entire file so that we can stat
125 * it, check its size, and initialize it, atomically.
126 */
127 if (writew_lock(db->idxfd, 0, SEEK_SET, 0) < 0)
128 err_dump("db_open: writew_lock error");
129 if (fstat(db->idxfd, &statbuff) < 0)
130 err_sys("db_open: fstat error");
131 if (statbuff.st_size == 0) {
132 /*
133 * We have to build a list of (NHASH_DEF + 1) chain
134 * ptrs with a value of 0. The +1 is for the free
135 * list pointer that precedes the hash table.
136 */
137 sprintf(asciiptr, "%*d", PTR_SZ, 0);
[121 – 130] We encounter locking if the database is being created. Consider two
processes trying to create the same database at about the same time.
Assume that the first process callsfstatand is blocked by the kernel after
fstatreturns. The second process callsdb_open,finds that the length of
the index file is 0, and initializes the free list and hash chain. The second
process then writes one record to the database. At this point, the second
process is blocked, and the first process continues executing right after the
call tofstat.The first process finds the size of the index file to be 0 (since
fstatwas called beforethe second process initialized the index file), so the
first process initializes the free list and hash chain, wiping out the recordthat
the second process stored in the database. The way to prevent this is to use
locking. Weuse the macrosreadw_lock, writew_lock,andun_lock
from Section 14.3.
[131 – 137] If the size of the index file is 0, we have just created it, so we need to
initialize the free list and hash chain pointers it contains. Note that we use
the format string%*dto convert a database pointer from an integer to an
ASCII string. (We’ll use this type of format again in_db_writeidxand
_db_writeptr.) This format tellssprintfto take thePTR_SZargument
and use it as the minimum field width for the next argument, which is 0 in
this instance (here we are initializing the pointers to 0, since we arecreating
anew database). This has the effect of forcing the string created to be at least
PTR_SZcharacters (padded on the left with spaces). In_db_writeidxand
_db_writeptr, we will pass a pointer value instead of zero, but we will
first verify that the pointer value isn’t greater thanPTR_MAX, to guarantee
that every pointer string we write to the database occupies exactlyPTR_SZ
( 7 )characters.