Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * copydir.c
4 : : * copies a directory
5 : : *
6 : : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : * While "xcopy /e /i /q" works fine for copying directories, on Windows XP
10 : : * it requires a Window handle which prevents it from working when invoked
11 : : * as a service.
12 : : *
13 : : * IDENTIFICATION
14 : : * src/backend/storage/file/copydir.c
15 : : *
16 : : *-------------------------------------------------------------------------
17 : : */
18 : :
19 : : #include "postgres.h"
20 : :
21 : : #include <fcntl.h>
22 : : #include <unistd.h>
23 : :
24 : : #include "common/file_utils.h"
25 : : #include "miscadmin.h"
26 : : #include "pgstat.h"
27 : : #include "storage/copydir.h"
28 : : #include "storage/fd.h"
29 : :
30 : : /*
31 : : * copydir: copy a directory
32 : : *
33 : : * If recurse is false, subdirectories are ignored. Anything that's not
34 : : * a directory or a regular file is ignored.
35 : : */
36 : : void
452 michael@paquier.xyz 37 :CBC 104 : copydir(const char *fromdir, const char *todir, bool recurse)
38 : : {
39 : : DIR *xldir;
40 : : struct dirent *xlde;
41 : : char fromfile[MAXPGPATH * 2];
42 : : char tofile[MAXPGPATH * 2];
43 : :
2199 sfrost@snowman.net 44 [ - + ]: 104 : if (MakePGDirectory(todir) != 0)
6830 tgl@sss.pgh.pa.us 45 [ # # ]:UBC 0 : ereport(ERROR,
46 : : (errcode_for_file_access(),
47 : : errmsg("could not create directory \"%s\": %m", todir)));
48 : :
7356 tgl@sss.pgh.pa.us 49 :CBC 104 : xldir = AllocateDir(fromdir);
50 : :
6830 51 [ + + ]: 31050 : while ((xlde = ReadDir(xldir, fromdir)) != NULL)
52 : : {
53 : : PGFileType xlde_type;
54 : :
55 : : /* If we got a cancel signal during the copy of the directory, quit */
5031 bruce@momjian.us 56 [ - + ]: 30946 : CHECK_FOR_INTERRUPTS();
57 : :
6756 58 [ + + ]: 30946 : if (strcmp(xlde->d_name, ".") == 0 ||
6830 tgl@sss.pgh.pa.us 59 [ + + ]: 30842 : strcmp(xlde->d_name, "..") == 0)
6756 bruce@momjian.us 60 : 208 : continue;
61 : :
2560 peter_e@gmx.net 62 : 30738 : snprintf(fromfile, sizeof(fromfile), "%s/%s", fromdir, xlde->d_name);
63 : 30738 : snprintf(tofile, sizeof(tofile), "%s/%s", todir, xlde->d_name);
64 : :
590 michael@paquier.xyz 65 : 30738 : xlde_type = get_dirent_type(fromfile, xlde, false, ERROR);
66 : :
67 [ - + ]: 30738 : if (xlde_type == PGFILETYPE_DIR)
68 : : {
69 : : /* recurse to handle subdirectories */
6830 tgl@sss.pgh.pa.us 70 [ # # ]:UBC 0 : if (recurse)
71 : 0 : copydir(fromfile, tofile, true);
72 : : }
590 michael@paquier.xyz 73 [ + - ]:CBC 30738 : else if (xlde_type == PGFILETYPE_REG)
6830 tgl@sss.pgh.pa.us 74 : 30738 : copy_file(fromfile, tofile);
75 : : }
5165 76 : 104 : FreeDir(xldir);
77 : :
78 : : /*
79 : : * Be paranoid here and fsync all files to ensure the copy is really done.
80 : : * But if fsync is disabled, we're done.
81 : : */
4285 82 [ + - ]: 104 : if (!enableFsync)
83 : 104 : return;
84 : :
5165 tgl@sss.pgh.pa.us 85 :UBC 0 : xldir = AllocateDir(todir);
86 : :
87 [ # # ]: 0 : while ((xlde = ReadDir(xldir, todir)) != NULL)
88 : : {
5172 stark@mit.edu 89 [ # # ]: 0 : if (strcmp(xlde->d_name, ".") == 0 ||
90 [ # # ]: 0 : strcmp(xlde->d_name, "..") == 0)
91 : 0 : continue;
92 : :
2560 peter_e@gmx.net 93 : 0 : snprintf(tofile, sizeof(tofile), "%s/%s", todir, xlde->d_name);
94 : :
95 : : /*
96 : : * We don't need to sync subdirectories here since the recursive
97 : : * copydir will do it before it returns
98 : : */
590 michael@paquier.xyz 99 [ # # ]: 0 : if (get_dirent_type(tofile, xlde, false, ERROR) == PGFILETYPE_REG)
5159 stark@mit.edu 100 : 0 : fsync_fname(tofile, false);
101 : : }
5172 102 : 0 : FreeDir(xldir);
103 : :
104 : : /*
105 : : * It's important to fsync the destination directory itself as individual
106 : : * file fsyncs don't guarantee that the directory entry for the file is
107 : : * synced. Recent versions of ext4 have made the window much wider but
108 : : * it's been true for ext3 and other filesystems in the past.
109 : : */
5159 110 : 0 : fsync_fname(todir, true);
111 : : }
112 : :
113 : : /*
114 : : * copy one file
115 : : */
116 : : void
452 michael@paquier.xyz 117 :CBC 30747 : copy_file(const char *fromfile, const char *tofile)
118 : : {
119 : : char *buffer;
120 : : int srcfd;
121 : : int dstfd;
122 : : int nbytes;
123 : : off_t offset;
124 : : off_t flush_offset;
125 : :
126 : : /* Size of copy buffer (read and write requests) */
127 : : #define COPY_BUF_SIZE (8 * BLCKSZ)
128 : :
129 : : /*
130 : : * Size of data flush requests. It seems beneficial on most platforms to
131 : : * do this every 1MB or so. But macOS, at least with early releases of
132 : : * APFS, is really unfriendly to small mmap/msync requests, so there do it
133 : : * only every 32MB.
134 : : */
135 : : #if defined(__darwin__)
136 : : #define FLUSH_DISTANCE (32 * 1024 * 1024)
137 : : #else
138 : : #define FLUSH_DISTANCE (1024 * 1024)
139 : : #endif
140 : :
141 : : /* Use palloc to ensure we get a maxaligned buffer */
6799 tgl@sss.pgh.pa.us 142 : 30747 : buffer = palloc(COPY_BUF_SIZE);
143 : :
144 : : /*
145 : : * Open the files
146 : : */
2395 peter_e@gmx.net 147 : 30747 : srcfd = OpenTransientFile(fromfile, O_RDONLY | PG_BINARY);
6830 tgl@sss.pgh.pa.us 148 [ - + ]: 30747 : if (srcfd < 0)
6830 tgl@sss.pgh.pa.us 149 [ # # ]:UBC 0 : ereport(ERROR,
150 : : (errcode_for_file_access(),
151 : : errmsg("could not open file \"%s\": %m", fromfile)));
152 : :
2395 peter_e@gmx.net 153 :CBC 30747 : dstfd = OpenTransientFile(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY);
6830 tgl@sss.pgh.pa.us 154 [ - + ]: 30747 : if (dstfd < 0)
6830 tgl@sss.pgh.pa.us 155 [ # # ]:UBC 0 : ereport(ERROR,
156 : : (errcode_for_file_access(),
157 : : errmsg("could not create file \"%s\": %m", tofile)));
158 : :
159 : : /*
160 : : * Do the data copying.
161 : : */
2380 tgl@sss.pgh.pa.us 162 :CBC 30747 : flush_offset = 0;
5161 bruce@momjian.us 163 : 30747 : for (offset = 0;; offset += nbytes)
164 : : {
165 : : /* If we got a cancel signal during the copy of the file, quit */
5031 166 [ - + ]: 62092 : CHECK_FOR_INTERRUPTS();
167 : :
168 : : /*
169 : : * We fsync the files later, but during the copy, flush them every so
170 : : * often to avoid spamming the cache and hopefully get the kernel to
171 : : * start writing them out before the fsync comes.
172 : : */
2380 tgl@sss.pgh.pa.us 173 [ + + ]: 62092 : if (offset - flush_offset >= FLUSH_DISTANCE)
174 : : {
175 : 32 : pg_flush_data(dstfd, flush_offset, offset - flush_offset);
176 : 32 : flush_offset = offset;
177 : : }
178 : :
2584 rhaas@postgresql.org 179 : 62092 : pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_READ);
6799 tgl@sss.pgh.pa.us 180 : 62092 : nbytes = read(srcfd, buffer, COPY_BUF_SIZE);
2584 rhaas@postgresql.org 181 : 62092 : pgstat_report_wait_end();
6830 tgl@sss.pgh.pa.us 182 [ - + ]: 62092 : if (nbytes < 0)
6830 tgl@sss.pgh.pa.us 183 [ # # ]:UBC 0 : ereport(ERROR,
184 : : (errcode_for_file_access(),
185 : : errmsg("could not read file \"%s\": %m", fromfile)));
6830 tgl@sss.pgh.pa.us 186 [ + + ]:CBC 62092 : if (nbytes == 0)
187 : 30747 : break;
188 : 31345 : errno = 0;
2584 rhaas@postgresql.org 189 : 31345 : pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_WRITE);
6830 tgl@sss.pgh.pa.us 190 [ - + ]: 31345 : if ((int) write(dstfd, buffer, nbytes) != nbytes)
191 : : {
192 : : /* if write didn't set errno, assume problem is no disk space */
6830 tgl@sss.pgh.pa.us 193 [ # # ]:UBC 0 : if (errno == 0)
194 : 0 : errno = ENOSPC;
195 [ # # ]: 0 : ereport(ERROR,
196 : : (errcode_for_file_access(),
197 : : errmsg("could not write to file \"%s\": %m", tofile)));
198 : : }
2584 rhaas@postgresql.org 199 :CBC 31345 : pgstat_report_wait_end();
200 : : }
201 : :
2380 tgl@sss.pgh.pa.us 202 [ + + ]: 30747 : if (offset > flush_offset)
203 : 25494 : pg_flush_data(dstfd, flush_offset, offset - flush_offset);
204 : :
1744 peter@eisentraut.org 205 [ - + ]: 30747 : if (CloseTransientFile(dstfd) != 0)
6830 tgl@sss.pgh.pa.us 206 [ # # ]:UBC 0 : ereport(ERROR,
207 : : (errcode_for_file_access(),
208 : : errmsg("could not close file \"%s\": %m", tofile)));
209 : :
1744 peter@eisentraut.org 210 [ - + ]:CBC 30747 : if (CloseTransientFile(srcfd) != 0)
1863 michael@paquier.xyz 211 [ # # ]:UBC 0 : ereport(ERROR,
212 : : (errcode_for_file_access(),
213 : : errmsg("could not close file \"%s\": %m", fromfile)));
214 : :
6799 tgl@sss.pgh.pa.us 215 :CBC 30747 : pfree(buffer);
7640 bruce@momjian.us 216 : 30747 : }
|