mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-29 22:49:41 +03:00 
			
		
		
		
	This includes removing tabs after periods in C comments, which was applied to back branches, so this change should not effect backpatching.
		
			
				
	
	
		
			251 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			251 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  *	file.c
 | |
|  *
 | |
|  *	file system operations
 | |
|  *
 | |
|  *	Copyright (c) 2010-2014, PostgreSQL Global Development Group
 | |
|  *	contrib/pg_upgrade/file.c
 | |
|  */
 | |
| 
 | |
| #include "postgres_fe.h"
 | |
| 
 | |
| #include "pg_upgrade.h"
 | |
| 
 | |
| #include <fcntl.h>
 | |
| 
 | |
| 
 | |
| 
 | |
| #ifndef WIN32
 | |
| static int	copy_file(const char *fromfile, const char *tofile, bool force);
 | |
| #else
 | |
| static int	win32_pghardlink(const char *src, const char *dst);
 | |
| #endif
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * copyAndUpdateFile()
 | |
|  *
 | |
|  *	Copies a relation file from src to dst.  If pageConverter is non-NULL, this function
 | |
|  *	uses that pageConverter to do a page-by-page conversion.
 | |
|  */
 | |
| const char *
 | |
| copyAndUpdateFile(pageCnvCtx *pageConverter,
 | |
| 				  const char *src, const char *dst, bool force)
 | |
