]> granicus.if.org Git - postgresql/blob - contrib/pg_upgrade/file.c
pg_upgrade: Report full disk better
[postgresql] / contrib / pg_upgrade / file.c
1 /*
2  *      file.c
3  *
4  *      file system operations
5  *
6  *      Copyright (c) 2010-2013, PostgreSQL Global Development Group
7  *      contrib/pg_upgrade/file.c
8  */
9
10 #include "postgres_fe.h"
11
12 #include "pg_upgrade.h"
13
14 #include <fcntl.h>
15
16
17
18 #ifndef WIN32
19 static int      copy_file(const char *fromfile, const char *tofile, bool force);
20 #else
21 static int      win32_pghardlink(const char *src, const char *dst);
22 #endif
23
24
25 /*
26  * copyAndUpdateFile()
27  *
28  *      Copies a relation file from src to dst.  If pageConverter is non-NULL, this function
29  *      uses that pageConverter to do a page-by-page conversion.
30  */
31 const char *
32 copyAndUpdateFile(pageCnvCtx *pageConverter,
33                                   const char *src, const char *dst, bool force)
34 {
35         if (pageConverter == NULL)
36         {
37                 if (pg_copy_file(src, dst, force) == -1)
38                         return getErrorText(errno);
39                 else
40                         return NULL;
41         }
42         else
43         {
44                 /*
45                  * We have a pageConverter object - that implies that the
46                  * PageLayoutVersion differs between the two clusters so we have to
47                  * perform a page-by-page conversion.
48                  *
49                  * If the pageConverter can convert the entire file at once, invoke
50                  * that plugin function, otherwise, read each page in the relation
51                  * file and call the convertPage plugin function.
52                  */
53
54 #ifdef PAGE_CONVERSION
55                 if (pageConverter->convertFile)
56                         return pageConverter->convertFile(pageConverter->pluginData,
57                                                                                           dst, src);
58                 else
59 #endif
60                 {
61                         int                     src_fd;
62                         int                     dstfd;
63                         char            buf[BLCKSZ];
64                         ssize_t         bytesRead;
65                         const char *msg = NULL;
66
67                         if ((src_fd = open(src, O_RDONLY, 0)) < 0)
68                                 return "could not open source file";
69
70                         if ((dstfd = open(dst, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) < 0)
71                         {
72                                 close(src_fd);
73                                 return "could not create destination file";
74                         }
75
76                         while ((bytesRead = read(src_fd, buf, BLCKSZ)) == BLCKSZ)
77                         {
78 #ifdef PAGE_CONVERSION
79                                 if ((msg = pageConverter->convertPage(pageConverter->pluginData, buf, buf)) != NULL)
80                                         break;
81 #endif
82                                 if (write(dstfd, buf, BLCKSZ) != BLCKSZ)
83                                 {
84                                         msg = "could not write new page to destination";
85                                         break;
86                                 }
87                         }
88
89                         close(src_fd);
90                         close(dstfd);
91
92                         if (msg)
93                                 return msg;
94                         else if (bytesRead != 0)
95                                 return "found partial page in source file";
96                         else
97                                 return NULL;
98                 }
99         }
100 }
101
102
103 /*
104  * linkAndUpdateFile()
105  *
106  * Creates a hard link between the given relation files. We use
107  * this function to perform a true in-place update. If the on-disk
108  * format of the new cluster is bit-for-bit compatible with the on-disk
109  * format of the old cluster, we can simply link each relation
110  * instead of copying the data from the old cluster to the new cluster.
111  */
112 const char *
113 linkAndUpdateFile(pageCnvCtx *pageConverter,
114                                   const char *src, const char *dst)
115 {
116         if (pageConverter != NULL)
117                 return "Cannot in-place update this cluster, page-by-page conversion is required";
118
119         if (pg_link_file(src, dst) == -1)
120                 return getErrorText(errno);
121         else
122                 return NULL;
123 }
124
125
126 #ifndef WIN32
127 static int
128 copy_file(const char *srcfile, const char *dstfile, bool force)
129 {
130 #define COPY_BUF_SIZE (50 * BLCKSZ)
131
132         int                     src_fd;
133         int                     dest_fd;
134         char       *buffer;
135         int                     ret = 0;
136         int                     save_errno = 0;
137
138         if ((srcfile == NULL) || (dstfile == NULL))
139         {
140                 errno = EINVAL;
141                 return -1;
142         }
143
144         if ((src_fd = open(srcfile, O_RDONLY, 0)) < 0)
145                 return -1;
146
147         if ((dest_fd = open(dstfile, O_RDWR | O_CREAT | (force ? 0 : O_EXCL), S_IRUSR | S_IWUSR)) < 0)
148         {
149                 save_errno = errno;
150
151                 if (src_fd != 0)
152                         close(src_fd);
153
154                 errno = save_errno;
155                 return -1;
156         }
157
158         buffer = (char *) pg_malloc(COPY_BUF_SIZE);
159
160         /* perform data copying i.e read src source, write to destination */
161         while (true)
162         {
163                 ssize_t         nbytes = read(src_fd, buffer, COPY_BUF_SIZE);
164
165                 if (nbytes < 0)
166                 {
167                         save_errno = errno;
168                         ret = -1;
169                         break;
170                 }
171
172                 if (nbytes == 0)
173                         break;
174
175                 errno = 0;
176
177                 if (write(dest_fd, buffer, nbytes) != nbytes)
178                 {
179                         /* if write didn't set errno, assume problem is no disk space */
180                         if (errno == 0)
181                                 errno = ENOSPC;
182                         save_errno = errno;
183                         ret = -1;
184                         break;
185                 }
186         }
187
188         pg_free(buffer);
189
190         if (src_fd != 0)
191                 close(src_fd);
192
193         if (dest_fd != 0)
194                 close(dest_fd);
195
196         if (save_errno != 0)
197                 errno = save_errno;
198
199         return ret;
200 }
201 #endif
202
203
204 void
205 check_hard_link(void)
206 {
207         char            existing_file[MAXPGPATH];
208         char            new_link_file[MAXPGPATH];
209
210         snprintf(existing_file, sizeof(existing_file), "%s/PG_VERSION", old_cluster.pgdata);
211         snprintf(new_link_file, sizeof(new_link_file), "%s/PG_VERSION.linktest", new_cluster.pgdata);
212         unlink(new_link_file);          /* might fail */
213
214         if (pg_link_file(existing_file, new_link_file) == -1)
215         {
216                 pg_fatal("Could not create hard link between old and new data directories: %s\n"
217                            "In link mode the old and new data directories must be on the same file system volume.\n",
218                            getErrorText(errno));
219         }
220         unlink(new_link_file);
221 }
222
223 #ifdef WIN32
224 static int
225 win32_pghardlink(const char *src, const char *dst)
226 {
227         /*
228          * CreateHardLinkA returns zero for failure
229          * http://msdn.microsoft.com/en-us/library/aa363860(VS.85).aspx
230          */
231         if (CreateHardLinkA(dst, src, NULL) == 0)
232                 return -1;
233         else
234                 return 0;
235 }
236 #endif
237
238
239 /* fopen() file with no group/other permissions */
240 FILE *
241 fopen_priv(const char *path, const char *mode)
242 {
243         mode_t          old_umask = umask(S_IRWXG | S_IRWXO);
244         FILE       *fp;
245
246         fp = fopen(path, mode);
247         umask(old_umask);
248
249         return fp;
250 }