From df1326eb0565640130b939526b6919c7d8dc89a9 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 27 Nov 1998 20:40:56 +0000 Subject: [PATCH] See ChangeLog: Fri Nov 27 21:37:41 CET 1998 Werner Koch --- NEWS | 6 +- doc/gpg.1pod | 38 ++++++++----- g10/ChangeLog | 12 ++++ g10/g10.c | 3 + g10/options.h | 1 + g10/options.skel | 6 ++ g10/ringedit.c | 43 ++++++++++++++- g10/tdbio.c | 38 ++++++++++++- include/util.h | 7 +++ util/ChangeLog | 4 ++ util/dotlock.c | 139 ++++++++++++++++++++++++++++++++++------------- 11 files changed, 238 insertions(+), 59 deletions(-) diff --git a/NEWS b/NEWS index 6e012423e..131e63bfb 100644 --- a/NEWS +++ b/NEWS @@ -1,7 +1,7 @@ - * - - + * The keyrings and the trustdb is now locked, so that + other GnuPG processes won't damage these files. You + may want to put the option --lock-once into your options file. Noteworthy changes in version 0.4.4 diff --git a/doc/gpg.1pod b/doc/gpg.1pod index ebee20db7..998030891 100644 --- a/doc/gpg.1pod +++ b/doc/gpg.1pod @@ -75,7 +75,7 @@ B<-k> [I] [I] B<-kvc> List fingerprints B<-kvvc> List fingerprints and signatures -B<--list-keys> [I] +B<--list-keys> [I] List all keys from the public keyrings, or just the ones given on the command line. @@ -83,7 +83,7 @@ B<--list-secret-keys> [I] List all keys from the secret keyrings, or just the ones given on the command line. -B<--list-sigs> [I] +B<--list-sigs> [I] Same as B<--list-keys>, but the signatures are listed too. @@ -129,7 +129,7 @@ B<--edit-key> I B Remove a subkey. B - Change the key expiration time. If a key is + Change the key expiration time. If a key is select, the time of this key will be changed. With no selection the key expiration of the primary key is changed. @@ -190,7 +190,7 @@ B<--export-secret-keys> [I This is normally not very useful. B<--import>, B<--fast-import> - Import/merge keys. The fast version does not build + Import/merge keys. The fast version does not build the trustdb; this can be deon at anytime with the command B<--update-trustdb>. @@ -207,7 +207,7 @@ B<--import-ownertrust> [I] Long options can be put in an options file (default F<~/.gnupg/options>); do not write the 2 dashes, but simply the name of the option and any -arguments if required. Lines with a hash as the first non-white-space +arguments if required. Lines with a hash as the first non-white-space character are ignored. Commands may be put in this file too, but that does not make sense. @@ -426,6 +426,11 @@ B<--force-v3-sigs> signatures on key material. This options forces v3 signatures for signatures on data. +B<--lock-once> + Lock the file the first time a lock is requested + and do not release the lock until the process + terminates. + B<--no-verbose> Reset verbose level to 0. @@ -466,11 +471,11 @@ a signature was bad and other errorcode for fatal errors. =head1 EXAMPLES - -se -r Bob [file] sign and encrypt for user Bob - -sat [file] make a clear text signature - -sb [file] make a detached signature - -k [userid] show keys - -kc [userid] show fingerprint + -se -r Bob [file] sign and encrypt for user Bob + -sat [file] make a clear text signature + -sb [file] make a detached signature + -k [userid] show keys + -kc [userid] show fingerprint =head1 ENVIRONMENT @@ -479,19 +484,22 @@ C If set, direcory used instead of F<~/.gnupg>. =head1 FILES -F<~/.gnupg/secring.gpg> The secret keyring +F<~/.gnupg/secring.gpg> The secret keyring +F<~/.gnupg/secring.gpg.lock> and the lock file -F<~/.gnupg/pubring.gpg> The public keyring +F<~/.gnupg/pubring.gpg> The public keyring +F<~/.gnupg/pubring.gpg.lock> and the lock file -F<~/.gnupg/trustdb.gpg> The trust database +F<~/.gnupg/trustdb.gpg> The trust database +F<~/.gnupg/trustdb.gpg.lock> and the lock file -F<~/.gnupg/options> May contain options +F<~/.gnupg/options> May contain options F Default location for extensions =head1 SEE ALSO -gpg(1) gpgm(1) +gpg(1) gpgm(1) =head1 WARNINGS diff --git a/g10/ChangeLog b/g10/ChangeLog index 20c5abdb9..455451cbd 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,15 @@ +Fri Nov 27 21:37:41 CET 1998 Werner Koch + + * g10.c: New option --lock-once + * tdbio.c (open_db): Add an atexit + (cleanup): New. + (tdbio_sync): Add locking. + (tdbio_end_transaction): Ditto. + (put_record_into_cache): Ditto. + * ringedit.c (keyring_copy): Ditto. + (cleanup): New. + (add_keyblock_resource): Add an atexit. + Fri Nov 27 15:30:24 CET 1998 Werner Koch * armor.c (find_header): Another fix for clearsigs. diff --git a/g10/g10.c b/g10/g10.c index a0ba4e1ef..815d61041 100644 --- a/g10/g10.c +++ b/g10/g10.c @@ -147,6 +147,7 @@ enum cmd_and_opt_values { aNull = 0, oS2KCipher, oCharset, oNotDashEscaped, + oLockOnce, aTest }; @@ -295,6 +296,7 @@ static ARGPARSE_OPTS opts[] = { { oSetFilename, "set-filename", 2, "@" }, { oComment, "comment", 2, "@" }, { oNotDashEscaped, "not-dash-escaped", 0, "@" }, + { oLockOnce, "lock-once", 0, "@" }, {0} }; @@ -768,6 +770,7 @@ main( int argc, char **argv ) pargs.r.ret_str); break; case oNotDashEscaped: opt.not_dash_escaped = 1; break; + case oLockOnce: opt.lock_once = 1; break; default : pargs.err = configfp? 1:2; break; } diff --git a/g10/options.h b/g10/options.h index 538b175d7..a8164f89d 100644 --- a/g10/options.h +++ b/g10/options.h @@ -65,6 +65,7 @@ struct { int s2k_digest_algo; int s2k_cipher_algo; int not_dash_escaped; + int lock_once; } opt; diff --git a/g10/options.skel b/g10/options.skel index 3ce6e5aa8..9fe89b1fb 100644 --- a/g10/options.skel +++ b/g10/options.skel @@ -46,3 +46,9 @@ compress-algo 1 # everytime you use --mynames, it will be expanded to the options # in the above defintion. The name of the alias may not be abbreviated. +# lock tthe file only once for the lifetime of a process. +# if you do not define this, the lock will be obtained and released +# every time it is needed - normally this is not needed. +lock-once + + diff --git a/g10/ringedit.c b/g10/ringedit.c index d12afa6dc..e7ffdbfa8 100644 --- a/g10/ringedit.c +++ b/g10/ringedit.c @@ -77,6 +77,7 @@ typedef struct resource_table_struct RESTBL; #define MAX_RESOURCES 10 static RESTBL resource_table[MAX_RESOURCES]; +static const char *keyring_lock; static int search( PACKET *pkt, KBPOS *kbpos, int secret ); @@ -116,6 +117,15 @@ fatal_gdbm_error( const char *string ) #endif /* HAVE_LIBGDBM */ +static void +cleanup( void ) +{ + if( keyring_lock ) { + release_dotlock( keyring_lock ); + keyring_lock = NULL; + } +} + /**************************************************************** ****************** public functions **************************** ****************************************************************/ @@ -152,6 +162,7 @@ enum_keyblock_resources( int *sequence, int secret ) int add_keyblock_resource( const char *url, int force, int secret ) { + static int initialized = 0; static int any_secret, any_public; const char *resname = url; IOBUF iobuf = NULL; @@ -160,6 +171,11 @@ add_keyblock_resource( const char *url, int force, int secret ) int rc = 0; enum resource_type rt = rt_UNKNOWN; + if( !initialized ) { + initialized = 1; + atexit( cleanup ); + } + /* Do we have an URL? * gnupg-gdbm:filename := this is a GDBM resource * gnupg-ring:filename := this is a plain keyring @@ -843,7 +859,7 @@ update_keyblock( KBPOS *kbpos, KBNODE root ) * * A string "GnuPG user db", a \n. * user ids of one key, delimited by \t, - * a # or ^ followed by a 20 byte fingerprint, followed by an \n + * a # or ^ followed by a 20 byte fingerprint, followed by an \n * The literal characters =, \n, \t, #, ^ must be replaced by a equal sign * and their hex value. * @@ -1224,6 +1240,11 @@ keyring_copy( KBPOS *kbpos, int mode, KBNODE root ) if( kbpos->fp ) BUG(); /* not allowed with such a handle */ + if( !keyring_lock ); + keyring_lock = make_dotlock( rentry->fname, -1 ); + if( !keyring_lock ) + log_fatal("can't lock '%s'\n", rentry->fname ); + /* open the source file */ fp = iobuf_fopen( rentry->fname, "rb" ); if( mode == 1 && !fp && errno == ENOENT ) { /* no file yet */ @@ -1233,6 +1254,10 @@ keyring_copy( KBPOS *kbpos, int mode, KBNODE root ) newfp = iobuf_create( rentry->fname ); if( !newfp ) { log_error(_("%s: can't create: %s\n"), rentry->fname, strerror(errno)); + if( !opt.lock_once ) { + release_dotlock( keyring_lock ); + keyring_lock = NULL; + } return G10ERR_OPEN_FILE; } else @@ -1244,16 +1269,28 @@ keyring_copy( KBPOS *kbpos, int mode, KBNODE root ) log_error("build_packet(%d) failed: %s\n", node->pkt->pkttype, g10_errstr(rc) ); iobuf_cancel(newfp); + if( !opt.lock_once ) { + release_dotlock( keyring_lock ); + keyring_lock = NULL; + } return G10ERR_WRITE_FILE; } } if( iobuf_close(newfp) ) { log_error("%s: close failed: %s\n", rentry->fname, strerror(errno)); + if( !opt.lock_once ) { + release_dotlock( keyring_lock ); + keyring_lock = NULL; + } return G10ERR_CLOSE_FILE; } if( chmod( rentry->fname, S_IRUSR | S_IWUSR ) ) { log_error("%s: chmod failed: %s\n", rentry->fname, strerror(errno) ); + if( !opt.lock_once ) { + release_dotlock( keyring_lock ); + keyring_lock = NULL; + } return G10ERR_WRITE_FILE; } return 0; @@ -1418,6 +1455,10 @@ keyring_copy( KBPOS *kbpos, int mode, KBNODE root ) } leave: + if( !opt.lock_once ) { + release_dotlock( keyring_lock ); + keyring_lock = NULL; + } m_free(bakfname); m_free(tmpfname); return rc; diff --git a/g10/tdbio.c b/g10/tdbio.c index bac8a3364..5d1864c3d 100644 --- a/g10/tdbio.c +++ b/g10/tdbio.c @@ -77,6 +77,7 @@ struct cmp_sdir_struct { static char *db_name; +static const char *lockname; static int db_fd = -1; static int in_transaction; @@ -235,6 +236,10 @@ put_record_into_cache( ulong recno, const char *data ) int n = dirty_count / 5; /* discard some dirty entries */ if( !n ) n = 1; + if( !lockname ) + lockname = make_dotlock( db_name, -1 ); + if( !lockname ) + log_fatal("can't get a lock - giving up\n"); for( unused = NULL, r = cache_list; r; r = r->next ) { if( r->flags.used && r->flags.dirty ) { int rc = write_cache_item( r ); @@ -248,6 +253,10 @@ put_record_into_cache( ulong recno, const char *data ) break; } } + if( !opt.lock_once ) { + release_dotlock( lockname ); + lockname=NULL; + } assert( unused ); r = unused; r->flags.used = 1; @@ -276,6 +285,7 @@ int tdbio_sync() { CACHE_CTRL r; + int did_lock = 0; if( in_transaction ) log_bug("tdbio: syncing while in transaction\n"); @@ -283,6 +293,12 @@ tdbio_sync() if( !cache_is_dirty ) return 0; + if( !lockname ) { + lockname = make_dotlock( db_name, -1 ); + did_lock = 1; + if( !lockname ) + log_fatal("can't get a lock - giving up\n"); + } for( r = cache_list; r; r = r->next ) { if( r->flags.used && r->flags.dirty ) { int rc = write_cache_item( r ); @@ -291,6 +307,10 @@ tdbio_sync() } } cache_is_dirty = 0; + if( did_lock && !opt.lock_once ) { + release_dotlock( lockname ); + lockname=NULL; + } return 0; } @@ -324,10 +344,18 @@ tdbio_end_transaction() if( !in_transaction ) log_bug("tdbio: no active transaction\n"); + if( !lockname ) + lockname = make_dotlock( db_name, -1 ); + if( !lockname ) + log_fatal("can't get a lock - giving up\n"); block_all_signals(); in_transaction = 0; rc = tdbio_sync(); unblock_all_signals(); + if( !opt.lock_once ) { + release_dotlock( lockname ); + lockname=NULL; + } return rc; } @@ -452,6 +480,14 @@ tdbio_get_dbname() } +static void +cleanup(void) +{ + if( lockname ) { + release_dotlock(lockname); + lockname = NULL; + } +} static void open_db() @@ -468,7 +504,7 @@ open_db() log_fatal( _("%s: can't open: %s\n"), db_name, strerror(errno) ); if( tdbio_read_record( 0, &rec, RECTYPE_VER ) ) log_fatal( _("%s: invalid trust-db\n"), db_name ); - /* fixme: check ->locked and other stuff */ + atexit( cleanup ); } diff --git a/include/util.h b/include/util.h index fdea00350..892d508cc 100644 --- a/include/util.h +++ b/include/util.h @@ -126,6 +126,13 @@ const char *default_strusage( int level ); const char *strusage( int level ); +/*-- dotlock.c --*/ +const char *make_dotlock( const char *file_to_lock, long timeout ); +int release_dotlock( const char *lockfile ); + + + + /*-- fileutil.c --*/ char * make_basename(const char *filepath); char * make_dirname(const char *filepath); diff --git a/util/ChangeLog b/util/ChangeLog index e047de824..dc24d3851 100644 --- a/util/ChangeLog +++ b/util/ChangeLog @@ -1,3 +1,7 @@ +Fri Nov 27 21:37:41 CET 1998 Werner Koch + + * dotlock.c: Implemented + Wed Nov 25 11:30:07 1998 Werner Koch (wk@isil.d.shuttle.de) * iobuf.c (iobuf_pop_filter): Fixed sigsegv after error. diff --git a/util/dotlock.c b/util/dotlock.c index 917b90dd5..b57583004 100644 --- a/util/dotlock.c +++ b/util/dotlock.c @@ -23,14 +23,20 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include #include "types.h" #include "util.h" #include "memory.h" +static int read_lockfile( const char *name ); - -#if 0 /**************** * Create a lockfile with the given name. A TIMEOUT of 0 * returns immediately, -1 waits forever (hopefully not), other @@ -40,67 +46,122 @@ * * 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.. is used. + * A temporary file ".#lk..pid[.threadid] is used. * This function does nothing for Windoze. */ const char * make_dotlock( const char *file_to_lock, long timeout ) { - int rc=-1, fd=-1, pid; + int fd=-1, pid; char pidstr[16]; + const char *handle = NULL; + char *lockname = NULL; char *tname = NULL; - char *p; + int have_tfile = 0; + struct utsname uts; + const char *dirpart; + int dirpartlen; - log_debug("dotlock_make: lock='%s'\n", lockfile ); sprintf( pidstr, "%10d\n", getpid() ); - /* add the hostname to the second line (FQDN or IP addr?) */ + /* fixme: add the hostname to the second line (FQDN or IP addr?) */ /* create a temporary file */ - tname = CreateTmpFile2( p, ".#lk" ); - free(p); - if( !tname ) - log_fatal( "could not create temporary lock file '%s'\n"); - log_debug( "dotlock_make: tmpname='%s'\n", tname ); - chmod( tname, 0644 ); /* just in case an umask is set */ - if( !(fd = open( tname, O_WRONLY )) ) - log_fatal( "could not open temporary lock file '%s'\n", tname); - if( write(fd, pidstr, 11 ) != 11 ) - log_fatal( "error writing to temporary lock file '%s'\n", tname); + #if SYS_NMLN < 8 + #error Aiiih + #endif + if( uname( &uts ) ) + strcpy( uts.nodename, "unknown" ); + + if( !(dirpart = strrchr( file_to_lock, '/' )) ) { + dirpart = "."; + dirpartlen = 1; + } + else { + dirpartlen = dirpart - file_to_lock; + dirpart = file_to_lock; + } + + #ifdef _THREAD_SAFE + tname = m_alloc( dirpartlen + 6 + strlen(uts.nodename) + 11+ 20 ); + sprintf( tname, "%.*s/.#lk.%s.%d.%p", + dirpartlen, dirpart, uts.nodename, getpid(), &pid ); + #else + tname = m_alloc( dirpartlen + 6 + strlen(uts.nodename) + 11 ); + sprintf( tname, "%.*s/.#lk.%s.%d", + dirpartlen, dirpart, uts.nodename, getpid() ); + #endif + do { + errno = 0; + fd = open( tname, O_WRONLY|O_CREAT|O_EXCL, + S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR ); + } while( fd == -1 && errno == EINTR ); + if( fd == -1 ) { + log_error( "failed to create temporary file '%s': %s\n", + tname, strerror(errno)); + goto leave; + } + have_tfile = 1; + if( write(fd, pidstr, 11 ) != 11 ) { + log_fatal( "error writing to '%s': %s\n", tname, strerror(errno) ); + goto leave; + } if( close(fd) ) { - log_fatal( "error closing '%s'\n", tname); + log_error( "error closing '%s': %s\n", tname, strerror(errno)); + goto leave; + } + fd = -1; + + lockname = m_alloc( strlen(file_to_lock) + 6 ); + strcpy(stpcpy(lockname, file_to_lock), ".lock"); retry: - if( !link(tname, lockfile) ) - rc = 0; /* okay */ - else if( errno != EEXIST ) - log_error( "lock not made: link() failed: %s\n", strerror(errno) ); - else { /* lock file already there */ - if( (pid = read_lockfile(lockfile)) == -1 ) { + if( !link(tname, lockname) ) {/* fixme: better use stat to check the link count */ + handle = lockname; + lockname = NULL; + } + else if( errno == EEXIST ) { + if( (pid = read_lockfile(lockname)) == -1 ) { if( errno == ENOENT ) { - log_debug( "lockfile disappeared\n"); + log_info( "lockfile disappeared\n"); goto retry; } - log_debug("cannot read lockfile\n"); + log_info("cannot read lockfile\n"); } else if( pid == getpid() ) { log_info( "Oops: lock already hold by us\n"); - rc = 0; /* okay */ + handle = lockname; + lockname = NULL; } + #if 0 /* we should not do this without checking the permissions */ + /* and the hostname */ else if( kill(pid, 0) && errno == ESRCH ) { - log_info( "removing stale lockfile (created by %d)", (int)pid ); - remove( lockfile ); + log_info( "removing stale lockfile (created by %d)", pid ); + remove( lockname ); goto retry; } - log_debug( "lock not made: lock file exists\n" ); + #endif + if( timeout == -1 ) { + struct timeval tv; + log_info( "waiting for lock (hold by %d) ...\n", pid ); + /* can't use sleep, cause signals may be blocked */ + tv.tv_sec = 1; + tv.tv_usec = 0; + select(0, NULL, NULL, NULL, &tv); + goto retry; + } + /* fixme: implement timeouts */ } + else + log_error( "lock not made: link() failed: %s\n", strerror(errno) ); - if( tname ) { + leave: + if( fd != -1 ) + close(fd); + if( have_tfile ) remove(tname); - free(tname); - } - if( !rc ) - log_debug( "lock made\n"); - return rc; + m_free(tname); + m_free(lockname); + return handle; } /**************** @@ -140,7 +201,7 @@ release_dotlock( const char *lockfile ) lockfile); return -1; } - log_debug( "release_dotlock: released lockfile '%s'", lockfile); + m_free( (char*)lockfile ); return 0; } @@ -156,7 +217,7 @@ read_lockfile( const char *name ) if( (fd = open(name, O_RDONLY)) == -1 ) { int e = errno; - log_debug("error opening lockfile '%s'", name ); + log_debug("error opening lockfile '%s': %s\n", name, strerror(errno) ); errno = e; return -1; } @@ -175,4 +236,4 @@ read_lockfile( const char *name ) } return pid; } -#endif +