| {
 | |
| 	if (pageConverter == NULL)
 | |
| 	{
 | |
| 		if (pg_copy_file(src, dst, force) == -1)
 | |
| 			return getErrorText(errno);
 | |
| 		else
 | |
| 			return NULL;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		/*
 | |
| 		 * We have a pageConverter object - that implies that the
 | |
| 		 * PageLayoutVersion differs between the two clusters so we have to
 | |
| 		 * perform a page-by-page conversion.
 | |
| 		 *
 | |
| 		 * If the pageConverter can convert the entire file at once, invoke
 | |
| 		 * that plugin function, otherwise, read each page in the relation
 | |
| 		 * file and call the convertPage plugin function.
 | |
| 		 */
 | |
| 
 | |
| #ifdef PAGE_CONVERSION
 | |
| 		if (pageConverter->convertFile)
 | |
| 			return pageConverter->convertFile(pageConverter->pluginData,
 | |
| 											  dst, src);
 | |
| 		else
 | |
| #endif
 | |
| 		{
 | |
| 			int			src_fd;
 | |
| 			int			dstfd;
 | |
| 			char		buf[BLCKSZ];
 | |
| 			ssize_t		bytesRead;
 | |
| 			const char *msg = NULL;
 | |
| 
 | |
| 			if ((src_fd = open(src, O_RDONLY, 0)) < 0)
 | |
| 				return "could not open source file";
 | |
| 
 | |
| 			if ((dstfd = open(dst, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) < 0)
 | |
| 			{
 | |
| 				close(src_fd);
 | |
| 				return "could not create destination file";
 | |
| 			}
 | |
| 
 | |
| 			while ((bytesRead = read(src_fd, buf, BLCKSZ)) == BLCKSZ)
 | |
| 			{
 | |
| #ifdef PAGE_CONVERSION
 | |
| 				if ((msg = pageConverter->convertPage(pageConverter->pluginData, buf, buf)) != NULL)
 | |
| 					break;
 | |
| #endif
 | |
| 				if (write(dstfd, buf, BLCKSZ) != BLCKSZ)
 | |
| 				{
 | |
| 					msg = "could not write new page to destination";
 | |
| 					break;
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			close(src_fd);
 | |
| 			close(dstfd);
 | |
| 
 | |
| 			if (msg)
 | |
| 				return msg;
 | |
| 			else if (bytesRead != 0)
 | |
| 				return "found partial page in source file";
 | |
| 			else
 | |
| 				return NULL;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * linkAndUpdateFile()
 | |
|  *
 | |
|  * Creates a hard link between the given relation files. We use
 | |
|  * this function to perform a true in-place update. If the on-disk
 | |
|  * format of the new cluster is bit-for-bit compatible with the on-disk
 | |
|  * format of the old cluster, we can simply link each relation
 | |
|  * instead of copying the data from the old cluster to the new cluster.
 | |
|  */
 | |
| const char *
 | |
| linkAndUpdateFile(pageCnvCtx *pageConverter,
 | |
| 				  const char *src, const char *dst)
 | |
| {
 | |
| 	if (pageConverter != NULL)
 | |
| 		return "Cannot in-place update this cluster, page-by-page conversion is required";
 | |
| 
 | |
| 	if (pg_link_file(src, dst) == -1)
 | |
| 		return getErrorText(errno);
 | |
| 	else
 | |
| 		return NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| #ifndef WIN32
 | |
| static int
 | |
| copy_file(const char *srcfile, const char *dstfile, bool force)
 | |
| {
 | |
| #define COPY_BUF_SIZE (50 * BLCKSZ)
 | |
| 
 | |
| 	int			src_fd;
 | |
| 	int			dest_fd;
 | |
| 	char	   *buffer;
 | |
| 	int			ret = 0;
 | |
| 	int			save_errno = 0;
 | |
| 
 | |
| 	if ((srcfile == NULL) || (dstfile == NULL))
 | |
| 	{
 | |
| 		errno = EINVAL;
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	if ((src_fd = open(srcfile, O_RDONLY, 0)) < 0)
 | |
| 		return -1;
 | |
| 
 | |
| 	if ((dest_fd = open(dstfile, O_RDWR | O_CREAT | (force ? 0 : O_EXCL), S_IRUSR | S_IWUSR)) < 0)
 | |
| 	{
 | |
| 		save_errno = errno;
 | |
| 
 | |
| 		if (src_fd != 0)
 | |
| 			close(src_fd);
 | |
| 
 | |
| 		errno = save_errno;
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	buffer = (char *) pg_malloc(COPY_BUF_SIZE);
 | |
| 
 | |
| 	/* perform data copying i.e read src source, write to destination */
 | |
| 	while (true)
 | |
| 	{
 | |
| 		ssize_t		nbytes = read(src_fd, buffer, COPY_BUF_SIZE);
 | |
| 
 | |
| 		if (nbytes < 0)
 | |
| 		{
 | |
| 			save_errno = errno;
 | |
| 			ret = -1;
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		if (nbytes == 0)
 | |
| 			break;
 | |
| 
 | |
| 		errno = 0;
 | |
| 
 | |
| 		if (write(dest_fd, buffer, nbytes) != nbytes)
 | |
| 		{
 | |
| 			/* if write didn't set errno, assume problem is no disk space */
 | |
| 			if (errno == 0)
 | |
| 				errno = ENOSPC;
 | |
| 			save_errno = errno;
 | |
| 			ret = -1;
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	pg_free(buffer);
 | |
| 
 | |
| 	if (src_fd != 0)
 | |
| 		close(src_fd);
 | |
| 
 | |
| 	if (dest_fd != 0)
 | |
| 		close(dest_fd);
 | |
| 
 | |
| 	if (save_errno != 0)
 | |
| 		errno = save_errno;
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| 
 | |
| void
 | |
| check_hard_link(void)
 | |
| {
 | |
| 	char		existing_file[MAXPGPATH];
 | |
| 	char		new_link_file[MAXPGPATH];
 | |
| 
 | |
| 	snprintf(existing_file, sizeof(existing_file), "%s/PG_VERSION", old_cluster.pgdata);
 | |
| 	snprintf(new_link_file, sizeof(new_link_file), "%s/PG_VERSION.linktest", new_cluster.pgdata);
 | |
| 	unlink(new_link_file);		/* might fail */
 | |
| 
 | |
| 	if (pg_link_file(existing_file, new_link_file) == -1)
 | |
| 	{
 | |
| 		pg_fatal("Could not create hard link between old and new data directories: %s\n"
 | |
| 				 "In link mode the old and new data directories must be on the same file system volume.\n",
 | |
| 				 getErrorText(errno));
 | |
| 	}
 | |
| 	unlink(new_link_file);
 | |
| }
 | |
| 
 | |
| #ifdef WIN32
 | |
| static int
 | |
| win32_pghardlink(const char *src, const char *dst)
 | |
| {
 | |
| 	/*
 | |
| 	 * CreateHardLinkA returns zero for failure
 | |
| 	 * http://msdn.microsoft.com/en-us/library/aa363860(VS.85).aspx
 | |
| 	 */
 | |
| 	if (CreateHardLinkA(dst, src, NULL) == 0)
 | |
| 		return -1;
 | |
| 	else
 | |
| 		return 0;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| 
 | |
| /* fopen() file with no group/other permissions */
 | |
| FILE *
 | |
| fopen_priv(const char *path, const char *mode)
 | |
| {
 | |
| 	mode_t		old_umask = umask(S_IRWXG | S_IRWXO);
 | |
| 	FILE	   *fp;
 | |
| 
 | |
| 	fp = fopen(path, mode);
 | |
| 	umask(old_umask);
 | |
| 
 | |
| 	return fp;
 | |
| }
 |