27
27
#include "receivelog.h"
28
28
#include "streamutil.h"
29
29
30
+ #include <sys/stat.h>
30
31
#include <sys/time.h>
31
32
#include <sys/types.h>
32
33
#include <unistd.h>
@@ -41,24 +42,128 @@ const XLogRecPtr InvalidXLogRecPtr = {0, 0};
41
42
* Open a new WAL file in the specified directory. Store the name
42
43
* (not including the full directory) in namebuf. Assumes there is
43
44
* enough room in this buffer...
45
+ *
46
+ * The file will be padded to 16Mb with zeroes.
44
47
*/
45
48
static int
46
49
open_walfile (XLogRecPtr startpoint , uint32 timeline , char * basedir , char * namebuf )
47
50
{
48
51
int f ;
49
52
char fn [MAXPGPATH ];
53
+ struct stat statbuf ;
54
+ char * zerobuf ;
55
+ int bytes ;
50
56
51
57
XLogFileName (namebuf , timeline , startpoint .xlogid ,
52
58
startpoint .xrecoff / XLOG_SEG_SIZE );
53
59
54
- snprintf (fn , sizeof (fn ), "%s/%s" , basedir , namebuf );
55
- f = open (fn , O_WRONLY | O_CREAT | O_EXCL | PG_BINARY , 0666 );
60
+ snprintf (fn , sizeof (fn ), "%s/%s.partial " , basedir , namebuf );
61
+ f = open (fn , O_WRONLY | O_CREAT | PG_BINARY , S_IRUSR | S_IWUSR );
56
62
if (f == -1 )
63
+ {
57
64
fprintf (stderr , _ ("%s: Could not open WAL segment %s: %s\n" ),
58
- progname , namebuf , strerror (errno ));
65
+ progname , fn , strerror (errno ));
66
+ return -1 ;
67
+ }
68
+
69
+ /*
70
+ * Verify that the file is either empty (just created), or a complete
71
+ * XLogSegSize segment. Anything in between indicates a corrupt file.
72
+ */
73
+ if (fstat (f , & statbuf ) != 0 )
74
+ {
75
+ fprintf (stderr , _ ("%s: could not stat WAL segment %s: %s\n" ),
76
+ progname , fn , strerror (errno ));
77
+ close (f );
78
+ return -1 ;
79
+ }
80
+ if (statbuf .st_size == XLogSegSize )
81
+ return f ; /* File is open and ready to use */
82
+ if (statbuf .st_size != 0 )
83
+ {
84
+ fprintf (stderr , _ ("%s: WAL segment %s is %d bytes, should be 0 or %d\n" ),
85
+ progname , fn , (int ) statbuf .st_size , XLogSegSize );
86
+ close (f );
87
+ return -1 ;
88
+ }
89
+
90
+ /* New, empty, file. So pad it to 16Mb with zeroes */
91
+ zerobuf = xmalloc0 (XLOG_BLCKSZ );
92
+ for (bytes = 0 ; bytes < XLogSegSize ; bytes += XLOG_BLCKSZ )
93
+ {
94
+ if (write (f , zerobuf , XLOG_BLCKSZ ) != XLOG_BLCKSZ )
95
+ {
96
+ fprintf (stderr , _ ("%s: could not pad WAL segment %s: %s\n" ),
97
+ progname , fn , strerror (errno ));
98
+ close (f );
99
+ unlink (fn );
100
+ return -1 ;
101
+ }
102
+ }
103
+ free (zerobuf );
104
+
105
+ if (lseek (f , SEEK_SET , 0 ) != 0 )
106
+ {
107
+ fprintf (stderr , _ ("%s: could not seek back to beginning of WAL segment %s: %s\n" ),
108
+ progname , fn , strerror (errno ));
109
+ close (f );
110
+ return -1 ;
111
+ }
59
112
return f ;
60
113
}
61
114
115
+ static bool
116
+ close_walfile (int walfile , char * basedir , char * walname )
117
+ {
118
+ off_t currpos = lseek (walfile , 0 , SEEK_CUR );
119
+
120
+ if (currpos == -1 )
121
+ {
122
+ fprintf (stderr , _ ("%s: could not get current position in file %s: %s\n" ),
123
+ progname , walname , strerror (errno ));
124
+ return false;
125
+ }
126
+
127
+ if (fsync (walfile ) != 0 )
128
+ {
129
+ fprintf (stderr , _ ("%s: could not fsync file %s: %s\n" ),
130
+ progname , walname , strerror (errno ));
131
+ return false;
132
+ }
133
+
134
+ if (close (walfile ) != 0 )
135
+ {
136
+ fprintf (stderr , _ ("%s: could not close file %s: %s\n" ),
137
+ progname , walname , strerror (errno ));
138
+ return false;
139
+ }
140
+
141
+ /*
142
+ * Rename the .partial file only if we've completed writing the
143
+ * whole segment.
144
+ */
145
+ if (currpos == XLOG_SEG_SIZE )
146
+ {
147
+ char oldfn [MAXPGPATH ];
148
+ char newfn [MAXPGPATH ];
149
+
150
+ snprintf (oldfn , sizeof (oldfn ), "%s/%s.partial" , basedir , walname );
151
+ snprintf (newfn , sizeof (newfn ), "%s/%s" , basedir , walname );
152
+ if (rename (oldfn , newfn ) != 0 )
153
+ {
154
+ fprintf (stderr , _ ("%s: could not rename file %s: %s\n" ),
155
+ progname , walname , strerror (errno ));
156
+ return false;
157
+ }
158
+ }
159
+ else
160
+ fprintf (stderr , _ ("%s: not renaming %s, segment is not complete.\n" ),
161
+ progname , walname );
162
+
163
+ return true;
164
+ }
165
+
166
+
62
167
/*
63
168
* Local version of GetCurrentTimestamp(), since we are not linked with
64
169
* backend code.
@@ -178,10 +283,8 @@ ReceiveXlogStream(PGconn *conn, XLogRecPtr startpos, uint32 timeline, char *sysi
178
283
if (stream_continue && stream_continue ())
179
284
{
180
285
if (walfile != -1 )
181
- {
182
- fsync (walfile );
183
- close (walfile );
184
- }
286
+ /* Potential error message is written by close_walfile */
287
+ return close_walfile (walfile , basedir , current_walfile_name );
185
288
return true;
186
289
}
187
290
@@ -360,8 +463,10 @@ ReceiveXlogStream(PGconn *conn, XLogRecPtr startpos, uint32 timeline, char *sysi
360
463
/* Did we reach the end of a WAL segment? */
361
464
if (blockpos .xrecoff % XLOG_SEG_SIZE == 0 )
362
465
{
363
- fsync (walfile );
364
- close (walfile );
466
+ if (!close_walfile (walfile , basedir , current_walfile_name ))
467
+ /* Error message written in close_walfile() */
468
+ return false;
469
+
365
470
walfile = -1 ;
366
471
xlogoff = 0 ;
367
472
0 commit comments