diff options
author | Werner Koch <[email protected]> | 2012-01-10 14:16:44 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2012-01-10 14:16:44 +0000 |
commit | b9333cd890a85cae5064ff2b0b97c4ba2afc1a99 (patch) | |
tree | cbdb29e5debd97313848a6e8bb271b0300e786bf | |
parent | Update copyright years. (diff) | |
download | gnupg-b9333cd890a85cae5064ff2b0b97c4ba2afc1a99.tar.gz gnupg-b9333cd890a85cae5064ff2b0b97c4ba2afc1a99.zip |
Replace file locking by the new portable dotlock code.
* include/dotlock.h: New. From current gnupg master.
* util/dotlock.c: Ditto. Include util.h. The major changes done in
master are: Factor Unix and W32 specific code out into specific
functions. Define HAVE_POSIX_SYSTEM. Rearrange some functions.
(disable_dotlock): Rename to dotlock_disable.
(create_dotlock): Rename to dotlock_create and add a dummy arg.
(destroy_dotlock): Rename to dotlock_destroy.
(make_dotlock): Rename to dotlock_take.
(release_dotlock): Rename to dotlock_release.
(remove_lockfiles): Rename to dotlock_remove_lockfiles.
-rw-r--r-- | configure.ac | 3 | ||||
-rw-r--r-- | g10/gpg.c | 4 | ||||
-rw-r--r-- | g10/gpgv.c | 15 | ||||
-rw-r--r-- | g10/keydb.c | 10 | ||||
-rw-r--r-- | g10/keyring.c | 212 | ||||
-rw-r--r-- | g10/signal.c | 6 | ||||
-rw-r--r-- | g10/tdbio.c | 48 | ||||
-rw-r--r-- | include/dotlock.h | 112 | ||||
-rw-r--r-- | include/util.h | 18 | ||||
-rw-r--r-- | util/dotlock.c | 1555 |
10 files changed, 1386 insertions, 597 deletions
diff --git a/configure.ac b/configure.ac index 8570daa7b..181c07bf5 100644 --- a/configure.ac +++ b/configure.ac @@ -933,7 +933,8 @@ AM_CONDITIONAL(ENABLE_AGENT_SUPPORT, test "$agent_support" = yes) dnl Checks for header files. AC_HEADER_STDC -AC_CHECK_HEADERS([unistd.h langinfo.h termio.h locale.h getopt.h pwd.h]) +AC_CHECK_HEADERS([unistd.h langinfo.h termio.h locale.h getopt.h pwd.h \ + signal.h]) # Note that we do not check for iconv here because this is done anyway # by the gettext checks and thus it allows us to disable the use of @@ -1895,7 +1895,7 @@ main (int argc, char **argv ) secure_randoxmalloc(); /* put random number into secure memory */ may_coredump = disable_core_dumps(); init_signals(); - create_dotlock(NULL); /* register locking cleanup */ + dotlock_create (NULL, 0); /* Register locking cleanup. */ i18n_init(); opt.command_fd = -1; /* no command fd */ opt.compress_level = -1; /* defaults to standard compress level */ @@ -2607,7 +2607,7 @@ main (int argc, char **argv ) case oNoEscapeFrom: opt.escape_from = 0; break; case oLockOnce: opt.lock_once = 1; break; case oLockNever: - disable_dotlock (); + dotlock_disable (); random_disable_locking (); break; case oLockMultiple: diff --git a/g10/gpgv.c b/g10/gpgv.c index dfeaa85d7..8d2a8e6b1 100644 --- a/g10/gpgv.c +++ b/g10/gpgv.c @@ -148,7 +148,7 @@ main( int argc, char **argv ) tty_no_terminal(1); tty_batchmode(1); - disable_dotlock(); + dotlock_disable (); set_native_charset (NULL); /* Try to auto set the character set */ @@ -430,9 +430,10 @@ void rl_free_line_state (void) {} #endif /* We do not do any locking, so use these stubs here */ -void disable_dotlock(void) {} -DOTLOCK create_dotlock( const char *file_to_lock ) { return NULL; } -void destroy_dotlock (DOTLOCK h) {} -int make_dotlock( DOTLOCK h, long timeout ) { return 0;} -int release_dotlock( DOTLOCK h ) {return 0;} -void remove_lockfiles(void) {} +void dotlock_disable(void) {} +dotlock_t dotlock_create (const char *file_to_lock, unsigned int flags) +{ return NULL; } +void dotlock_destroy (dotlock_t h) {} +int dotlock_take (dotlock_t h, long timeout) { return 0;} +int dotlock_release (dotlock_t h) {return 0;} +void dotlock_remove_lockfiles (void) {} diff --git a/g10/keydb.c b/g10/keydb.c index 5a62f18c0..d6d83e252 100644 --- a/g10/keydb.c +++ b/g10/keydb.c @@ -76,7 +76,7 @@ static void unlock_all (KEYDB_HANDLE hd); static int maybe_create_keyring (char *filename, int force) { - DOTLOCK lockhd = NULL; + dotlock_t lockhd = NULL; IOBUF iobuf; int rc; mode_t oldmask; @@ -120,7 +120,7 @@ maybe_create_keyring (char *filename, int force) /* To avoid races with other instances of gpg trying to create or update the keyring (it is removed during an update for a short time), we do the next stuff in a locked state. */ - lockhd = create_dotlock (filename); + lockhd = dotlock_create (filename, 0); if (!lockhd) { /* A reason for this to fail is that the directory is not @@ -136,7 +136,7 @@ maybe_create_keyring (char *filename, int force) return G10ERR_GENERAL; } - if ( make_dotlock (lockhd, -1) ) + if ( dotlock_take (lockhd, -1) ) { /* This is something bad. Probably a stale lockfile. */ log_info ("can't lock `%s'\n", filename ); @@ -180,8 +180,8 @@ maybe_create_keyring (char *filename, int force) leave: if (lockhd) { - release_dotlock (lockhd); - destroy_dotlock (lockhd); + dotlock_release (lockhd); + dotlock_destroy (lockhd); } return rc; } diff --git a/g10/keyring.c b/g10/keyring.c index fb399d4a2..108e107b2 100644 --- a/g10/keyring.c +++ b/g10/keyring.c @@ -30,7 +30,7 @@ #include "util.h" #include "keyring.h" #include "packet.h" -#include "keydb.h" +#include "keydb.h" #include "options.h" #include "main.h" /*for check_key_signature()*/ #include "i18n.h" @@ -45,14 +45,14 @@ struct off_item { /*off_t off;*/ }; -typedef struct off_item **OffsetHashTable; +typedef struct off_item **OffsetHashTable; typedef struct keyring_name *KR_NAME; struct keyring_name { struct keyring_name *next; int secret; - DOTLOCK lockhd; + dotlock_t lockhd; int is_locked; int did_full_scan; char fname[1]; @@ -76,7 +76,7 @@ struct keyring_handle { int error; } current; struct { - CONST_KR_NAME kr; + CONST_KR_NAME kr; off_t offset; size_t pk_no; size_t uid_no; @@ -99,7 +99,7 @@ static struct off_item * new_offset_item (void) { struct off_item *k; - + k = xmalloc_clear (sizeof *k); return k; } @@ -118,7 +118,7 @@ release_offset_items (struct off_item *k) } #endif -static OffsetHashTable +static OffsetHashTable new_offset_hash_table (void) { struct off_item **tbl; @@ -159,7 +159,7 @@ update_offset_hash_table (OffsetHashTable tbl, u32 *kid, off_t off) for (k = tbl[(kid[1] & 0x07ff)]; k; k = k->next) { - if (k->kid[0] == kid[0] && k->kid[1] == kid[1]) + if (k->kid[0] == kid[0] && k->kid[1] == kid[1]) { /*k->off = off;*/ return; @@ -189,7 +189,7 @@ update_offset_hash_table_from_kb (OffsetHashTable tbl, KBNODE node, off_t off) } } -/* +/* * Register a filename for plain keyring files. ptr is set to a * pointer to be used to create a handles etc, or the already-issued * pointer if it has already been registered. The function returns 1 @@ -241,12 +241,12 @@ keyring_is_writable (void *token) return r? !access (r->fname, W_OK) : 0; } - + /* Create a new handle for the resource associated with TOKEN. SECRET is just just as a cross-check. - + The returned handle must be released using keyring_release (). */ KEYRING_HANDLE keyring_new (void *token, int secret) @@ -255,7 +255,7 @@ keyring_new (void *token, int secret) KR_NAME resource = token; assert (resource && !resource->secret == !secret); - + hd = xmalloc_clear (sizeof *hd); hd->resource = resource; hd->secret = !!secret; @@ -263,7 +263,7 @@ keyring_new (void *token, int secret) return hd; } -void +void keyring_release (KEYRING_HANDLE hd) { if (!hd) @@ -290,7 +290,7 @@ keyring_get_resource_name (KEYRING_HANDLE hd) * Lock the keyring with the given handle, or unlok if yes is false. * We ignore the handle and lock all registered files. */ -int +int keyring_lock (KEYRING_HANDLE hd, int yes) { KR_NAME kr; @@ -302,7 +302,7 @@ keyring_lock (KEYRING_HANDLE hd, int yes) if (!keyring_is_writable(kr)) continue; if (!kr->lockhd) { - kr->lockhd = create_dotlock( kr->fname ); + kr->lockhd = dotlock_create (kr->fname, 0); if (!kr->lockhd) { log_info ("can't allocate lock for `%s'\n", kr->fname ); rc = G10ERR_GENERAL; @@ -311,18 +311,18 @@ keyring_lock (KEYRING_HANDLE hd, int yes) } if (rc) return rc; - + /* and now set the locks */ for (kr=kr_names; kr; kr = kr->next) { if (!keyring_is_writable(kr)) continue; if (kr->is_locked) ; - else if (make_dotlock (kr->lockhd, -1) ) { + else if (dotlock_take (kr->lockhd, -1) ) { log_info ("can't lock `%s'\n", kr->fname ); rc = G10ERR_GENERAL; } - else + else kr->is_locked = 1; } } @@ -333,12 +333,12 @@ keyring_lock (KEYRING_HANDLE hd, int yes) continue; if (!kr->is_locked) ; - else if (release_dotlock (kr->lockhd)) + else if (dotlock_release (kr->lockhd)) log_info ("can't unlock `%s'\n", kr->fname ); - else + else kr->is_locked = 0; } - } + } return rc; } @@ -348,7 +348,7 @@ keyring_lock (KEYRING_HANDLE hd, int yes) /* * Return the last found keyring. Caller must free it. * The returned keyblock has the kbode flag bit 0 set for the node with - * the public key used to locate the keyblock or flag bit 1 set for + * the public key used to locate the keyblock or flag bit 1 set for * the user ID node. */ int @@ -394,7 +394,7 @@ keyring_get_keyblock (KEYRING_HANDLE hd, KBNODE *ret_kb) init_packet (pkt); continue; } - if (rc) { + if (rc) { log_error ("keyring_get_keyblock: read error: %s\n", g10_errstr(rc) ); rc = G10ERR_INV_KEYRING; @@ -416,14 +416,14 @@ keyring_get_keyblock (KEYRING_HANDLE hd, KBNODE *ret_kb) in_cert = 1; if (pkt->pkttype == PKT_RING_TRUST) { /*(this code is duplicated after the loop)*/ - if ( lastnode + if ( lastnode && lastnode->pkt->pkttype == PKT_SIGNATURE && (pkt->pkt.ring_trust->sigcache & 1) ) { - /* This is a ring trust packet with a checked signature + /* This is a ring trust packet with a checked signature * status cache following directly a signature paket. * Set the cache status into that signature packet. */ PKT_signature *sig = lastnode->pkt->pkt.signature; - + sig->flags.checked = 1; sig->flags.valid = !!(pkt->pkt.ring_trust->sigcache & 2); } @@ -441,27 +441,27 @@ keyring_get_keyblock (KEYRING_HANDLE hd, KBNODE *ret_kb) keyblock = node; else add_kbnode (keyblock, node); - + if ( pkt->pkttype == PKT_PUBLIC_KEY || pkt->pkttype == PKT_PUBLIC_SUBKEY || pkt->pkttype == PKT_SECRET_KEY - || pkt->pkttype == PKT_SECRET_SUBKEY) + || pkt->pkttype == PKT_SECRET_SUBKEY) { if (++pk_no == hd->found.pk_no) node->flag |= 1; } - else if ( pkt->pkttype == PKT_USER_ID) + else if ( pkt->pkttype == PKT_USER_ID) { if (++uid_no == hd->found.uid_no) node->flag |= 2; } - + pkt = xmalloc (sizeof *pkt); init_packet(pkt); } set_packet_list_mode(save_mode); - if (rc == -1 && keyblock) + if (rc == -1 && keyblock) rc = 0; /* got the entire keyblock */ if (rc || !ret_kb) @@ -469,7 +469,7 @@ keyring_get_keyblock (KEYRING_HANDLE hd, KBNODE *ret_kb) else { /*(duplicated form the loop body)*/ if ( pkt && pkt->pkttype == PKT_RING_TRUST - && lastnode + && lastnode && lastnode->pkt->pkttype == PKT_SIGNATURE && (pkt->pkt.ring_trust->sigcache & 1) ) { PKT_signature *sig = lastnode->pkt->pkt.signature; @@ -483,7 +483,7 @@ keyring_get_keyblock (KEYRING_HANDLE hd, KBNODE *ret_kb) iobuf_close(a); /* Make sure that future search operations fail immediately when - * we know that we are working on a invalid keyring + * we know that we are working on a invalid keyring */ if (rc == G10ERR_INV_KEYRING) hd->current.error = rc; @@ -543,11 +543,11 @@ keyring_insert_keyblock (KEYRING_HANDLE hd, KBNODE kb) fname = hd->found.kr->fname; else if (hd->current.kr) fname = hd->current.kr->fname; - else + else fname = hd->resource? hd->resource->fname:NULL; if (!fname) - return G10ERR_GENERAL; + return G10ERR_GENERAL; /* close this one otherwise we will lose the position for * a next search. Fixme: it would be better to adjust the position @@ -562,7 +562,7 @@ keyring_insert_keyblock (KEYRING_HANDLE hd, KBNODE kb) { update_offset_hash_table_from_kb (kr_offtbl, kb, 0); } - + return rc; } @@ -608,10 +608,10 @@ keyring_delete_keyblock (KEYRING_HANDLE hd) -/* +/* * Start the next search on this handle right at the beginning */ -int +int keyring_search_reset (KEYRING_HANDLE hd) { assert (hd); @@ -621,17 +621,17 @@ keyring_search_reset (KEYRING_HANDLE hd) hd->current.iobuf = NULL; hd->current.eof = 0; hd->current.error = 0; - + hd->found.kr = NULL; hd->found.offset = 0; - return 0; + return 0; } static int prepare_search (KEYRING_HANDLE hd) { - if (hd->current.error) + if (hd->current.error) return hd->current.error; /* still in error state */ if (hd->current.kr && !hd->current.eof) { @@ -640,7 +640,7 @@ prepare_search (KEYRING_HANDLE hd) return 0; /* okay */ } - if (!hd->current.kr && hd->current.eof) + if (!hd->current.kr && hd->current.eof) return -1; /* still EOF */ if (!hd->current.kr) { /* start search with first keyring */ @@ -652,7 +652,7 @@ prepare_search (KEYRING_HANDLE hd) assert (!hd->current.iobuf); } else { /* EOF */ - iobuf_close (hd->current.iobuf); + iobuf_close (hd->current.iobuf); hd->current.iobuf = NULL; hd->current.kr = NULL; hd->current.eof = 1; @@ -809,7 +809,7 @@ compare_name (int mode, const char *name, const char *uid, size_t uidlen) int i; const char *s, *se; - if (mode == KEYDB_SEARCH_MODE_EXACT) { + if (mode == KEYDB_SEARCH_MODE_EXACT) { for (i=0; name[i] && uidlen; i++, uidlen--) if (uid[i] != name[i]) break; @@ -820,7 +820,7 @@ compare_name (int mode, const char *name, const char *uid, size_t uidlen) if (ascii_memistr( uid, uidlen, name )) return 0; } - else if ( mode == KEYDB_SEARCH_MODE_MAIL + else if ( mode == KEYDB_SEARCH_MODE_MAIL || mode == KEYDB_SEARCH_MODE_MAILSUB || mode == KEYDB_SEARCH_MODE_MAILEND) { for (i=0, s= uid; i < uidlen && *s != '<'; s++, i++) @@ -832,7 +832,7 @@ compare_name (int mode, const char *name, const char *uid, size_t uidlen) ; if (i < uidlen) { i = se - s; - if (mode == KEYDB_SEARCH_MODE_MAIL) { + if (mode == KEYDB_SEARCH_MODE_MAIL) { if( strlen(name)-2 == i && !ascii_memcasecmp( s, name+1, i) ) return 0; @@ -856,11 +856,11 @@ compare_name (int mode, const char *name, const char *uid, size_t uidlen) } -/* +/* * Search through the keyring(s), starting at the current position, * for a keyblock which contains one of the keys described in the DESC array. */ -int +int keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc, size_t *descindex) { @@ -880,28 +880,28 @@ keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, /* figure out what information we need */ need_uid = need_words = need_keyid = need_fpr = any_skip = 0; - for (n=0; n < ndesc; n++) + for (n=0; n < ndesc; n++) { - switch (desc[n].mode) + switch (desc[n].mode) { - case KEYDB_SEARCH_MODE_EXACT: + case KEYDB_SEARCH_MODE_EXACT: case KEYDB_SEARCH_MODE_SUBSTR: case KEYDB_SEARCH_MODE_MAIL: case KEYDB_SEARCH_MODE_MAILSUB: case KEYDB_SEARCH_MODE_MAILEND: need_uid = 1; break; - case KEYDB_SEARCH_MODE_WORDS: + case KEYDB_SEARCH_MODE_WORDS: need_uid = 1; need_words = 1; break; - case KEYDB_SEARCH_MODE_SHORT_KID: + case KEYDB_SEARCH_MODE_SHORT_KID: case KEYDB_SEARCH_MODE_LONG_KID: need_keyid = 1; break; - case KEYDB_SEARCH_MODE_FPR16: + case KEYDB_SEARCH_MODE_FPR16: case KEYDB_SEARCH_MODE_FPR20: - case KEYDB_SEARCH_MODE_FPR: + case KEYDB_SEARCH_MODE_FPR: need_fpr = 1; break; case KEYDB_SEARCH_MODE_FIRST: @@ -910,7 +910,7 @@ keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, break; default: break; } - if (desc[n].skipfnc) + if (desc[n].skipfnc) { any_skip = 1; need_keyid = 1; @@ -929,7 +929,7 @@ keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, else if (ndesc == 1 && desc[0].mode == KEYDB_SEARCH_MODE_LONG_KID) { struct off_item *oi; - + oi = lookup_offset_hash_table (kr_offtbl, desc[0].u.kid); if (!oi) { /* We know that we don't have this key */ @@ -938,9 +938,9 @@ keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, return -1; } /* We could now create a positive search status and return. - * However the problem is that another instance of gpg may + * However the problem is that another instance of gpg may * have changed the keyring so that the offsets are not valid - * anymore - therefore we don't do it + * anymore - therefore we don't do it */ } @@ -951,13 +951,13 @@ keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, log_debug ("word search mode does not yet work\n"); /* FIXME: here is a long standing bug in our function and in addition we just use the first search description */ - for (n=0; n < ndesc && !name; n++) + for (n=0; n < ndesc && !name; n++) { - if (desc[n].mode == KEYDB_SEARCH_MODE_WORDS) + if (desc[n].mode == KEYDB_SEARCH_MODE_WORDS) name = desc[n].u.name; } assert (name); - if ( !hd->word_match.name || strcmp (hd->word_match.name, name) ) + if ( !hd->word_match.name || strcmp (hd->word_match.name, name) ) { /* name changed */ xfree (hd->word_match.name); @@ -975,23 +975,23 @@ keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, main_offset = 0; pk_no = uid_no = 0; initial_skip = 1; /* skip until we see the start of a keyblock */ - while (!(rc=search_packet (hd->current.iobuf, &pkt, &offset, need_uid))) + while (!(rc=search_packet (hd->current.iobuf, &pkt, &offset, need_uid))) { byte afp[MAX_FINGERPRINT_LEN]; size_t an; - if (pkt.pkttype == PKT_PUBLIC_KEY || pkt.pkttype == PKT_SECRET_KEY) + if (pkt.pkttype == PKT_PUBLIC_KEY || pkt.pkttype == PKT_SECRET_KEY) { main_offset = offset; pk_no = uid_no = 0; initial_skip = 0; } - if (initial_skip) + if (initial_skip) { free_packet (&pkt); continue; } - + pk = NULL; sk = NULL; uid = NULL; @@ -1012,13 +1012,13 @@ keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, if (use_offtbl && !kr_offtbl_ready) update_offset_hash_table (kr_offtbl, aki, main_offset); } - else if (pkt.pkttype == PKT_USER_ID) + else if (pkt.pkttype == PKT_USER_ID) { uid = pkt.pkt.user_id; ++uid_no; } else if ( pkt.pkttype == PKT_SECRET_KEY - || pkt.pkttype == PKT_SECRET_SUBKEY) + || pkt.pkttype == PKT_SECRET_SUBKEY) { sk = pkt.pkt.secret_key; ++pk_no; @@ -1030,28 +1030,28 @@ keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, } if (need_keyid) keyid_from_sk (sk, aki); - + } - for (n=0; n < ndesc; n++) + for (n=0; n < ndesc; n++) { switch (desc[n].mode) { - case KEYDB_SEARCH_MODE_NONE: + case KEYDB_SEARCH_MODE_NONE: BUG (); break; - case KEYDB_SEARCH_MODE_EXACT: + case KEYDB_SEARCH_MODE_EXACT: case KEYDB_SEARCH_MODE_SUBSTR: case KEYDB_SEARCH_MODE_MAIL: case KEYDB_SEARCH_MODE_MAILSUB: case KEYDB_SEARCH_MODE_MAILEND: - case KEYDB_SEARCH_MODE_WORDS: + case KEYDB_SEARCH_MODE_WORDS: if ( uid && !compare_name (desc[n].mode, desc[n].u.name, - uid->name, uid->len)) + uid->name, uid->len)) goto found; break; - - case KEYDB_SEARCH_MODE_SHORT_KID: + + case KEYDB_SEARCH_MODE_SHORT_KID: if ((pk||sk) && desc[n].u.kid[1] == aki[1]) goto found; break; @@ -1065,19 +1065,19 @@ keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, goto found; break; case KEYDB_SEARCH_MODE_FPR20: - case KEYDB_SEARCH_MODE_FPR: + case KEYDB_SEARCH_MODE_FPR: if ((pk||sk) && !memcmp (desc[n].u.fpr, afp, 20)) goto found; break; - case KEYDB_SEARCH_MODE_FIRST: + case KEYDB_SEARCH_MODE_FIRST: if (pk||sk) goto found; break; - case KEYDB_SEARCH_MODE_NEXT: + case KEYDB_SEARCH_MODE_NEXT: if (pk||sk) goto found; break; - default: + default: rc = G10ERR_INV_ARG; goto found; } @@ -1089,7 +1089,7 @@ keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, meaningful if this function returns with no errors. */ if(descindex) *descindex=n; - for (n=any_skip?0:ndesc; n < ndesc; n++) + for (n=any_skip?0:ndesc; n < ndesc; n++) { if (desc[n].skipfnc && desc[n].skipfnc (desc[n].skipfncvalue, aki, uid)) @@ -1115,12 +1115,12 @@ keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, if (use_offtbl && !kr_offtbl_ready) { KR_NAME kr; - + /* First set the did_full_scan flag for this keyring (ignore secret keyrings) */ for (kr=kr_names; kr; kr = kr->next) { - if (!kr->secret && hd->resource == kr) + if (!kr->secret && hd->resource == kr) { kr->did_full_scan = 1; break; @@ -1130,14 +1130,14 @@ keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, offtbl ready */ for (kr=kr_names; kr; kr = kr->next) { - if (!kr->secret && !kr->did_full_scan) + if (!kr->secret && !kr->did_full_scan) break; } if (!kr) kr_offtbl_ready = 1; } } - else + else hd->current.error = rc; free_packet(&pkt); @@ -1149,7 +1149,7 @@ keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, static int create_tmp_file (const char *template, char **r_bakfname, char **r_tmpfname, IOBUF *r_fp) -{ +{ char *bakfname, *tmpfname; mode_t oldmask; @@ -1173,7 +1173,7 @@ create_tmp_file (const char *template, strcpy (tmpfname,template); strcpy (tmpfname+strlen(template)-4, EXTSEP_S "tmp"); } - else + else { /* file does not end with gpg; hmmm */ bakfname = xmalloc (strlen( template ) + 5); strcpy (stpcpy(bakfname, template), EXTSEP_S "bak"); @@ -1206,7 +1206,7 @@ create_tmp_file (const char *template, xfree (bakfname); return G10ERR_OPEN_FILE; } - + *r_bakfname = bakfname; *r_tmpfname = tmpfname; return 0; @@ -1233,7 +1233,7 @@ rename_tmp_file (const char *bakfname, const char *tmpfname, /* first make a backup file except for secret keyrings */ if (!secret) - { + { #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__) remove (bakfname); #endif @@ -1244,7 +1244,7 @@ rename_tmp_file (const char *bakfname, const char *tmpfname, return G10ERR_RENAME_FILE; } } - + /* then rename the file */ #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__) remove( fname ); @@ -1298,10 +1298,10 @@ write_keyblock (IOBUF fp, KBNODE keyblock) { KBNODE kbctx = NULL, node; int rc; - - while ( (node = walk_kbnode (keyblock, &kbctx, 0)) ) + + while ( (node = walk_kbnode (keyblock, &kbctx, 0)) ) { - if (node->pkt->pkttype == PKT_RING_TRUST) + if (node->pkt->pkttype == PKT_RING_TRUST) continue; /* we write it later on our own */ if ( (rc = build_packet (fp, node->pkt) )) @@ -1310,12 +1310,12 @@ write_keyblock (IOBUF fp, KBNODE keyblock) node->pkt->pkttype, g10_errstr(rc) ); return rc; } - if (node->pkt->pkttype == PKT_SIGNATURE) + if (node->pkt->pkttype == PKT_SIGNATURE) { /* always write a signature cache packet */ PKT_signature *sig = node->pkt->pkt.signature; unsigned int cacheval = 0; - - if (sig->flags.checked) + + if (sig->flags.checked) { cacheval |= 1; if (sig->flags.valid) @@ -1333,7 +1333,7 @@ write_keyblock (IOBUF fp, KBNODE keyblock) return 0; } -/* +/* * Walk over all public keyrings, check the signatures and replace the * keyring with a new one where the signature cache is then updated. * This is only done for the public keyrings. @@ -1378,7 +1378,7 @@ keyring_rebuild_cache (void *token,int noisy) * the original file is closed */ tmpfp = NULL; } - rc = lastresname? rename_tmp_file (bakfilename, tmpfilename, + rc = lastresname? rename_tmp_file (bakfilename, tmpfilename, lastresname, 0) : 0; xfree (tmpfilename); tmpfilename = NULL; xfree (bakfilename); bakfilename = NULL; @@ -1391,10 +1391,10 @@ keyring_rebuild_cache (void *token,int noisy) if (rc) goto leave; } - + release_kbnode (keyblock); rc = keyring_get_keyblock (hd, &keyblock); - if (rc) + if (rc) { log_error ("keyring_get_keyblock failed: %s\n", g10_errstr(rc)); goto leave; @@ -1438,7 +1438,7 @@ keyring_rebuild_cache (void *token,int noisy) sigcount++; } } - + /* write the keyblock to the temporary file */ rc = write_keyblock (tmpfp, keyblock); if (rc) @@ -1448,10 +1448,10 @@ keyring_rebuild_cache (void *token,int noisy) log_info(_("%lu keys cached so far (%lu signatures)\n"), count, sigcount ); - } /* end main loop */ + } /* end main loop */ if (rc == -1) rc = 0; - if (rc) + if (rc) { log_error ("keyring_search failed: %s\n", g10_errstr(rc)); goto leave; @@ -1479,8 +1479,8 @@ keyring_rebuild_cache (void *token,int noisy) leave: if (tmpfp) iobuf_cancel (tmpfp); - xfree (tmpfilename); - xfree (bakfilename); + xfree (tmpfilename); + xfree (bakfilename); release_kbnode (keyblock); keyring_lock (hd, 0); keyring_release (hd); @@ -1503,13 +1503,13 @@ do_copy (int mode, const char *fname, KBNODE root, int secret, char *bakfname = NULL; char *tmpfname = NULL; - /* Open the source file. Because we do a rename, we have to check the + /* Open the source file. Because we do a rename, we have to check the permissions of the file */ if (access (fname, W_OK)) return G10ERR_WRITE_FILE; fp = iobuf_open (fname); - if (mode == 1 && !fp && errno == ENOENT) { + if (mode == 1 && !fp && errno == ENOENT) { /* insert mode but file does not exist: create a new file */ KBNODE kbctx, node; mode_t oldmask; diff --git a/g10/signal.c b/g10/signal.c index 4aaa11eaa..086bf51d3 100644 --- a/g10/signal.c +++ b/g10/signal.c @@ -66,7 +66,7 @@ init_one_signal (int sig, RETSIGTYPE (*handler)(int), int check_ign ) sigemptyset (&nact.sa_mask); nact.sa_flags = 0; sigaction ( sig, &nact, NULL); -#else +#else RETSIGTYPE (*ohandler)(int); ohandler = signal (sig, handler); @@ -122,7 +122,7 @@ got_fatal_signal( int sig ) /* Reset action to default action and raise signal again. */ init_one_signal (sig, SIG_DFL, 0); - remove_lockfiles (); + dotlock_remove_lockfiles (); #ifdef __riscos__ riscos_close_fds (); #endif /* __riscos__ */ @@ -165,7 +165,7 @@ pause_on_sigusr( int which ) sigsuspend( &oldmask ); caught_sigusr1 = 0; sigprocmask( SIG_UNBLOCK, &mask, NULL ); -#else +#else assert (which == 1); sighold (SIGUSR1); while (!caught_sigusr1) diff --git a/g10/tdbio.c b/g10/tdbio.c index 282411e5d..2732b1b27 100644 --- a/g10/tdbio.c +++ b/g10/tdbio.c @@ -86,7 +86,7 @@ struct cmp_xdir_struct { static char *db_name; -static DOTLOCK lockhandle; +static dotlock_t lockhandle; static int is_locked; static int db_fd = -1; static int in_transaction; @@ -248,7 +248,7 @@ put_record_into_cache( ulong recno, const char *data ) if( !n ) n = 1; if( !is_locked ) { - if( make_dotlock( lockhandle, -1 ) ) + if (dotlock_take (lockhandle, -1)) log_fatal("can't acquire lock - giving up\n"); else is_locked = 1; @@ -267,7 +267,7 @@ put_record_into_cache( ulong recno, const char *data ) } } if( !opt.lock_once ) { - if( !release_dotlock( lockhandle ) ) + if (!dotlock_release (lockhandle)) is_locked = 0; } assert( unused ); @@ -309,7 +309,7 @@ tdbio_sync() return 0; if( !is_locked ) { - if( make_dotlock( lockhandle, -1 ) ) + if (dotlock_take (lockhandle, -1)) log_fatal("can't acquire lock - giving up\n"); else is_locked = 1; @@ -324,7 +324,7 @@ tdbio_sync() } cache_is_dirty = 0; if( did_lock && !opt.lock_once ) { - if( !release_dotlock( lockhandle ) ) + if (!dotlock_release (lockhandle)) is_locked = 0; } @@ -364,7 +364,7 @@ tdbio_end_transaction() if( !in_transaction ) log_bug("tdbio: no active transaction\n"); if( !is_locked ) { - if( make_dotlock( lockhandle, -1 ) ) + if (dotlock_take (lockhandle, -1)) log_fatal("can't acquire lock - giving up\n"); else is_locked = 1; @@ -374,7 +374,7 @@ tdbio_end_transaction() rc = tdbio_sync(); unblock_all_signals(); if( !opt.lock_once ) { - if( !release_dotlock( lockhandle ) ) + if (!dotlock_release (lockhandle)) is_locked = 0; } return rc; @@ -414,7 +414,7 @@ static void cleanup(void) { if( is_locked ) { - if( !release_dotlock(lockhandle) ) + if (!dotlock_release (lockhandle)) is_locked = 0; } } @@ -447,7 +447,7 @@ create_version_record (void) { TRUSTREC rec; int rc; - + memset( &rec, 0, sizeof rec ); rec.r.ver.version = 3; rec.r.ver.created = make_timestamp(); @@ -517,10 +517,10 @@ tdbio_set_dbname( const char *new_dbname, int create ) db_name = fname; #ifdef __riscos__ if( !lockhandle ) - lockhandle = create_dotlock( db_name ); + lockhandle = dotlock_create (db_name, 0); if( !lockhandle ) log_fatal( _("can't create lock for `%s'\n"), db_name ); - if( make_dotlock( lockhandle, -1 ) ) + if (dotlock_take (lockhandle, -1)) log_fatal( _("can't lock `%s'\n"), db_name ); #endif /* __riscos__ */ oldmask=umask(077); @@ -540,7 +540,7 @@ tdbio_set_dbname( const char *new_dbname, int create ) #ifndef __riscos__ if( !lockhandle ) - lockhandle = create_dotlock( db_name ); + lockhandle = dotlock_create (db_name, 0); if( !lockhandle ) log_fatal( _("can't create lock for `%s'\n"), db_name ); #endif /* !__riscos__ */ @@ -583,11 +583,11 @@ open_db() assert( db_fd == -1 ); if (!lockhandle ) - lockhandle = create_dotlock( db_name ); + lockhandle = dotlock_create (db_name, 0); if (!lockhandle ) log_fatal( _("can't create lock for `%s'\n"), db_name ); #ifdef __riscos__ - if (make_dotlock( lockhandle, -1 ) ) + if (dotlock_take (lockhandle, -1)) log_fatal( _("can't lock `%s'\n"), db_name ); #endif /* __riscos__ */ db_fd = open (db_name, O_RDWR | MY_O_BINARY ); @@ -613,7 +613,7 @@ open_db() { migrate_from_v2 (); } - + /* read the version record */ if (tdbio_read_record (0, &rec, RECTYPE_VER ) ) log_fatal( _("%s: invalid trustdb\n"), db_name ); @@ -690,7 +690,7 @@ tdbio_read_model(void) { TRUSTREC vr; int rc; - + rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), @@ -1008,7 +1008,7 @@ drop_from_hashtable( ulong table, byte *key, int keylen, ulong recnum ) */ static int lookup_hashtable( ulong table, const byte *key, size_t keylen, - int (*cmpfnc)(const void*, const TRUSTREC *), + int (*cmpfnc)(const void*, const TRUSTREC *), const void *cmpdata, TRUSTREC *rec ) { int rc; @@ -1534,12 +1534,12 @@ migrate_from_v2 () /* We have some restrictions here. We can't use the version record * and we can't use any of the old hashtables because we dropped the * code. So we first collect all ownertrusts and then use a second - * pass fo find the associated keys. We have to do this all without using + * pass fo find the associated keys. We have to do this all without using * the regular record read functions. */ /* get all the ownertrusts */ - if (lseek (db_fd, 0, SEEK_SET ) == -1 ) + if (lseek (db_fd, 0, SEEK_SET ) == -1 ) log_fatal ("migrate_from_v2: lseek failed: %s\n", strerror (errno)); for (recno=0;;recno++) { @@ -1553,7 +1553,7 @@ migrate_from_v2 () if (*oldbuf != 2) continue; - + /* v2 dir record */ if (ottable_used == ottable_size) { @@ -1570,7 +1570,7 @@ migrate_from_v2 () log_info ("found %d ownertrust records\n", ottable_used); /* Read again and find the fingerprints */ - if (lseek (db_fd, 0, SEEK_SET ) == -1 ) + if (lseek (db_fd, 0, SEEK_SET ) == -1 ) log_fatal ("migrate_from_v2: lseek failed: %s\n", strerror (errno)); for (recno=0;;recno++) { @@ -1582,7 +1582,7 @@ migrate_from_v2 () if (n != 40) log_fatal ("migrate_from_v2: read error or short read\n"); - if (*oldbuf != 3) + if (*oldbuf != 3) continue; /* v2 key record */ @@ -1603,7 +1603,7 @@ migrate_from_v2 () if (create_version_record ()) log_fatal ("failed to recreate version record of `%s'\n", db_name); - /* access the hash table, so it is store just after the version record, + /* access the hash table, so it is store just after the version record, * this is not needed put a dump is more pretty */ get_trusthashrec (); @@ -1613,7 +1613,7 @@ migrate_from_v2 () { if (!ottable[i].okay) continue; - + memset (&rec, 0, sizeof rec); rec.recnum = tdbio_new_recnum (); rec.rectype = RECTYPE_TRUST; diff --git a/include/dotlock.h b/include/dotlock.h new file mode 100644 index 000000000..920a81a16 --- /dev/null +++ b/include/dotlock.h @@ -0,0 +1,112 @@ +/* dotlock.h - dotfile locking declarations + * Copyright (C) 2000, 2001, 2006, 2011 Free Software Foundation, Inc. + * + * This file is part of JNLIB, which is a subsystem of GnuPG. + * + * JNLIB is free software; you can redistribute it and/or modify it + * under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * JNLIB is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see <http://www.gnu.org/licenses/>. + * + * ALTERNATIVELY, this file may be distributed under the terms of the + * following license, in which case the provisions of this license are + * required INSTEAD OF the GNU Lesser General License or the GNU + * General Public License. If you wish to allow use of your version of + * this file only under the terms of the GNU Lesser General License or + * the GNU General Public License, and not to allow others to use your + * version of this file under the terms of the following license, + * indicate your decision by deleting this paragraph and the license + * below. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LIBJNLIB_DOTLOCK_H +#define LIBJNLIB_DOTLOCK_H + +/* See dotlock.c for a description. */ + +#ifdef DOTLOCK_EXT_SYM_PREFIX +# ifndef _DOTLOCK_PREFIX +# define _DOTLOCK_PREFIX1(x,y) x ## y +# define _DOTLOCK_PREFIX2(x,y) _DOTLOCK_PREFIX1(x,y) +# define _DOTLOCK_PREFIX(x) _DOTLOCK_PREFIX2(DOTLOCK_EXT_SYM_PREFIX,x) +# endif /*_DOTLOCK_PREFIX*/ +# define dotlock_disable _DOTLOCK_PREFIX(dotlock_disable) +# define dotlock_create _DOTLOCK_PREFIX(dotlock_create) +# define dotlock_set_fd _DOTLOCK_PREFIX(dotlock_set_fd) +# define dotlock_get_fd _DOTLOCK_PREFIX(dotlock_get_fd) +# define dotlock_destroy _DOTLOCK_PREFIX(dotlock_destroy) +# define dotlock_take _DOTLOCK_PREFIX(dotlock_take) +# define dotlock_release _DOTLOCK_PREFIX(dotlock_release) +# define dotlock_remove_lockfiles _DOTLOCK_PREFIX(dotlock_remove_lockfiles) +#endif /*DOTLOCK_EXT_SYM_PREFIX*/ + +#ifdef __cplusplus +extern "C" +{ +#if 0 +} +#endif +#endif + + +struct dotlock_handle; +typedef struct dotlock_handle *dotlock_t; + +void dotlock_disable (void); +dotlock_t dotlock_create (const char *file_to_lock, unsigned int flags); +void dotlock_set_fd (dotlock_t h, int fd); +int dotlock_get_fd (dotlock_t h); +void dotlock_destroy (dotlock_t h); +int dotlock_take (dotlock_t h, long timeout); +int dotlock_release (dotlock_t h); +void dotlock_remove_lockfiles (void); + +#ifdef __cplusplus +} +#endif +#endif /*LIBJNLIB_DOTLOCK_H*/ diff --git a/include/util.h b/include/util.h index 4b3c40bbb..9303a50b1 100644 --- a/include/util.h +++ b/include/util.h @@ -124,15 +124,7 @@ const char *strusage( int level ); /*-- dotlock.c --*/ -struct dotlock_handle; -typedef struct dotlock_handle *DOTLOCK; - -void disable_dotlock(void); -DOTLOCK create_dotlock( const char *file_to_lock ); -void destroy_dotlock ( DOTLOCK h ); -int make_dotlock( DOTLOCK h, long timeout ); -int release_dotlock( DOTLOCK h ); -void remove_lockfiles (void); +#include "../include/dotlock.h" /*-- fileutil.c --*/ char * make_basename(const char *filepath, const char *inputpath); @@ -217,10 +209,10 @@ int strncasecmp (const char *, const char *b, size_t n); /* The definition of the structure is private, we only need it here, so it can be allocated on the stack. */ struct private_membuf_s { - size_t len; - size_t size; - char *buf; - int out_of_core; + size_t len; + size_t size; + char *buf; + int out_of_core; }; typedef struct private_membuf_s membuf_t; diff --git a/util/dotlock.c b/util/dotlock.c index ccbb6e89d..c5f3a784b 100644 --- a/util/dotlock.c +++ b/util/dotlock.c @@ -1,344 +1,511 @@ /* dotlock.c - dotfile locking - * Copyright (C) 1998, 1999, 2000, 2001, 2004, - * 2005, 2009 Free Software Foundation, Inc. + * Copyright (C) 1998, 2000, 2001, 2003, 2004, + * 2005, 2006, 2008, 2010, 2011 Free Software Foundation, Inc. * - * This file is part of GnuPG. + * This file is part of JNLIB, which is a subsystem of GnuPG. * - * GnuPG is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. + * JNLIB is free software; you can redistribute it and/or modify it + * under the terms of either * - * GnuPG is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * JNLIB is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see <http://www.gnu.org/licenses/>. + * + * ALTERNATIVELY, this file may be distributed under the terms of the + * following license, in which case the provisions of this license are + * required INSTEAD OF the GNU Lesser General License or the GNU + * General Public License. If you wish to allow use of your version of + * this file only under the terms of the GNU Lesser General License or + * the GNU General Public License, and not to allow others to use your + * version of this file under the terms of the following license, + * indicate your decision by deleting this paragraph and the license + * below. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <config.h> +/* + Overview: + ========= + + This module implements advisory file locking in a portable way. + Due to the problems with POSIX fcntl locking a separate lock file + is used. It would be possible to use fcntl locking on this lock + file and thus avoid the weird auto unlock bug of POSIX while still + having an unproved better performance of fcntl locking. However + there are still problems left, thus we resort to use a hardlink + which has the well defined property that a link call will fail if + the target file already exists. + + Given that hardlinks are also available on NTFS file systems since + Windows XP; it will be possible to enhance this module to use + hardlinks even on Windows and thus allow Windows and Posix clients + to use locking on the same directory. This is not yet implemented; + instead we use a lockfile on Windows along with W32 style file + locking. + + On FAT file systems hardlinks are not supported. Thus this method + does not work. Our solution is to use a O_EXCL locking instead. + Querying the type of the file system is not easy to do in a + portable way (e.g. Linux has a statfs, BSDs have a the same call + but using different structures and constants). What we do instead + is to check at runtime whether link(2) works for a specific lock + file. + + + How to use: + =========== + + At program initialization time, the module should be explicitly + initialized: + + dotlock_create (NULL, 0); + + This installs an atexit handler and may also initialize mutex etc. + It is optional for non-threaded applications. Only the first call + has an effect. This needs to be done before any extra threads are + started. + + To create a lock file (which prepares it but does not take the + lock) you do: + + dotlock_t h + + h = dotlock_create (fname, 0); + if (!h) + error ("error creating lock file: %s\n", strerror (errno)); + + It is important to handle the error. For example on a read-only + file system a lock can't be created (but is usually not needed). + FNAME is the file you want to lock; the actual lockfile is that + name with the suffix ".lock" appended. On success a handle to be + used with the other functions is returned or NULL on error. Note + that the handle shall only be used by one thread at a time. This + function creates a unique file temporary file (".#lk*") in the same + directory as FNAME and returns a handle for further operations. + The module keeps track of theses unique files so that they will be + unlinked using the atexit handler. If you don't need the lock file + anymore, you may also explicitly remove it with a call to: + + dotlock_destroy (h); + + To actually lock the file, you use: + + if (dotlock_take (h, -1)) + error ("error taking lock: %s\n", strerror (errno)); + + This function will wait until the lock is acquired. If an + unexpected error occurs if will return non-zero and set ERRNO. If + you pass (0) instead of (-1) the function does not wait in case the + file is already locked but returns -1 and sets ERRNO to EACCES. + Any other positive value for the second parameter is considered a + timeout valuie in milliseconds. + + To release the lock you call: + + if (dotlock_release (h)) + error ("error releasing lock: %s\n", strerror (errno)); + + or, if the lock file is not anymore needed, you may just call + dotlock_destroy. However dotlock_release does some extra checks + before releasing the lock and prints diagnostics to help detecting + bugs. + + If you want to explicitly destroy all lock files you may call + + dotlock_remove_lockfiles (); + + which is the core of the installed atexit handler. In case your + application wants to disable locking completely it may call + + disable_locking () + + before any locks are created. + + There are two convenience functions to store an integer (e.g. a + file descriptor) value with the handle: + + void dotlock_set_fd (dotlock_t h, int fd); + int dotlock_get_fd (dotlock_t h); + + If nothing has been stored dotlock_get_fd returns -1. + + + + How to build: + ============= + + This module was originally developed for GnuPG but later changed to + allow its use without any GnuPG dependency. If you want to use it + with you application you may simply use it and it should figure out + most things automagically. + + You may use the common config.h file to pass macros, but take care + to pass -DHAVE_CONFIG_H to the compiler. Macros used by this + module are: + + DOTLOCK_USE_PTHREAD - Define if POSIX threads are in use. + + DOTLOCK_GLIB_LOGGING - Define this to use Glib logging functions. + + DOTLOCK_EXT_SYM_PREFIX - Prefix all external symbols with the + string to which this macro evaluates. + + GNUPG_MAJOR_VERSION - Defined when used by GnuPG. + + HAVE_DOSISH_SYSTEM - Defined for Windows etc. Will be + automatically defined if a the target is + Windows. + + HAVE_POSIX_SYSTEM - Internally defined to !HAVE_DOSISH_SYSTEM. + + HAVE_SIGNAL_H - Should be defined on Posix systems. If config.h + is not used defaults to defined. + + DIRSEP_C - Separation character for file name parts. + Usually not redefined. + + EXTSEP_S - Separation string for file name suffixes. + Usually not redefined. + + HAVE_W32CE_SYSTEM - Currently only used by GnuPG. + + Note that there is a test program t-dotlock which has compile + instructions at its end. At least for SMBFS and CIFS it is + important that 64 bit versions of stat are used; most programming + environments do this these days, just in case you want to compile + it on the command line, remember to pass -D_FILE_OFFSET_BITS=64 + + + Bugs: + ===== + + On Windows this module is not yet thread-safe. + + + Miscellaneous notes: + ==================== + + On hardlinks: + - Hardlinks are supported under Windows with NTFS since XP/Server2003. + - In Linux 2.6.33 both SMBFS and CIFS seem to support hardlinks. + - NFS supports hard links. But there are solvable problems. + - FAT does not support links + + On the file locking API: + - CIFS on Linux 2.6.33 supports several locking methods. + SMBFS seems not to support locking. No closer checks done. + - NFS supports Posix locks. flock is emulated in the server. + However there are a couple of problems; see below. + - FAT does not support locks. + - An advantage of fcntl locking is that R/W locks can be + implemented which is not easy with a straight lock file. + + On O_EXCL: + - Does not work reliable on NFS + - Should work on CIFS and SMBFS but how can we delete lockfiles? + + On NFS problems: + - Locks vanish if the server crashes and reboots. + - Client crashes keep the lock in the server until the client + re-connects. + - Communication problems may return unreliable error codes. The + MUA Postfix's workaround is to compare the link count after + seeing an error for link. However that gives a race. If using a + unique file to link to a lockfile and using stat to check the + link count instead of looking at the error return of link(2) is + the best solution. + - O_EXCL seems to have a race and may re-create a file anyway. + +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +/* Some quick replacements for stuff we usually expect to be defined + in config.h. Define HAVE_POSIX_SYSTEM for better readability. */ +#if !defined (HAVE_DOSISH_SYSTEM) && defined(_WIN32) +# define HAVE_DOSISH_SYSTEM 1 +#endif +#if !defined (HAVE_DOSISH_SYSTEM) && !defined (HAVE_POSIX_SYSTEM) +# define HAVE_POSIX_SYSTEM 1 +#endif + +/* With no config.h assume that we have sitgnal.h. */ +#if !defined (HAVE_CONFIG_H) && defined (HAVE_POSIX_SYSTEM) +# define HAVE_SIGNAL_H 1 +#endif + +/* Standard headers. */ #include <stdlib.h> +#include <stdio.h> #include <string.h> #include <errno.h> #include <ctype.h> #include <errno.h> #include <unistd.h> #ifdef HAVE_DOSISH_SYSTEM -# define WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN /* We only need the OS core stuff. */ # include <windows.h> #else +# include <sys/types.h> +# include <sys/stat.h> # include <sys/utsname.h> #endif #include <sys/types.h> -#ifndef _WIN32 #include <sys/time.h> -#endif #include <sys/stat.h> #include <fcntl.h> -#include <signal.h> -#include "types.h" -#include "util.h" -#include "memory.h" -#include "i18n.h" - - -/* The object describing a lock. */ -struct dotlock_handle { - struct dotlock_handle *next; - char *tname; /* name of lockfile template */ - char *lockname; /* name of the real lockfile */ - int locked; /* lock status */ - int disable; /* locking */ -#ifdef HAVE_DOSISH_SYSTEM - HANDLE lockhd; /* The W32 handle of the lock file. */ -#else - size_t nodename_off; /* Offset in TNAME to the nodename part. */ - size_t nodename_len; /* Length of the nodename part. */ +#ifdef HAVE_SIGNAL_H +# include <signal.h> +#endif +#ifdef DOTLOCK_USE_PTHREAD +# include <pthread.h> #endif -}; +#ifdef DOTLOCK_GLIB_LOGGING +# include <glib.h> +#endif -/* A list of of all lock handles. */ -static volatile DOTLOCK all_lockfiles; +#ifdef GNUPG_MAJOR_VERSION +# if GNUPG_MAJOR_VERSION > 1 +# include "libjnlib-config.h" +# else +# include "../include/util.h" +# endif +#endif +#ifdef HAVE_W32CE_SYSTEM +# include "utf8conv.h" /* WindowsCE requires filename conversion. */ +#endif -/* If this has the value true all locking is disabled. */ -static int never_lock; +#include "dotlock.h" -#ifdef _REENTRANT - /* fixme: acquire mutex on all_lockfiles */ -# define lock_all_lockfiles(h) do { } while (0) -# define unlock_all_lockfiles(h) do { } while (0) +/* Define constants for file name construction. */ +#if !defined(DIRSEP_C) && !defined(EXTSEP_S) +# ifdef HAVE_DOSISH_SYSTEM +# define DIRSEP_C '\\' +# define EXTSEP_S "." #else -# define lock_all_lockfiles(h) do { } while (0) -# define unlock_all_lockfiles(h) do { } while (0) +# define DIRSEP_C '/' +# define EXTSEP_S "." +# endif #endif +/* In GnuPG we use wrappers around the malloc fucntions. If they are + not defined we assume that this code is used outside of GnuPG and + fall back to the regular malloc functions. */ +#ifndef jnlib_malloc +# define jnlib_malloc(a) malloc ((a)) +# define jnlib_calloc(a,b) calloc ((a), (b)) +# define jnlib_free(a) free ((a)) +#endif +/* Wrapper to set ERRNO. */ +#ifndef jnlib_set_errno +# ifdef HAVE_W32CE_SYSTEM +# define jnlib_set_errno(e) gpg_err_set_errno ((e)) +# else +# define jnlib_set_errno(e) do { errno = (e); } while (0) +# endif +#endif -void -disable_dotlock(void) -{ - never_lock = 1; -} - - -/**************** - * Create a lockfile with the given name and return an object of - * type DOTLOCK which may be used later to actually do the lock. - * A cleanup routine gets installed to cleanup left over locks - * or other files used together with the lockmechanism. - * Althoug the function is called dotlock, this does not necessarily - * mean that real lockfiles are used - the function may decide to - * use fcntl locking. Calling the function with NULL only install - * the atexit handler and maybe used to assure that the cleanup - * is called after all other atexit handlers. - * - * Notes: This function creates a lock file in the same directory - * as file_to_lock with the name "file_to_lock.lock" - * A temporary file ".#lk.<hostname>.pid[.threadid] is used. - * This function does nothing for Windoze. - */ -DOTLOCK -create_dotlock( const char *file_to_lock ) -{ - static int initialized; - DOTLOCK h; -#ifndef HAVE_DOSISH_SYSTEM - int fd = -1; - char pidstr[16]; - struct utsname utsbuf; - const char *nodename; - const char *dirpart; - int dirpartlen; - size_t tnamelen; - int n; -#endif /*!HAVE_DOSISH_SYSTEM*/ +/* Gettext macro replacement. */ +#ifndef _ +# define _(a) (a) +#endif - if( !initialized ) { - atexit( remove_lockfiles ); - initialized = 1; - } - if( !file_to_lock ) - return NULL; +#ifdef GNUPG_MAJOR_VERSION +# define my_info_0(a) log_info ((a)) +# define my_info_1(a,b) log_info ((a), (b)) +# define my_info_2(a,b,c) log_info ((a), (b), (c)) +# define my_info_3(a,b,c,d) log_info ((a), (b), (c), (d)) +# define my_error_0(a) log_error ((a)) +# define my_error_1(a,b) log_error ((a), (b)) +# define my_error_2(a,b,c) log_error ((a), (b), (c)) +# define my_debug_1(a,b) log_debug ((a), (b)) +# define my_fatal_0(a) log_fatal ((a)) +#elif defined (DOTLOCK_GLIB_LOGGING) +# define my_info_0(a) g_message ((a)) +# define my_info_1(a,b) g_message ((a), (b)) +# define my_info_2(a,b,c) g_message ((a), (b), (c)) +# define my_info_3(a,b,c,d) g_message ((a), (b), (c), (d)) +# define my_error_0(a) g_warning ((a)) +# define my_error_1(a,b) g_warning ((a), (b)) +# define my_error_2(a,b,c) g_warning ((a), (b), (c)) +# define my_debug_1(a,b) g_debug ((a), (b)) +# define my_fatal_0(a) g_error ((a)) +#else +# define my_info_0(a) fprintf (stderr, (a)) +# define my_info_1(a,b) fprintf (stderr, (a), (b)) +# define my_info_2(a,b,c) fprintf (stderr, (a), (b), (c)) +# define my_info_3(a,b,c,d) fprintf (stderr, (a), (b), (c), (d)) +# define my_error_0(a) fprintf (stderr, (a)) +# define my_error_1(a,b) fprintf (stderr, (a), (b)) +# define my_error_2(a,b,c) fprintf (stderr, (a), (b), (c)) +# define my_debug_1(a,b) fprintf (stderr, (a), (b)) +# define my_fatal_0(a) do { fprintf (stderr,(a)); fflush (stderr); \ + abort (); } while (0) +#endif - h = xmalloc_clear( sizeof *h ); - if( never_lock ) { - h->disable = 1; - lock_all_lockfiles (h); - h->next = all_lockfiles; - all_lockfiles = h; - return h; - } -#ifndef HAVE_DOSISH_SYSTEM - snprintf (pidstr, sizeof pidstr, "%10d\n", (int)getpid() ); - /* create a temporary file */ - if( uname( &utsbuf ) ) - nodename = "unknown"; - else - nodename = utsbuf.nodename; - -# ifdef __riscos__ - { - char *iter = (char *) nodename; - for (; iter[0]; iter++) - if (iter[0] == '.') - iter[0] = '/'; - } -# endif /* __riscos__ */ - - if( !(dirpart = strrchr( file_to_lock, DIRSEP_C )) ) { - dirpart = EXTSEP_S; - dirpartlen = 1; - } - else { - dirpartlen = dirpart - file_to_lock; - dirpart = file_to_lock; - } - - lock_all_lockfiles (h); - h->next = all_lockfiles; - all_lockfiles = h; - - tnamelen = dirpartlen + 6+30+ strlen(nodename) + 10; - h->tname = xmalloc (tnamelen + 1); - h->nodename_len = strlen (nodename); - -# ifndef __riscos__ - snprintf (h->tname, tnamelen, "%.*s/.#lk%p.%n%s.%d", - dirpartlen, dirpart, (void *)h, &n, nodename, (int)getpid ()); -# else /* __riscos__ */ - snprintf (h->tname, tnamelen, "%.*s.lk%p/%n%s/%d", - dirpartlen, dirpart, (void *)h, &n, nodename, (int)getpid ()); -# endif /* __riscos__ */ - h->nodename_off = n; - - do { - errno = 0; - fd = open (h->tname, O_WRONLY|O_CREAT|O_EXCL, - S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR); - } while( fd == -1 && errno == EINTR ); - if( fd == -1 ) { - all_lockfiles = h->next; - log_error( "failed to create temporary file `%s': %s\n", - h->tname, strerror(errno)); - xfree(h->tname); - xfree(h); - return NULL; - } - if (write (fd, pidstr, 11) != 11) - goto write_failed; - if (write (fd, nodename, strlen (nodename)) != strlen (nodename)) - goto write_failed; - if (write (fd, "\n", 1 ) != 1) - goto write_failed; - if (close (fd)) - goto write_failed; + +/* The object describing a lock. */ +struct dotlock_handle +{ + struct dotlock_handle *next; + char *lockname; /* Name of the actual lockfile. */ + unsigned int locked:1; /* Lock status. */ + unsigned int disable:1; /* If true, locking is disabled. */ + unsigned int use_o_excl:1; /* Use open (O_EXCL) for locking. */ - unlock_all_lockfiles (h); - h->lockname = xmalloc( strlen(file_to_lock) + 6 ); - strcpy(stpcpy(h->lockname, file_to_lock), EXTSEP_S "lock"); - return h; + int extra_fd; /* A place for the caller to store an FD. */ - write_failed: - all_lockfiles = h->next; - unlock_all_lockfiles (h); - log_error ("error writing to `%s': %s\n", h->tname, strerror(errno)); - close (fd); - unlink (h->tname); - xfree (h->tname); - xfree (h); - return NULL; +#ifdef HAVE_DOSISH_SYSTEM + HANDLE lockhd; /* The W32 handle of the lock file. */ +#else /*!HAVE_DOSISH_SYSTEM */ + char *tname; /* Name of the lockfile template. */ + size_t nodename_off; /* Offset in TNAME of the nodename part. */ + size_t nodename_len; /* Length of the nodename part. */ +#endif /*!HAVE_DOSISH_SYSTEM */ +}; -#else /* HAVE_DOSISH_SYSTEM */ - /* The Windows version does not need a temporary file but uses the - plain lock file along with record locking. We create this file - here so that we later do only need to do the file locking. For - error reporting it is useful to keep the name of the file in the - handle. */ - h->next = all_lockfiles; - all_lockfiles = h; +/* A list of of all lock handles. The volatile attribute might help + if used in an atexit handler. */ +static volatile dotlock_t all_lockfiles; +#ifdef DOTLOCK_USE_PTHREAD +static pthread_mutex_t all_lockfiles_mutex = PTHREAD_MUTEX_INITIALIZER; +# define LOCK_all_lockfiles() do { \ + if (pthread_mutex_lock (&all_lockfiles_mutex)) \ + my_fatal_0 ("locking all_lockfiles_mutex failed\n"); \ + } while (0) +# define UNLOCK_all_lockfiles() do { \ + if (pthread_mutex_unlock (&all_lockfiles_mutex)) \ + my_fatal_0 ("unlocking all_lockfiles_mutex failed\n"); \ + } while (0) +#else /*!DOTLOCK_USE_PTHREAD*/ +# define LOCK_all_lockfiles() do { } while (0) +# define UNLOCK_all_lockfiles() do { } while (0) +#endif /*!DOTLOCK_USE_PTHREAD*/ - h->lockname = xmalloc ( strlen (file_to_lock) + 6 ); - strcpy (stpcpy(h->lockname, file_to_lock), EXTSEP_S "lock"); +/* If this has the value true all locking is disabled. */ +static int never_lock; - /* If would be nice if we would use the FILE_FLAG_DELETE_ON_CLOSE - along with FILE_SHARE_DELETE but that does not work due to a race - condition: Despite the OPEN_ALWAYS flag CreateFile may return an - error and we can't reliable create/open the lock file unless we - would wait here until it works - however there are other valid - reasons why a lock file can't be created and thus the process - would not stop as expected but spin until Windows crashes. Our - solution is to keep the lock file open; that does not harm. */ - h->lockhd = CreateFile (h->lockname, - GENERIC_READ|GENERIC_WRITE, - FILE_SHARE_READ|FILE_SHARE_WRITE, - NULL, OPEN_ALWAYS, 0, NULL); - if (h->lockhd == INVALID_HANDLE_VALUE) - { - log_error (_("can't create `%s': %s\n"), h->lockname, w32_strerror (-1)); - all_lockfiles = h->next; - xfree (h->lockname); - xfree (h); - return NULL; - } - return h; -#endif /* HAVE_DOSISH_SYSTEM */ -} + +/* Entirely disable all locking. This function should be called + before any locking is done. It may be called right at startup of + the process as it only sets a global value. */ void -destroy_dotlock ( DOTLOCK h ) +dotlock_disable (void) { - DOTLOCK hprev, htmp; - - if (!h) - return; - - /* First remove the handle from our global list of all locks. */ - for (hprev=NULL, htmp=all_lockfiles; htmp; hprev=htmp, htmp=htmp->next) - if (htmp == h) - { - if (hprev) - hprev->next = htmp->next; - else - all_lockfiles = htmp->next; - h->next = NULL; - break; - } - - /* Second destroy the lock. */ - if (!h->disable) - { -#ifdef HAVE_DOSISH_SYSTEM - if (h->locked) - UnlockFile (h->lockhd, 0, 0, 1, 0); - CloseHandle (h->lockhd); -#else - if (h->locked && h->lockname) - unlink (h->lockname); - if (h->tname) - unlink (h->tname); - xfree (h->tname); -#endif - xfree (h->lockname); - } - xfree(h); + never_lock = 1; } -#ifndef HAVE_DOSISH_SYSTEM +#ifdef HAVE_POSIX_SYSTEM static int -maybe_deadlock( DOTLOCK h ) +maybe_deadlock (dotlock_t h) { - DOTLOCK r; + dotlock_t r; + int res = 0; - for( r=all_lockfiles; r; r = r->next ) { - if( r != h && r->locked ) - return 1; + LOCK_all_lockfiles (); + for (r=all_lockfiles; r; r = r->next) + { + if ( r != h && r->locked ) + { + res = 1; + break; + } } - return 0; + UNLOCK_all_lockfiles (); + return res; } -#endif /* !HAVE_DOSISH_SYSTEM */ +#endif /*HAVE_POSIX_SYSTEM*/ /* Read the lock file and return the pid, returns -1 on error. True will be stored in the integer at address SAME_NODE if the lock file has been created on the same node. */ -#ifndef HAVE_DOSISH_SYSTEM +#ifdef HAVE_POSIX_SYSTEM static int -read_lockfile (DOTLOCK h, int *same_node ) +read_lockfile (dotlock_t h, int *same_node ) { char buffer_space[10+1+70+1]; /* 70 is just an estimated value; node - names are usually shorter. */ + names are usually shorter. */ int fd; int pid = -1; char *buffer, *p; size_t expected_len; int res, nread; - + *same_node = 0; expected_len = 10 + 1 + h->nodename_len + 1; if ( expected_len >= sizeof buffer_space) - buffer = xmalloc (expected_len); + { + buffer = jnlib_malloc (expected_len); + if (!buffer) + return -1; + } else buffer = buffer_space; if ( (fd = open (h->lockname, O_RDONLY)) == -1 ) { int e = errno; - log_info ("error opening lockfile `%s': %s\n", - h->lockname, strerror(errno) ); + my_info_2 ("error opening lockfile `%s': %s\n", + h->lockname, strerror(errno) ); if (buffer != buffer_space) - xfree (buffer); - errno = e; /* Need to return ERRNO here. */ + jnlib_free (buffer); + jnlib_set_errno (e); /* Need to return ERRNO here. */ return -1; } @@ -351,274 +518,790 @@ read_lockfile (DOTLOCK h, int *same_node ) continue; if (res < 0) { - log_info ("error reading lockfile `%s'", h->lockname ); - close (fd); + my_info_1 ("error reading lockfile `%s'\n", h->lockname ); + close (fd); if (buffer != buffer_space) - xfree (buffer); - errno = 0; /* Do not return an inappropriate ERRNO. */ + jnlib_free (buffer); + jnlib_set_errno (0); /* Do not return an inappropriate ERRNO. */ return -1; } p += res; nread += res; } while (res && nread != expected_len); - close (fd); + close(fd); if (nread < 11) { - log_info ("invalid size of lockfile `%s'", h->lockname ); + my_info_1 ("invalid size of lockfile `%s'\n", h->lockname); if (buffer != buffer_space) - xfree (buffer); - errno = 0; /* Better don't return an inappropriate ERRNO. */ + jnlib_free (buffer); + jnlib_set_errno (0); /* Better don't return an inappropriate ERRNO. */ return -1; } if (buffer[10] != '\n' || (buffer[10] = 0, pid = atoi (buffer)) == -1 -#ifndef __riscos__ - || !pid -#else /* __riscos__ */ - || (!pid && riscos_getpid()) -#endif /* __riscos__ */ - ) - { - log_error ("invalid pid %d in lockfile `%s'", pid, h->lockname ); + || !pid ) + { + my_error_2 ("invalid pid %d in lockfile `%s'\n", pid, h->lockname); if (buffer != buffer_space) - xfree (buffer); - errno = 0; + jnlib_free (buffer); + jnlib_set_errno (0); return -1; } if (nread == expected_len - && !memcmp (h->tname+h->nodename_off, buffer+11, h->nodename_len) + && !memcmp (h->tname+h->nodename_off, buffer+11, h->nodename_len) && buffer[11+h->nodename_len] == '\n') *same_node = 1; if (buffer != buffer_space) - xfree (buffer); + jnlib_free (buffer); return pid; } -#endif /* !HAVE_DOSISH_SYSTEM */ +#endif /*HAVE_POSIX_SYSTEM */ + + +/* Check whether the file system which stores TNAME supports + hardlinks. Instead of using the non-portable statsfs call which + differs between various Unix versions, we do a runtime test. + Returns: 0 supports hardlinks; 1 no hardlink support, -1 unknown + (test error). */ +#ifdef HAVE_POSIX_SYSTEM +static int +use_hardlinks_p (const char *tname) +{ + char *lname; + struct stat sb; + unsigned int nlink; + int res; + + if (stat (tname, &sb)) + return -1; + nlink = (unsigned int)sb.st_nlink; + + lname = jnlib_malloc (strlen (tname) + 1 + 1); + if (!lname) + return -1; + strcpy (lname, tname); + strcat (lname, "x"); + + /* We ignore the return value of link() because it is unreliable. */ + (void) link (tname, lname); + + if (stat (tname, &sb)) + res = -1; /* Ooops. */ + else if (sb.st_nlink == nlink + 1) + res = 0; /* Yeah, hardlinks are supported. */ + else + res = 1; /* No hardlink support. */ + + unlink (lname); + jnlib_free (lname); + return res; +} +#endif /*HAVE_POSIX_SYSTEM */ + + + +#ifdef HAVE_POSIX_SYSTEM +/* Locking core for Unix. It used a temporary file and the link + system call to make locking an atomic operation. */ +static dotlock_t +dotlock_create_unix (dotlock_t h, const char *file_to_lock) +{ + int fd = -1; + char pidstr[16]; + const char *nodename; + const char *dirpart; + int dirpartlen; + struct utsname utsbuf; + size_t tnamelen; + + snprintf (pidstr, sizeof pidstr, "%10d\n", (int)getpid() ); + + /* Create a temporary file. */ + if ( uname ( &utsbuf ) ) + nodename = "unknown"; + else + nodename = utsbuf.nodename; + + if ( !(dirpart = strrchr (file_to_lock, DIRSEP_C)) ) + { + dirpart = EXTSEP_S; + dirpartlen = 1; + } + else + { + dirpartlen = dirpart - file_to_lock; + dirpart = file_to_lock; + } + + LOCK_all_lockfiles (); + h->next = all_lockfiles; + all_lockfiles = h; + + tnamelen = dirpartlen + 6 + 30 + strlen(nodename) + 10 + 1; + h->tname = jnlib_malloc (tnamelen + 1); + if (!h->tname) + { + all_lockfiles = h->next; + UNLOCK_all_lockfiles (); + jnlib_free (h); + return NULL; + } + h->nodename_len = strlen (nodename); + + snprintf (h->tname, tnamelen, "%.*s/.#lk%p.", dirpartlen, dirpart, h ); + h->nodename_off = strlen (h->tname); + snprintf (h->tname+h->nodename_off, tnamelen - h->nodename_off, + "%s.%d", nodename, (int)getpid ()); + + do + { + jnlib_set_errno (0); + fd = open (h->tname, O_WRONLY|O_CREAT|O_EXCL, + S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR ); + } + while (fd == -1 && errno == EINTR); + + if ( fd == -1 ) + { + all_lockfiles = h->next; + UNLOCK_all_lockfiles (); + my_error_2 (_("failed to create temporary file `%s': %s\n"), + h->tname, strerror(errno)); + jnlib_free (h->tname); + jnlib_free (h); + return NULL; + } + if ( write (fd, pidstr, 11 ) != 11 ) + goto write_failed; + if ( write (fd, nodename, strlen (nodename) ) != strlen (nodename) ) + goto write_failed; + if ( write (fd, "\n", 1 ) != 1 ) + goto write_failed; + if ( close (fd) ) + goto write_failed; + + /* Check whether we support hard links. */ + switch (use_hardlinks_p (h->tname)) + { + case 0: /* Yes. */ + break; + case 1: /* No. */ + unlink (h->tname); + h->use_o_excl = 1; + break; + default: + my_error_2 ("can't check whether hardlinks are supported for `%s': %s\n", + h->tname, strerror(errno)); + goto write_failed; + } + + h->lockname = jnlib_malloc (strlen (file_to_lock) + 6 ); + if (!h->lockname) + { + all_lockfiles = h->next; + UNLOCK_all_lockfiles (); + unlink (h->tname); + jnlib_free (h->tname); + jnlib_free (h); + return NULL; + } + strcpy (stpcpy (h->lockname, file_to_lock), EXTSEP_S "lock"); + UNLOCK_all_lockfiles (); + if (h->use_o_excl) + my_debug_1 ("locking for `%s' done via O_EXCL\n", h->lockname); + + return h; + + write_failed: + all_lockfiles = h->next; + UNLOCK_all_lockfiles (); + my_error_2 (_("error writing to `%s': %s\n"), h->tname, strerror (errno)); + close (fd); + unlink (h->tname); + jnlib_free (h->tname); + jnlib_free (h); + return NULL; +} +#endif /*HAVE_POSIX_SYSTEM*/ + + +#ifdef HAVE_DOSISH_SYSTEM +/* Locking core for Windows. This version does not need a temporary + file but uses the plain lock file along with record locking. We + create this file here so that we later only need to do the file + locking. For error reporting it is useful to keep the name of the + file in the handle. */ +static dotlock_t +dotlock_create_w32 (dotlock_t h, const char *file_to_lock) +{ + LOCK_all_lockfiles (); + h->next = all_lockfiles; + all_lockfiles = h; + + h->lockname = jnlib_malloc ( strlen (file_to_lock) + 6 ); + if (!h->lockname) + { + all_lockfiles = h->next; + UNLOCK_all_lockfiles (); + jnlib_free (h); + return NULL; + } + strcpy (stpcpy(h->lockname, file_to_lock), EXTSEP_S "lock"); + + /* If would be nice if we would use the FILE_FLAG_DELETE_ON_CLOSE + along with FILE_SHARE_DELETE but that does not work due to a race + condition: Despite the OPEN_ALWAYS flag CreateFile may return an + error and we can't reliable create/open the lock file unless we + would wait here until it works - however there are other valid + reasons why a lock file can't be created and thus the process + would not stop as expected but spin until Windows crashes. Our + solution is to keep the lock file open; that does not harm. */ + { +#ifdef HAVE_W32CE_SYSTEM + wchar_t *wname = utf8_to_wchar (h->lockname); + + if (wname) + h->lockhd = CreateFile (wname, + GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, OPEN_ALWAYS, 0, NULL); + else + h->lockhd = INVALID_HANDLE_VALUE; + jnlib_free (wname); +#else + h->lockhd = CreateFile (h->lockname, + GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, OPEN_ALWAYS, 0, NULL); +#endif + } + if (h->lockhd == INVALID_HANDLE_VALUE) + { + all_lockfiles = h->next; + UNLOCK_all_lockfiles (); + my_error_2 (_("can't create `%s': %s\n"), h->lockname, w32_strerror (-1)); + jnlib_free (h->lockname); + jnlib_free (h); + return NULL; + } + return h; +} +#endif /*HAVE_DOSISH_SYSTEM*/ + + +/* Create a lockfile for a file name FILE_TO_LOCK and returns an + object of type dotlock_t which may be used later to actually acquire + the lock. A cleanup routine gets installed to cleanup left over + locks or other files used internally by the lock mechanism. + + Calling this function with NULL does only install the atexit + handler and may thus be used to assure that the cleanup is called + after all other atexit handlers. + This function creates a lock file in the same directory as + FILE_TO_LOCK using that name and a suffix of ".lock". Note that on + POSIX systems a temporary file ".#lk.<hostname>.pid[.threadid] is + used. -/**************** - * Do a lock on H. A TIMEOUT of 0 returns immediately, - * -1 waits forever (hopefully not), other - * values are timeouts in milliseconds. - * Returns: 0 on success + FLAGS must be 0. + + The function returns an new handle which needs to be released using + destroy_dotlock but gets also released at the termination of the + process. On error NULL is returned. */ + +dotlock_t +dotlock_create (const char *file_to_lock, unsigned int flags) +{ + static int initialized; + dotlock_t h; + + if ( !initialized ) + { + atexit (dotlock_remove_lockfiles); + initialized = 1; + } + + if ( !file_to_lock ) + return NULL; /* Only initialization was requested. */ + + if (flags) + { + jnlib_set_errno (EINVAL); + return NULL; + } + + h = jnlib_calloc (1, sizeof *h); + if (!h) + return NULL; + h->extra_fd = -1; + + if (never_lock) + { + h->disable = 1; + LOCK_all_lockfiles (); + h->next = all_lockfiles; + all_lockfiles = h; + UNLOCK_all_lockfiles (); + return h; + } + +#ifdef HAVE_DOSISH_SYSTEM + return dotlock_create_w32 (h, file_to_lock); +#else /*!HAVE_DOSISH_SYSTEM */ + return dotlock_create_unix (h, file_to_lock); +#endif /*!HAVE_DOSISH_SYSTEM*/ +} + + + +/* Convenience function to store a file descriptor (or any any other + integer value) in the context of handle H. */ +void +dotlock_set_fd (dotlock_t h, int fd) +{ + h->extra_fd = fd; +} + +/* Convenience function to retrieve a file descriptor (or any any other + integer value) stored in the context of handle H. */ int -make_dotlock( DOTLOCK h, long timeout ) +dotlock_get_fd (dotlock_t h) { - int backoff=0; -#ifndef HAVE_DOSISH_SYSTEM - int pid; - const char *maybe_dead=""; - int same_node; -#endif - - if (h->disable) - return 0; + return h->extra_fd; +} + + + +#ifdef HAVE_POSIX_SYSTEM +/* Unix specific code of destroy_dotlock. */ +static void +dotlock_destroy_unix (dotlock_t h) +{ + if (h->locked && h->lockname) + unlink (h->lockname); + if (h->tname && !h->use_o_excl) + unlink (h->tname); + jnlib_free (h->tname); +} +#endif /*HAVE_POSIX_SYSTEM*/ + +#ifdef HAVE_DOSISH_SYSTEM +/* Windows specific code of destroy_dotlock. */ +static void +dotlock_destroy_w32 (dotlock_t h) +{ if (h->locked) { -#ifndef __riscos__ - log_debug ("oops, `%s' is already locked\n", h->lockname); -#endif /* !__riscos__ */ - return 0; + OVERLAPPED ovl; + + memset (&ovl, 0, sizeof ovl); + UnlockFileEx (h->lockhd, 0, 1, 0, &ovl); } - - for (;;) + CloseHandle (h->lockhd); +} +#endif /*HAVE_DOSISH_SYSTEM*/ + + +/* Destroy the locck handle H and release the lock. */ +void +dotlock_destroy (dotlock_t h) +{ + dotlock_t hprev, htmp; + + if ( !h ) + return; + + /* First remove the handle from our global list of all locks. */ + LOCK_all_lockfiles (); + for (hprev=NULL, htmp=all_lockfiles; htmp; hprev=htmp, htmp=htmp->next) + if (htmp == h) + { + if (hprev) + hprev->next = htmp->next; + else + all_lockfiles = htmp->next; + h->next = NULL; + break; + } + UNLOCK_all_lockfiles (); + + /* Then destroy the lock. */ + if (!h->disable) { -#ifndef HAVE_DOSISH_SYSTEM -# ifndef __riscos__ - if( !link(h->tname, h->lockname) ) +#ifdef HAVE_DOSISH_SYSTEM + dotlock_destroy_w32 (h); +#else /* !HAVE_DOSISH_SYSTEM */ + dotlock_destroy_unix (h); +#endif /* HAVE_DOSISH_SYSTEM */ + jnlib_free (h->lockname); + } + jnlib_free(h); +} + + + +#ifdef HAVE_POSIX_SYSTEM +/* Unix specific code of make_dotlock. Returns 0 on success and -1 on + error. */ +static int +dotlock_take_unix (dotlock_t h, long timeout) +{ + int wtime = 0; + int sumtime = 0; + int pid; + int lastpid = -1; + int ownerchanged; + const char *maybe_dead=""; + int same_node; + + again: + if (h->use_o_excl) + { + /* No hardlink support - use open(O_EXCL). */ + int fd; + + do { - /* fixme: better use stat to check the link count */ - h->locked = 1; - return 0; /* okay */ - } - if (errno != EEXIST) + jnlib_set_errno (0); + fd = open (h->lockname, O_WRONLY|O_CREAT|O_EXCL, + S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR ); + } + while (fd == -1 && errno == EINTR); + + if (fd == -1 && errno == EEXIST) + ; /* Lock held by another process. */ + else if (fd == -1) { - log_error ("lock not made: link() failed: %s\n", strerror(errno)); + my_error_2 ("lock not made: open(O_EXCL) of `%s' failed: %s\n", + h->lockname, strerror (errno)); return -1; - } -# else /* __riscos__ */ - if ( !riscos_renamefile(h->tname, h->lockname) ) - { - h->locked = 1; - return 0; /* okay */ } - if (errno != EEXIST) + else { - log_error ("lock not made: rename() failed: %s\n", strerror(errno)); + char pidstr[16]; + + snprintf (pidstr, sizeof pidstr, "%10d\n", (int)getpid()); + if (write (fd, pidstr, 11 ) == 11 + && write (fd, h->tname + h->nodename_off,h->nodename_len) + == h->nodename_len + && write (fd, "\n", 1) == 1 + && !close (fd)) + { + h->locked = 1; + return 0; + } + /* Write error. */ + my_error_2 ("lock not made: writing to `%s' failed: %s\n", + h->lockname, strerror (errno)); + close (fd); + unlink (h->lockname); return -1; } -# endif /* __riscos__ */ + } + else /* Standard method: Use hardlinks. */ + { + struct stat sb; - if ((pid = read_lockfile (h, &same_node)) == -1 ) + /* We ignore the return value of link() because it is unreliable. */ + (void) link (h->tname, h->lockname); + + if (stat (h->tname, &sb)) { - if (errno != ENOENT) - { - log_info ("cannot read lockfile\n"); - return -1; - } - log_info ("lockfile disappeared\n"); - continue; - } - else if (pid == getpid ()) + my_error_1 ("lock not made: Oops: stat of tmp file failed: %s\n", + strerror (errno)); + /* In theory this might be a severe error: It is possible + that link succeeded but stat failed due to changed + permissions. We can't do anything about it, though. */ + return -1; + } + + if (sb.st_nlink == 2) { - log_info ("Oops: lock already held by us\n"); h->locked = 1; - return 0; /* okay */ - } - else if (same_node && kill(pid, 0) && errno == ESRCH) - { -# ifndef __riscos__ - log_info (_("removing stale lockfile (created by %d)\n"), pid ); - unlink (h->lockname); - continue; -# else /* __riscos__ */ - /* We are *pretty* sure that the other task is dead and - therefore we remove the other lock file. */ - maybe_dead = " - probably dead - removing lock"; - unlink (h->lockname); -# endif /* __riscos__ */ - } + return 0; /* Okay. */ + } + } - if (timeout == -1) + /* Check for stale lock files. */ + if ( (pid = read_lockfile (h, &same_node)) == -1 ) + { + if ( errno != ENOENT ) { - struct timeval tv; - - log_info ("waiting for lock (held by %d%s) %s...\n", - pid, maybe_dead, maybe_deadlock(h)? "(deadlock?) ":""); - + my_info_0 ("cannot read lockfile\n"); + return -1; + } + my_info_0 ("lockfile disappeared\n"); + goto again; + } + else if ( pid == getpid() && same_node ) + { + my_info_0 ("Oops: lock already held by us\n"); + h->locked = 1; + return 0; /* okay */ + } + else if ( same_node && kill (pid, 0) && errno == ESRCH ) + { + /* Note: It is unlikley that we get a race here unless a pid is + reused too fast or a new process with the same pid as the one + of the stale file tries to lock right at the same time as we. */ + my_info_1 (_("removing stale lockfile (created by %d)\n"), pid); + unlink (h->lockname); + goto again; + } - /* We can't use sleep, because signals may be blocked. */ - tv.tv_sec = 1 + backoff; - tv.tv_usec = 0; - select (0, NULL, NULL, NULL, &tv); - if (backoff < 10) - backoff++ ; - } - else - return -1; + if (lastpid == -1) + lastpid = pid; + ownerchanged = (pid != lastpid); -#else /*HAVE_DOSISH_SYSTEM*/ - int w32err; - - if (LockFile (h->lockhd, 0, 0, 1, 0)) + if (timeout) + { + struct timeval tv; + + /* Wait until lock has been released. We use increasing retry + intervals of 50ms, 100ms, 200ms, 400ms, 800ms, 2s, 4s and 8s + but reset it if the lock owner meanwhile changed. */ + if (!wtime || ownerchanged) + wtime = 50; + else if (wtime < 800) + wtime *= 2; + else if (wtime == 800) + wtime = 2000; + else if (wtime < 8000) + wtime *= 2; + + if (timeout > 0) { - h->locked = 1; - return 0; /* okay */ + if (wtime > timeout) + wtime = timeout; + timeout -= wtime; } - w32err = GetLastError (); - if (w32err != ERROR_LOCK_VIOLATION) + + sumtime += wtime; + if (sumtime >= 1500) { - log_error (_("lock `%s' not made: %s\n"), - h->lockname, w32_strerror (w32err)); - return -1; + sumtime = 0; + my_info_3 (_("waiting for lock (held by %d%s) %s...\n"), + pid, maybe_dead, maybe_deadlock(h)? _("(deadlock?) "):""); } - - if (timeout == -1) + + + tv.tv_sec = wtime / 1000; + tv.tv_usec = (wtime % 1000) * 1000; + select (0, NULL, NULL, NULL, &tv); + goto again; + } + + jnlib_set_errno (EACCES); + return -1; +} +#endif /*HAVE_POSIX_SYSTEM*/ + + +#ifdef HAVE_DOSISH_SYSTEM +/* Windows specific code of make_dotlock. Returns 0 on success and -1 on + error. */ +static int +dotlock_take_w32 (dotlock_t h, long timeout) +{ + int wtime = 0; + int w32err; + OVERLAPPED ovl; + + again: + /* Lock one byte at offset 0. The offset is given by OVL. */ + memset (&ovl, 0, sizeof ovl); + if (LockFileEx (h->lockhd, (LOCKFILE_EXCLUSIVE_LOCK + | LOCKFILE_FAIL_IMMEDIATELY), 0, 1, 0, &ovl)) + { + h->locked = 1; + return 0; /* okay */ + } + + w32err = GetLastError (); + if (w32err != ERROR_LOCK_VIOLATION) + { + my_error_2 (_("lock `%s' not made: %s\n"), + h->lockname, w32_strerror (w32err)); + return -1; + } + + if (timeout) + { + /* Wait until lock has been released. We use retry intervals of + 50ms, 100ms, 200ms, 400ms, 800ms, 2s, 4s and 8s. */ + if (!wtime) + wtime = 50; + else if (wtime < 800) + wtime *= 2; + else if (wtime == 800) + wtime = 2000; + else if (wtime < 8000) + wtime *= 2; + + if (timeout > 0) { - /* Wait until lock has been released. */ - log_info (_("waiting for lock %s...\n"), h->lockname); - Sleep ((1 + backoff)*1000); - if ( backoff < 10 ) - backoff++ ; - } - else - return -1; -#endif /*HAVE_DOSISH_SYSTEM*/ + if (wtime > timeout) + wtime = timeout; + timeout -= wtime; + } + + if (wtime >= 800) + my_info_1 (_("waiting for lock %s...\n"), h->lockname); + + Sleep (wtime); + goto again; } - /*NOTREACHED */ + + return -1; } +#endif /*HAVE_DOSISH_SYSTEM*/ -/**************** - * release a lock - * Returns: 0 := success - */ +/* Take a lock on H. A value of 0 for TIMEOUT returns immediately if + the lock can't be taked, -1 waits forever (hopefully not), other + values wait for TIMEOUT milliseconds. Returns: 0 on success */ int -release_dotlock (DOTLOCK h) +dotlock_take (dotlock_t h, long timeout) { -#ifndef HAVE_DOSISH_SYSTEM - int pid, same_node; -#endif - - /* To avoid atexit race conditions we first check whether there are - any locks left. It might happen that another atexit handler - tries to release the lock while the atexit handler of this module - already ran and thus H is undefined. */ - if (!all_lockfiles) - return 0; + int ret; - if (h->disable) - return 0; + if ( h->disable ) + return 0; /* Locks are completely disabled. Return success. */ - if (!h->locked) + if ( h->locked ) { - log_debug("oops, `%s' is not locked\n", h->lockname ); + my_debug_1 ("Oops, `%s' is already locked\n", h->lockname); return 0; } -#ifndef HAVE_DOSISH_SYSTEM +#ifdef HAVE_DOSISH_SYSTEM + ret = dotlock_take_w32 (h, timeout); +#else /*!HAVE_DOSISH_SYSTEM*/ + ret = dotlock_take_unix (h, timeout); +#endif /*!HAVE_DOSISH_SYSTEM*/ + + return ret; +} + + + +#ifdef HAVE_POSIX_SYSTEM +/* Unix specific code of release_dotlock. */ +static int +dotlock_release_unix (dotlock_t h) +{ + int pid, same_node; + pid = read_lockfile (h, &same_node); - if (pid == -1) + if ( pid == -1 ) { - log_error ("release_dotlock: lockfile error\n"); + my_error_0 ("release_dotlock: lockfile error\n"); return -1; } - if (pid != getpid () || !same_node) + if ( pid != getpid() || !same_node ) { - log_error ("release_dotlock: not our lock (pid=%d)\n", pid); + my_error_1 ("release_dotlock: not our lock (pid=%d)\n", pid); return -1; } -# ifndef __riscos__ - if (unlink (h->lockname)) + + if ( unlink( h->lockname ) ) { - log_error ("release_dotlock: error removing lockfile `%s'", - h->lockname); + my_error_1 ("release_dotlock: error removing lockfile `%s'\n", + h->lockname); return -1; } -# else /* __riscos__ */ - if( riscos_renamefile(h->lockname, h->tname) ) + /* Fixme: As an extra check we could check whether the link count is + now really at 1. */ + return 0; +} +#endif /*HAVE_POSIX_SYSTEM */ + + +#ifdef HAVE_DOSISH_SYSTEM +/* Windows specific code of release_dotlock. */ +static int +dotlock_release_w32 (dotlock_t h) +{ + OVERLAPPED ovl; + + memset (&ovl, 0, sizeof ovl); + if (!UnlockFileEx (h->lockhd, 0, 1, 0, &ovl)) { - log_error( "release_dotlock: error renaming lockfile `%s' to `%s'", - h->lockname, h->tname); + my_error_2 ("release_dotlock: error removing lockfile `%s': %s\n", + h->lockname, w32_strerror (-1)); return -1; } -# endif /* __riscos__ */ -#else /*HAVE_DOSISH_SYSTEM*/ + return 0; +} +#endif /*HAVE_DOSISH_SYSTEM */ + + +/* Release a lock. Returns 0 on success. */ +int +dotlock_release (dotlock_t h) +{ + int ret; - if (!UnlockFile (h->lockhd, 0, 0, 1, 0)) + /* To avoid atexit race conditions we first check whether there are + any locks left. It might happen that another atexit handler + tries to release the lock while the atexit handler of this module + already ran and thus H is undefined. */ + LOCK_all_lockfiles (); + ret = !all_lockfiles; + UNLOCK_all_lockfiles (); + if (ret) + return 0; + + if ( h->disable ) + return 0; + + if ( !h->locked ) { - log_error ("release_dotlock: error removing lockfile `%s': %s\n", - h->lockname, w32_strerror (-1)); - return -1; + my_debug_1 ("Oops, `%s' is not locked\n", h->lockname); + return 0; } -#endif /*HAVE_DOSISH_SYSTEM*/ - h->locked = 0; - return 0; +#ifdef HAVE_DOSISH_SYSTEM + ret = dotlock_release_w32 (h); +#else + ret = dotlock_release_unix (h); +#endif + + if (!ret) + h->locked = 0; + return ret; } + +/* Remove all lockfiles. This is called by the atexit handler + installed by this module but may also be called by other + termination handlers. */ void -remove_lockfiles() +dotlock_remove_lockfiles (void) { -#if !defined (HAVE_DOSISH_SYSTEM) - DOTLOCK h, h2; + dotlock_t h, h2; - h = all_lockfiles; - all_lockfiles = NULL; + /* First set the lockfiles list to NULL so that for example + dotlock_release is ware that this fucntion is currently + running. */ + LOCK_all_lockfiles (); + h = all_lockfiles; + all_lockfiles = NULL; + UNLOCK_all_lockfiles (); - while( h ) { - h2 = h->next; - destroy_dotlock (h); - h = h2; + while ( h ) + { + h2 = h->next; + dotlock_destroy (h); + h = h2; } -#endif } |