1 /*-------------------------------------------------------------------------
2  *
3  * copydir.c
4  *	  copies a directory
5  *
6  * Portions Copyright (c) 1996-2017, 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 #include <sys/stat.h>
24 
25 #include "storage/copydir.h"
26 #include "storage/fd.h"
27 #include "miscadmin.h"
28 #include "pgstat.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
copydir(char * fromdir,char * todir,bool recurse)37 copydir(char *fromdir, char *todir, bool recurse)
38 {
39 	DIR		   *xldir;
40 	struct dirent *xlde;
41 	char		fromfile[MAXPGPATH * 2];
42 	char		tofile[MAXPGPATH * 2];
43 
44 	if (mkdir(todir, S_IRWXU) != 0)
45 		ereport(ERROR,
46 				(errcode_for_file_access(),
47 				 errmsg("could not create directory \"%s\": %m", todir)));
48 
49 	xldir = AllocateDir(fromdir);
50 	if (xldir == NULL)
51 		ereport(ERROR,
52 				(errcode_for_file_access(),
53 				 errmsg("could not open directory \"%s\": %m", fromdir)));
54 
55 	while ((xlde = ReadDir(xldir, fromdir)) != NULL)
56 	{
57 		struct stat fst;
58 
59 		/* If we got a cancel signal during the copy of the directory, quit */
60 		CHECK_FOR_INTERRUPTS();
61 
62 		if (strcmp(xlde->d_name, ".") == 0 ||
63 			strcmp(xlde->d_name, "..") == 0)
64 			continue;
65 
66 		snprintf(fromfile, sizeof(fromfile), "%s/%s", fromdir, xlde->d_name);
67 		snprintf(tofile, sizeof(tofile), "%s/%s", todir, xlde->d_name);
68 
69 		if (lstat(fromfile, &fst) < 0)
70 			ereport(ERROR,
71 					(errcode_for_file_access(),
72 					 errmsg("could not stat file \"%s\": %m", fromfile)));
73 
74 		if (S_ISDIR(fst.st_mode))
75 		{
76 			/* recurse to handle subdirectories */
77 			if (recurse)
78 				copydir(fromfile, tofile, true);
79 		}
80 		else if (S_ISREG(fst.st_mode))
81 			copy_file(fromfile, tofile);
82 	}
83 	FreeDir(xldir);
84 
85 	/*
86 	 * Be paranoid here and fsync all files to ensure the copy is really done.
87 	 * But if fsync is disabled, we're done.
88 	 */
89 	if (!enableFsync)
90 		return;
91 
92 	xldir = AllocateDir(todir);
93 	if (xldir == NULL)
94 		ereport(ERROR,
95 				(errcode_for_file_access(),
96 				 errmsg("could not open directory \"%s\": %m", todir)));
97 
98 	while ((xlde = ReadDir(xldir, todir)) != NULL)
99 	{
100 		struct stat fst;
101 
102 		if (strcmp(xlde->d_name, ".") == 0 ||
103 			strcmp(xlde->d_name, "..") == 0)
104 			continue;
105 
106 		snprintf(tofile, sizeof(tofile), "%s/%s", todir, xlde->d_name);
107 
108 		/*
109 		 * We don't need to sync subdirectories here since the recursive
110 		 * copydir will do it before it returns
111 		 */
112 		if (lstat(tofile, &fst) < 0)
113 			ereport(ERROR,
114 					(errcode_for_file_access(),
115 					 errmsg("could not stat file \"%s\": %m", tofile)));
116 
117 		if (S_ISREG(fst.st_mode))
118 			fsync_fname(tofile, false);
119 	}
120 	FreeDir(xldir);
121 
122 	/*
123 	 * It's important to fsync the destination directory itself as individual
124 	 * file fsyncs don't guarantee that the directory entry for the file is
125 	 * synced. Recent versions of ext4 have made the window much wider but
126 	 * it's been true for ext3 and other filesystems in the past.
127 	 */
128 	fsync_fname(todir, true);
129 }
130 
131 /*
132  * copy one file
133  */
134 void
copy_file(char * fromfile,char * tofile)135 copy_file(char *fromfile, char *tofile)
136 {
137 	char	   *buffer;
138 	int			srcfd;
139 	int			dstfd;
140 	int			nbytes;
141 	off_t		offset;
142 	off_t		flush_offset;
143 
144 	/* Size of copy buffer (read and write requests) */
145 #define COPY_BUF_SIZE (8 * BLCKSZ)
146 
147 	/*
148 	 * Size of data flush requests.  It seems beneficial on most platforms to
149 	 * do this every 1MB or so.  But macOS, at least with early releases of
150 	 * APFS, is really unfriendly to small mmap/msync requests, so there do it
151 	 * only every 32MB.
152 	 */
153 #if defined(__darwin__)
154 #define FLUSH_DISTANCE (32 * 1024 * 1024)
155 #else
156 #define FLUSH_DISTANCE (1024 * 1024)
157 #endif
158 
159 	/* Use palloc to ensure we get a maxaligned buffer */
160 	buffer = palloc(COPY_BUF_SIZE);
161 
162 	/*
163 	 * Open the files
164 	 */
165 	srcfd = OpenTransientFile(fromfile, O_RDONLY | PG_BINARY, 0);
166 	if (srcfd < 0)
167 		ereport(ERROR,
168 				(errcode_for_file_access(),
169 				 errmsg("could not open file \"%s\": %m", fromfile)));
170 
171 	dstfd = OpenTransientFile(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
172 							  S_IRUSR | S_IWUSR);
173 	if (dstfd < 0)
174 		ereport(ERROR,
175 				(errcode_for_file_access(),
176 				 errmsg("could not create file \"%s\": %m", tofile)));
177 
178 	/*
179 	 * Do the data copying.
180 	 */
181 	flush_offset = 0;
182 	for (offset = 0;; offset += nbytes)
183 	{
184 		/* If we got a cancel signal during the copy of the file, quit */
185 		CHECK_FOR_INTERRUPTS();
186 
187 		/*
188 		 * We fsync the files later, but during the copy, flush them every so
189 		 * often to avoid spamming the cache and hopefully get the kernel to
190 		 * start writing them out before the fsync comes.
191 		 */
192 		if (offset - flush_offset >= FLUSH_DISTANCE)
193 		{
194 			pg_flush_data(dstfd, flush_offset, offset - flush_offset);
195 			flush_offset = offset;
196 		}
197 
198 		pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_READ);
199 		nbytes = read(srcfd, buffer, COPY_BUF_SIZE);
200 		pgstat_report_wait_end();
201 		if (nbytes < 0)
202 			ereport(ERROR,
203 					(errcode_for_file_access(),
204 					 errmsg("could not read file \"%s\": %m", fromfile)));
205 		if (nbytes == 0)
206 			break;
207 		errno = 0;
208 		pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_WRITE);
209 		if ((int) write(dstfd, buffer, nbytes) != nbytes)
210 		{
211 			pgstat_report_wait_end();
212 			/* if write didn't set errno, assume problem is no disk space */
213 			if (errno == 0)
214 				errno = ENOSPC;
215 			ereport(ERROR,
216 					(errcode_for_file_access(),
217 					 errmsg("could not write to file \"%s\": %m", tofile)));
218 		}
219 		pgstat_report_wait_end();
220 	}
221 
222 	if (offset > flush_offset)
223 		pg_flush_data(dstfd, flush_offset, offset - flush_offset);
224 
225 	if (CloseTransientFile(dstfd))
226 		ereport(ERROR,
227 				(errcode_for_file_access(),
228 				 errmsg("could not close file \"%s\": %m", tofile)));
229 
230 	CloseTransientFile(srcfd);
231 
232 	pfree(buffer);
233 }
234