From 35851f133bf39da0e814e9681d6a7fcc62431de6 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Sat, 15 Sep 2012 15:15:22 +0200 Subject: [PATCH] add option to control amount of fsync()ing --- NEWS | 2 ++ TODO | 2 ++ src/config.c | 11 +++++++++++ src/drv_maildir.c | 4 ++-- src/isync.h | 6 ++++++ src/mbsync.1 | 22 +++++++++++++++++++++- src/run-tests.pl | 4 +++- src/sync.c | 5 +++-- 8 files changed, 50 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index aa478c9..ff72886 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,8 @@ UIDPLUS extension (e.g., M$ Exchange). More automatic handling of SSL certificates. +Data safety in case of system crashes is improved. + [1.0.0] Essentially a rewrite. Synchronization state storage concept, configuration diff --git a/TODO b/TODO index 8eed42d..c12931c 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,7 @@ find out why mutt's message size calc is confused. +f{,data}sync() usage could be optimized by batching the calls. + add some marker about message being already [remotely] trashed. real transactions would be certainly not particularly useful ... diff --git a/src/config.c b/src/config.c index a998514..9e4f766 100644 --- a/src/config.c +++ b/src/config.c @@ -39,6 +39,7 @@ channel_conf_t *channels; group_conf_t *groups; int global_ops[2]; char *global_sync_state; +int FSyncLevel = FSYNC_NORMAL; #define ARG_OPTIONAL 0 #define ARG_REQUIRED 1 @@ -448,6 +449,16 @@ load_config( const char *where, int pseudo ) } break; } + else if (!strcasecmp( "FSync", cfile.cmd )) + { + arg = cfile.val; + if (!strcasecmp( "None", arg )) + FSyncLevel = FSYNC_NONE; + else if (!strcasecmp( "Normal", arg )) + FSyncLevel = FSYNC_NORMAL; + else if (!strcasecmp( "Thorough", arg )) + FSyncLevel = FSYNC_THOROUGH; + } else if (!getopt_helper( &cfile, &gcops, global_ops, &global_sync_state )) { error( "%s:%d: unknown section keyword '%s'\n", diff --git a/src/drv_maildir.c b/src/drv_maildir.c index 921f97f..4807e75 100644 --- a/src/drv_maildir.c +++ b/src/drv_maildir.c @@ -432,7 +432,7 @@ maildir_store_uid( maildir_store_t *ctx ) n = sprintf( buf, "%d\n%d\n", ctx->gen.uidvalidity, ctx->nuid ); lseek( ctx->uvfd, 0, SEEK_SET ); - if (write( ctx->uvfd, buf, n ) != n || ftruncate( ctx->uvfd, n ) || fdatasync( ctx->uvfd )) { + if (write( ctx->uvfd, buf, n ) != n || ftruncate( ctx->uvfd, n ) || (FSyncLevel >= FSYNC_NORMAL && fdatasync( ctx->uvfd ))) { error( "Maildir error: cannot write UIDVALIDITY.\n" ); return DRV_BOX_BAD; } @@ -1204,7 +1204,7 @@ maildir_store_msg( store_t *gctx, msg_data_t *data, int to_trash, } ret = write( fd, data->data, data->len ); free( data->data ); - if (ret != data->len || (ret = fsync( fd ))) { + if (ret != data->len || ((FSyncLevel >= FSYNC_NORMAL) && (ret = fsync( fd )))) { if (ret < 0) sys_error( "Maildir error: cannot write %s", buf ); else diff --git a/src/isync.h b/src/isync.h index 3ea5fd0..ef6759e 100644 --- a/src/isync.h +++ b/src/isync.h @@ -481,6 +481,12 @@ extern group_conf_t *groups; extern int global_ops[2]; extern char *global_sync_state; +#define FSYNC_NONE 0 +#define FSYNC_NORMAL 1 +#define FSYNC_THOROUGH 2 + +extern int FSyncLevel; + int parse_bool( conffile_t *cfile ); int parse_int( conffile_t *cfile ); int parse_size( conffile_t *cfile ); diff --git a/src/mbsync.1 b/src/mbsync.1 index ced52ed..d452e31 100644 --- a/src/mbsync.1 +++ b/src/mbsync.1 @@ -20,7 +20,7 @@ \" As a special exception, mbsync may be linked with the OpenSSL library, \" despite that library's more restrictive license. .. -.TH mbsync 1 "2012 Aug 25" +.TH mbsync 1 "2012 Sep 15" .. .SH NAME mbsync - synchronize IMAP4 and Maildir mailboxes @@ -477,6 +477,26 @@ line, except that there newlines can be used as mailbox name separators as well. Add the specified channels to the group. This option can be specified multiple times within a Group. .. +.SS Global Options +.TP +\fBFSync\fR {\fINone\fR|\fINormal\fR|\fIThorough\fR} +.br +Select the amount of forced flushing \fBmbsync\fR performs, which determines +the level of data safety after system crashes and power outages: +.br +\fBNone\fR - no flushing at all. This is reasonably safe for file systems +which are mounted with data=ordered mode. +.br +\fBNormal\fR - message and critical metadata writes are flushed. No data +should be lost due to crashes, though it is still possible that messages +are duplicated after crashes. This is the default, and is a wise choice for +file systems mounted with data=writeback, in particular modern systems like +ext4, btrfs and xfs. The performance impact on older file systems may be +disproportionate. +.br +\fBThorough\fR - this avoids message duplication after crashes as well, +at some additional performance cost. +.. .SH SSL CERTIFICATES [to be done] .. diff --git a/src/run-tests.pl b/src/run-tests.pl index 9924f33..25eae65 100755 --- a/src/run-tests.pl +++ b/src/run-tests.pl @@ -256,7 +256,9 @@ sub writecfg($$$) open(FILE, ">", ".mbsyncrc") or die "Cannot open .mbsyncrc.\n"; print FILE -"MaildirStore master +"FSync None + +MaildirStore master Path ./ Inbox ./master ".shift()." diff --git a/src/sync.c b/src/sync.c index 3a1f386..17e0e06 100644 --- a/src/sync.c +++ b/src/sync.c @@ -42,7 +42,7 @@ const char *str_ms[] = { "master", "slave" }, *str_hl[] = { "push", "pull" }; void Fclose( FILE *f, int safe ) { - if ((safe && (fflush( f ) || fdatasync( fileno( f ) ))) || fclose( f ) == EOF) { + if ((safe && (fflush( f ) || (FSyncLevel >= FSYNC_NORMAL && fdatasync( fileno( f ) )))) || fclose( f ) == EOF) { sys_error( "Error: cannot close file. Disk full?" ); exit( 1 ); } @@ -1202,7 +1202,8 @@ box_loaded( int sts, void *aux ) cv->srec = srec; cv->msg = tmsg; Fprintf( svars->jfp, "# %d %d %." stringify(TUIDL) "s\n", srec->uid[M], srec->uid[S], srec->tuid ); - fdatasync( fileno( svars->jfp ) ); + if (FSyncLevel >= FSYNC_THOROUGH) + fdatasync( fileno( svars->jfp ) ); debug( " -> %sing message, TUID %." stringify(TUIDL) "s\n", str_hl[t], srec->tuid ); if (copy_msg( cv )) return;