49
49
#include "postgres_fe.h"
50
50
51
51
#include <dirent.h>
52
+ #include <fcntl.h>
52
53
#include <sys/stat.h>
53
54
#include <unistd.h>
54
55
#include <locale.h>
@@ -116,6 +117,7 @@ static const char *authmethodhost = "";
116
117
static const char * authmethodlocal = "" ;
117
118
static bool debug = false;
118
119
static bool noclean = false;
120
+ static bool do_sync = true;
119
121
static bool show_setting = false;
120
122
static char * xlog_dir = "" ;
121
123
@@ -160,6 +162,9 @@ static char *authwarning = NULL;
160
162
/*
161
163
* Centralized knowledge of switches to pass to backend
162
164
*
165
+ * Note: we run the backend with -F (fsync disabled) and then do a single
166
+ * pass of fsync'ing at the end. This is faster than fsync'ing each step.
167
+ *
163
168
* Note: in the shell-script version, we also passed PGDATA as a -D switch,
164
169
* but here it is more convenient to pass it as an environment variable
165
170
* (no quoting to worry about).
@@ -182,6 +187,9 @@ static char **filter_lines_with_token(char **lines, const char *token);
182
187
#endif
183
188
static char * * readfile (const char * path );
184
189
static void writefile (char * path , char * * lines );
190
+ static void walkdir (char * path , void (* action )(char * fname , bool isdir ));
191
+ static void pre_sync_fname (char * fname , bool isdir );
192
+ static void fsync_fname (char * fname , bool isdir );
185
193
static FILE * popen_check (const char * command , const char * mode );
186
194
static void exit_nicely (void );
187
195
static char * get_id (void );
@@ -209,6 +217,7 @@ static void load_plpgsql(void);
209
217
static void vacuum_db (void );
210
218
static void make_template0 (void );
211
219
static void make_postgres (void );
220
+ static void perform_fsync (void );
212
221
static void trapsig (int signum );
213
222
static void check_ok (void );
214
223
static char * escape_quotes (const char * src );
@@ -489,6 +498,174 @@ writefile(char *path, char **lines)
489
498
}
490
499
}
491
500
501
+ /*
502
+ * walkdir: recursively walk a directory, applying the action to each
503
+ * regular file and directory (including the named directory itself).
504
+ *
505
+ * Adapted from copydir() in copydir.c.
506
+ */
507
+ static void
508
+ walkdir (char * path , void (* action ) (char * fname , bool isdir ))
509
+ {
510
+ DIR * dir ;
511
+ struct dirent * direntry ;
512
+ char subpath [MAXPGPATH ];
513
+
514
+ dir = opendir (path );
515
+ if (dir == NULL )
516
+ {
517
+ fprintf (stderr , _ ("%s: could not open directory \"%s\": %s\n" ),
518
+ progname , path , strerror (errno ));
519
+ exit_nicely ();
520
+ }
521
+
522
+ while (errno = 0 , (direntry = readdir (dir )) != NULL )
523
+ {
524
+ struct stat fst ;
525
+
526
+ if (strcmp (direntry -> d_name , "." ) == 0 ||
527
+ strcmp (direntry -> d_name , ".." ) == 0 )
528
+ continue ;
529
+
530
+ snprintf (subpath , MAXPGPATH , "%s/%s" , path , direntry -> d_name );
531
+
532
+ if (lstat (subpath , & fst ) < 0 )
533
+ {
534
+ fprintf (stderr , _ ("%s: could not stat file \"%s\": %s\n" ),
535
+ progname , subpath , strerror (errno ));
536
+ exit_nicely ();
537
+ }
538
+
539
+ if (S_ISDIR (fst .st_mode ))
540
+ walkdir (subpath , action );
541
+ else if (S_ISREG (fst .st_mode ))
542
+ (* action ) (subpath , false);
543
+ }
544
+
545
+ #ifdef WIN32
546
+ /*
547
+ * This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4), but not in
548
+ * released version
549
+ */
550
+ if (GetLastError () == ERROR_NO_MORE_FILES )
551
+ errno = 0 ;
552
+ #endif
553
+
554
+ if (errno )
555
+ {
556
+ fprintf (stderr , _ ("%s: could not read directory \"%s\": %s\n" ),
557
+ progname , path , strerror (errno ));
558
+ exit_nicely ();
559
+ }
560
+
561
+ closedir (dir );
562
+
563
+ /*
564
+ * It's important to fsync the destination directory itself as individual
565
+ * file fsyncs don't guarantee that the directory entry for the file is
566
+ * synced. Recent versions of ext4 have made the window much wider but
567
+ * it's been an issue for ext3 and other filesystems in the past.
568
+ */
569
+ (* action ) (path , true);
570
+ }
571
+
572
+ /*
573
+ * Hint to the OS that it should get ready to fsync() this file.
574
+ */
575
+ static void
576
+ pre_sync_fname (char * fname , bool isdir )
577
+ {
578
+ #if defined(HAVE_SYNC_FILE_RANGE ) || \
579
+ (defined(USE_POSIX_FADVISE ) && defined(POSIX_FADV_DONTNEED ))
580
+ int fd ;
581
+
582
+ fd = open (fname , O_RDONLY | PG_BINARY );
583
+
584
+ /*
585
+ * Some OSs don't allow us to open directories at all (Windows returns
586
+ * EACCES)
587
+ */
588
+ if (fd < 0 && isdir && (errno == EISDIR || errno == EACCES ))
589
+ return ;
590
+
591
+ if (fd < 0 )
592
+ {
593
+ fprintf (stderr , _ ("%s: could not open file \"%s\": %s\n" ),
594
+ progname , fname , strerror (errno ));
595
+ exit_nicely ();
596
+ }
597
+
598
+ /*
599
+ * Prefer sync_file_range, else use posix_fadvise. We ignore any error
600
+ * here since this operation is only a hint anyway.
601
+ */
602
+ #if defined(HAVE_SYNC_FILE_RANGE )
603
+ sync_file_range (fd , 0 , 0 , SYNC_FILE_RANGE_WRITE );
604
+ #elif defined(USE_POSIX_FADVISE ) && defined(POSIX_FADV_DONTNEED )
605
+ posix_fadvise (fd , 0 , 0 , POSIX_FADV_DONTNEED );
606
+ #endif
607
+
608
+ close (fd );
609
+ #endif
610
+ }
611
+
612
+ /*
613
+ * fsync a file or directory
614
+ *
615
+ * Try to fsync directories but ignore errors that indicate the OS
616
+ * just doesn't allow/require fsyncing directories.
617
+ *
618
+ * Adapted from fsync_fname() in copydir.c.
619
+ */
620
+ static void
621
+ fsync_fname (char * fname , bool isdir )
622
+ {
623
+ int fd ;
624
+ int returncode ;
625
+
626
+ /*
627
+ * Some OSs require directories to be opened read-only whereas other
628
+ * systems don't allow us to fsync files opened read-only; so we need both
629
+ * cases here
630
+ */
631
+ if (!isdir )
632
+ fd = open (fname , O_RDWR | PG_BINARY );
633
+ else
634
+ fd = open (fname , O_RDONLY | PG_BINARY );
635
+
636
+ /*
637
+ * Some OSs don't allow us to open directories at all (Windows returns
638
+ * EACCES)
639
+ */
640
+ if (fd < 0 && isdir && (errno == EISDIR || errno == EACCES ))
641
+ return ;
642
+
643
+ else if (fd < 0 )
644
+ {
645
+ fprintf (stderr , _ ("%s: could not open file \"%s\": %s\n" ),
646
+ progname , fname , strerror (errno ));
647
+ exit_nicely ();
648
+ }
649
+
650
+ returncode = fsync (fd );
651
+
652
+ /* Some OSs don't allow us to fsync directories at all */
653
+ if (returncode != 0 && isdir && errno == EBADF )
654
+ {
655
+ close (fd );
656
+ return ;
657
+ }
658
+
659
+ if (returncode != 0 )
660
+ {
661
+ fprintf (stderr , _ ("%s: could not fsync file \"%s\": %s\n" ),
662
+ progname , fname , strerror (errno ));
663
+ exit_nicely ();
664
+ }
665
+
666
+ close (fd );
667
+ }
668
+
492
669
/*
493
670
* Open a subcommand with suitable error messaging
494
671
*/
@@ -2092,6 +2269,47 @@ make_postgres(void)
2092
2269
check_ok ();
2093
2270
}
2094
2271
2272
+ /*
2273
+ * fsync everything down to disk
2274
+ */
2275
+ static void
2276
+ perform_fsync (void )
2277
+ {
2278
+ char pdir [MAXPGPATH ];
2279
+
2280
+ fputs (_ ("syncing data to disk ... " ), stdout );
2281
+ fflush (stdout );
2282
+
2283
+ /*
2284
+ * We need to name the parent of PGDATA. get_parent_directory() isn't
2285
+ * enough here, because it can result in an empty string.
2286
+ */
2287
+ snprintf (pdir , MAXPGPATH , "%s/.." , pg_data );
2288
+ canonicalize_path (pdir );
2289
+
2290
+ /*
2291
+ * Hint to the OS so that we're going to fsync each of these files soon.
2292
+ */
2293
+
2294
+ /* first the parent of the PGDATA directory */
2295
+ pre_sync_fname (pdir , true);
2296
+
2297
+ /* then recursively through the directory */
2298
+ walkdir (pg_data , pre_sync_fname );
2299
+
2300
+ /*
2301
+ * Now, do the fsync()s in the same order.
2302
+ */
2303
+
2304
+ /* first the parent of the PGDATA directory */
2305
+ fsync_fname (pdir , true);
2306
+
2307
+ /* then recursively through the directory */
2308
+ walkdir (pg_data , fsync_fname );
2309
+
2310
+ check_ok ();
2311
+ }
2312
+
2095
2313
2096
2314
/*
2097
2315
* signal handler in case we are interrupted.
@@ -2532,6 +2750,7 @@ usage(const char *progname)
2532
2750
printf (_ (" -d, --debug generate lots of debugging output\n" ));
2533
2751
printf (_ (" -L DIRECTORY where to find the input files\n" ));
2534
2752
printf (_ (" -n, --noclean do not clean up after errors\n" ));
2753
+ printf (_ (" -N, --nosync do not wait for changes to be written safely to disk\n" ));
2535
2754
printf (_ (" -s, --show show internal settings\n" ));
2536
2755
printf (_ ("\nOther options:\n" ));
2537
2756
printf (_ (" -V, --version output version information, then exit\n" ));
@@ -2621,6 +2840,7 @@ main(int argc, char *argv[])
2621
2840
{"debug" , no_argument , NULL , 'd' },
2622
2841
{"show" , no_argument , NULL , 's' },
2623
2842
{"noclean" , no_argument , NULL , 'n' },
2843
+ {"nosync" , no_argument , NULL , 'N' },
2624
2844
{"xlogdir" , required_argument , NULL , 'X' },
2625
2845
{NULL , 0 , NULL , 0 }
2626
2846
};
@@ -2676,7 +2896,7 @@ main(int argc, char *argv[])
2676
2896
2677
2897
/* process command-line options */
2678
2898
2679
- while ((c = getopt_long (argc , argv , "dD:E:L:nU :WA:sT:X:" , long_options , & option_index )) != -1 )
2899
+ while ((c = getopt_long (argc , argv , "dD:E:L:nNU :WA:sT:X:" , long_options , & option_index )) != -1 )
2680
2900
{
2681
2901
switch (c )
2682
2902
{
@@ -2719,6 +2939,9 @@ main(int argc, char *argv[])
2719
2939
noclean = true;
2720
2940
printf (_ ("Running in noclean mode. Mistakes will not be cleaned up.\n" ));
2721
2941
break ;
2942
+ case 'N' :
2943
+ do_sync = false;
2944
+ break ;
2722
2945
case 'L' :
2723
2946
share_path = xstrdup (optarg );
2724
2947
break ;
@@ -3310,6 +3533,11 @@ main(int argc, char *argv[])
3310
3533
3311
3534
make_postgres ();
3312
3535
3536
+ if (do_sync )
3537
+ perform_fsync ();
3538
+ else
3539
+ printf (_ ("\nSync to disk skipped.\nThe data directory might become corrupt if the operating system crashes.\n" ));
3540
+
3313
3541
if (authwarning != NULL )
3314
3542
fprintf (stderr , "%s" , authwarning );
3315
3543
0 commit comments