mirror of
				https://git.proxmox.com/git/libgit2
				synced 2025-10-31 11:25:52 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			334 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			334 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #ifdef _WIN32
 | |
| 
 | |
| #define RM_RETRY_COUNT	5
 | |
| #define RM_RETRY_DELAY	10
 | |
| 
 | |
| #ifdef __MINGW32__
 | |
| 
 | |
| /* These security-enhanced functions are not available
 | |
|  * in MinGW, so just use the vanilla ones */
 | |
| #define wcscpy_s(a, b, c) wcscpy((a), (c))
 | |
| #define wcscat_s(a, b, c) wcscat((a), (c))
 | |
| 
 | |
| #endif /* __MINGW32__ */
 | |
| 
 | |
| static int
 | |
| fs__dotordotdot(WCHAR *_tocheck)
 | |
| {
 | |
| 	return _tocheck[0] == '.' &&
 | |
| 		(_tocheck[1] == '\0' ||
 | |
| 		 (_tocheck[1] == '.' && _tocheck[2] == '\0'));
 | |
| }
 | |
| 
 | |
| static int
 | |
| fs_rmdir_rmdir(WCHAR *_wpath)
 | |
| {
 | |
| 	unsigned retries = 1;
 | |
| 
 | |
| 	while (!RemoveDirectoryW(_wpath)) {
 | |
| 		/* Only retry when we have retries remaining, and the
 | |
| 		 * error was ERROR_DIR_NOT_EMPTY. */
 | |
| 		if (retries++ > RM_RETRY_COUNT ||
 | |
| 			ERROR_DIR_NOT_EMPTY != GetLastError())
 | |
| 			return -1;
 | |
| 
 | |
| 		/* Give whatever has a handle to a child item some time
 | |
| 		 * to release it before trying again */
 | |
| 		Sleep(RM_RETRY_DELAY * retries * retries);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void
 | |
| fs_rmdir_helper(WCHAR *_wsource)
 | |
| {
 | |
| 	WCHAR buffer[MAX_PATH];
 | |
| 	HANDLE find_handle;
 | |
| 	WIN32_FIND_DATAW find_data;
 | |
| 	size_t buffer_prefix_len;
 | |
| 
 | |
| 	/* Set up the buffer and capture the length */
 | |
| 	wcscpy_s(buffer, MAX_PATH, _wsource);
 | |
| 	wcscat_s(buffer, MAX_PATH, L"\\");
 | |
| 	buffer_prefix_len = wcslen(buffer);
 | |
| 
 | |
| 	/* FindFirstFile needs a wildcard to match multiple items */
 | |
| 	wcscat_s(buffer, MAX_PATH, L"*");
 | |
| 	find_handle = FindFirstFileW(buffer, &find_data);
 | |
| 	cl_assert(INVALID_HANDLE_VALUE != find_handle);
 | |
| 
 | |
| 	do {
 | |
| 		/* FindFirstFile/FindNextFile gives back . and ..
 | |
| 		 * entries at the beginning */
 | |
| 		if (fs__dotordotdot(find_data.cFileName))
 | |
| 			continue;
 | |
| 
 | |
| 		wcscpy_s(buffer + buffer_prefix_len, MAX_PATH - buffer_prefix_len, find_data.cFileName);
 | |
| 
 | |
| 		if (FILE_ATTRIBUTE_DIRECTORY & find_data.dwFileAttributes)
 | |
| 			fs_rmdir_helper(buffer);
 | |
| 		else {
 | |
| 			/* If set, the +R bit must be cleared before deleting */
 | |
| 			if (FILE_ATTRIBUTE_READONLY & find_data.dwFileAttributes)
 | |
| 				cl_assert(SetFileAttributesW(buffer, find_data.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY));
 | |
| 
 | |
| 			cl_assert(DeleteFileW(buffer));
 | |
| 		}
 | |
| 	}
 | |
| 	while (FindNextFileW(find_handle, &find_data));
 | |
| 
 | |
| 	/* Ensure that we successfully completed the enumeration */
 | |
| 	cl_assert(ERROR_NO_MORE_FILES == GetLastError());
 | |
| 
 | |
| 	/* Close the find handle */
 | |
| 	FindClose(find_handle);
 | |
| 
 | |
| 	/* Now that the directory is empty, remove it */
 | |
| 	cl_assert(0 == fs_rmdir_rmdir(_wsource));
 | |
| }
 | |
| 
 | |
| static int
 | |
| fs_rm_wait(WCHAR *_wpath)
 | |
| {
 | |
| 	unsigned retries = 1;
 | |
| 	DWORD last_error;
 | |
| 
 | |
| 	do {
 | |
| 		if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(_wpath))
 | |
| 			last_error = GetLastError();
 | |
| 		else
 | |
| 			last_error = ERROR_SUCCESS;
 | |
| 
 | |
| 		/* Is the item gone? */
 | |
| 		if (ERROR_FILE_NOT_FOUND == last_error ||
 | |
| 			ERROR_PATH_NOT_FOUND == last_error)
 | |
| 			return 0;
 | |
| 
 | |
| 		Sleep(RM_RETRY_DELAY * retries * retries);	
 | |
| 	}
 | |
| 	while (retries++ <= RM_RETRY_COUNT);
 | |
| 
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| static void
 | |
| fs_rm(const char *_source)
 | |
| {
 | |
| 	WCHAR wsource[MAX_PATH];
 | |
| 	DWORD attrs;
 | |
| 
 | |
| 	/* The input path is UTF-8. Convert it to wide characters
 | |
| 	 * for use with the Windows API */
 | |
| 	cl_assert(MultiByteToWideChar(CP_UTF8,
 | |
| 				MB_ERR_INVALID_CHARS,
 | |
| 				_source,
 | |
| 				-1, /* Indicates NULL termination */
 | |
| 				wsource,
 | |
| 				MAX_PATH));
 | |
| 
 | |
| 	/* Does the item exist? If not, we have no work to do */
 | |
| 	attrs = GetFileAttributesW(wsource);
 | |
| 
 | |
| 	if (INVALID_FILE_ATTRIBUTES == attrs)
 | |
| 		return;
 | |
| 
 | |
| 	if (FILE_ATTRIBUTE_DIRECTORY & attrs)
 | |
| 		fs_rmdir_helper(wsource);
 | |
| 	else {
 | |
| 		/* The item is a file. Strip the +R bit */
 | |
| 		if (FILE_ATTRIBUTE_READONLY & attrs)
 | |
| 			cl_assert(SetFileAttributesW(wsource, attrs & ~FILE_ATTRIBUTE_READONLY));
 | |
| 
 | |
| 		cl_assert(DeleteFileW(wsource));
 | |
| 	}
 | |
| 
 | |
| 	/* Wait for the DeleteFile or RemoveDirectory call to complete */
 | |
| 	cl_assert(0 == fs_rm_wait(wsource));
 | |
| }
 | |
| 
 | |
| static void
 | |
| fs_copydir_helper(WCHAR *_wsource, WCHAR *_wdest)
 | |
| {
 | |
| 	WCHAR buf_source[MAX_PATH], buf_dest[MAX_PATH];
 | |
| 	HANDLE find_handle;
 | |
| 	WIN32_FIND_DATAW find_data;
 | |
| 	size_t buf_source_prefix_len, buf_dest_prefix_len;
 | |
| 
 | |
| 	wcscpy_s(buf_source, MAX_PATH, _wsource);
 | |
| 	wcscat_s(buf_source, MAX_PATH, L"\\");
 | |
| 	buf_source_prefix_len = wcslen(buf_source);
 | |
| 
 | |
| 	wcscpy_s(buf_dest, MAX_PATH, _wdest);
 | |
| 	wcscat_s(buf_dest, MAX_PATH, L"\\");
 | |
| 	buf_dest_prefix_len = wcslen(buf_dest);
 | |
| 
 | |
| 	/* Get an enumerator for the items in the source. */
 | |
| 	wcscat_s(buf_source, MAX_PATH, L"*");
 | |
| 	find_handle = FindFirstFileW(buf_source, &find_data);
 | |
| 	cl_assert(INVALID_HANDLE_VALUE != find_handle);
 | |
| 
 | |
| 	/* Create the target directory. */
 | |
| 	cl_assert(CreateDirectoryW(_wdest, NULL));
 | |
| 
 | |
| 	do {
 | |
| 		/* FindFirstFile/FindNextFile gives back . and ..
 | |
| 		 * entries at the beginning */
 | |
| 		if (fs__dotordotdot(find_data.cFileName))
 | |
| 			continue;
 | |
| 
 | |
| 		wcscpy_s(buf_source + buf_source_prefix_len, MAX_PATH - buf_source_prefix_len, find_data.cFileName);
 | |
| 		wcscpy_s(buf_dest + buf_dest_prefix_len, MAX_PATH - buf_dest_prefix_len, find_data.cFileName);
 | |
| 
 | |
| 		if (FILE_ATTRIBUTE_DIRECTORY & find_data.dwFileAttributes)
 | |
| 			fs_copydir_helper(buf_source, buf_dest);
 | |
| 		else
 | |
| 			cl_assert(CopyFileW(buf_source, buf_dest, TRUE));
 | |
| 	}
 | |
| 	while (FindNextFileW(find_handle, &find_data));
 | |
| 
 | |
| 	/* Ensure that we successfully completed the enumeration */
 | |
| 	cl_assert(ERROR_NO_MORE_FILES == GetLastError());
 | |
| 
 | |
| 	/* Close the find handle */
 | |
| 	FindClose(find_handle);
 | |
| }
 | |
| 
 | |
| static void
 | |
| fs_copy(const char *_source, const char *_dest)
 | |
| {
 | |
| 	WCHAR wsource[MAX_PATH], wdest[MAX_PATH];
 | |
| 	DWORD source_attrs, dest_attrs;
 | |
| 	HANDLE find_handle;
 | |
| 	WIN32_FIND_DATAW find_data;
 | |
| 
 | |
| 	/* The input paths are UTF-8. Convert them to wide characters
 | |
| 	 * for use with the Windows API. */
 | |
| 	cl_assert(MultiByteToWideChar(CP_UTF8,
 | |
| 				MB_ERR_INVALID_CHARS,
 | |
| 				_source,
 | |
| 				-1,
 | |
| 				wsource,
 | |
| 				MAX_PATH));
 | |
| 
 | |
| 	cl_assert(MultiByteToWideChar(CP_UTF8,
 | |
| 				MB_ERR_INVALID_CHARS,
 | |
| 				_dest,
 | |
| 				-1,
 | |
| 				wdest,
 | |
| 				MAX_PATH));
 | |
| 
 | |
| 	/* Check the source for existence */
 | |
| 	source_attrs = GetFileAttributesW(wsource);
 | |
| 	cl_assert(INVALID_FILE_ATTRIBUTES != source_attrs);
 | |
| 
 | |
| 	/* Check the target for existence */
 | |
| 	dest_attrs = GetFileAttributesW(wdest);
 | |
| 
 | |
| 	if (INVALID_FILE_ATTRIBUTES != dest_attrs) {
 | |
| 		/* Target exists; append last path part of source to target.
 | |
| 		 * Use FindFirstFile to parse the path */
 | |
| 		find_handle = FindFirstFileW(wsource, &find_data);
 | |
| 		cl_assert(INVALID_HANDLE_VALUE != find_handle);
 | |
| 		wcscat_s(wdest, MAX_PATH, L"\\");
 | |
| 		wcscat_s(wdest, MAX_PATH, find_data.cFileName);
 | |
| 		FindClose(find_handle);
 | |
| 
 | |
| 		/* Check the new target for existence */
 | |
| 		cl_assert(INVALID_FILE_ATTRIBUTES == GetFileAttributesW(wdest));
 | |
| 	}
 | |
| 
 | |
| 	if (FILE_ATTRIBUTE_DIRECTORY & source_attrs)
 | |
| 		fs_copydir_helper(wsource, wdest);
 | |
| 	else
 | |
| 		cl_assert(CopyFileW(wsource, wdest, TRUE));
 | |
| }
 | |
| 
 | |
| void
 | |
| cl_fs_cleanup(void)
 | |
| {
 | |
| 	fs_rm(fixture_path(_clar_path, "*"));
 | |
| }
 | |
| 
 | |
| #else
 | |
| 
 | |
| #include <errno.h>
 | |
| #include <string.h>
 | |
| 
 | |
| static int
 | |
| shell_out(char * const argv[])
 | |
| {
 | |
| 	int status, piderr;
 | |
| 	pid_t pid;
 | |
| 
 | |
| 	pid = fork();
 | |
| 
 | |
| 	if (pid < 0) {
 | |
| 		fprintf(stderr,
 | |
| 			"System error: `fork()` call failed (%d) - %s\n",
 | |
| 			errno, strerror(errno));
 | |
| 		exit(-1);
 | |
| 	}
 | |
| 
 | |
| 	if (pid == 0) {
 | |
| 		execv(argv[0], argv);
 | |
| 	}
 | |
| 
 | |
| 	do {
 | |
| 		piderr = waitpid(pid, &status, WUNTRACED);
 | |
| 	} while (piderr < 0 && (errno == EAGAIN || errno == EINTR));
 | |
| 
 | |
| 	return WEXITSTATUS(status);
 | |
| }
 | |
| 
 | |
| static void
 | |
| fs_copy(const char *_source, const char *dest)
 | |
| {
 | |
| 	char *argv[5];
 | |
| 	char *source;
 | |
| 	size_t source_len;
 | |
| 
 | |
| 	source = strdup(_source);
 | |
| 	source_len = strlen(source);
 | |
| 
 | |
| 	if (source[source_len - 1] == '/')
 | |
| 		source[source_len - 1] = 0;
 | |
| 
 | |
| 	argv[0] = "/bin/cp";
 | |
| 	argv[1] = "-R";
 | |
| 	argv[2] = source;
 | |
| 	argv[3] = (char *)dest;
 | |
| 	argv[4] = NULL;
 | |
| 
 | |
| 	cl_must_pass_(
 | |
| 		shell_out(argv),
 | |
| 		"Failed to copy test fixtures to sandbox"
 | |
| 	);
 | |
| 
 | |
| 	free(source);
 | |
| }
 | |
| 
 | |
| static void
 | |
| fs_rm(const char *source)
 | |
| {
 | |
| 	char *argv[4];
 | |
| 
 | |
| 	argv[0] = "/bin/rm";
 | |
| 	argv[1] = "-Rf";
 | |
| 	argv[2] = (char *)source;
 | |
| 	argv[3] = NULL;
 | |
| 
 | |
| 	cl_must_pass_(
 | |
| 		shell_out(argv),
 | |
| 		"Failed to cleanup the sandbox"
 | |
| 	);
 | |
| }
 | |
| 
 | |
| void
 | |
| cl_fs_cleanup(void)
 | |
| {
 | |
| 	clar_unsandbox();
 | |
| 	clar_sandbox();
 | |
| }
 | |
| #endif
 | 
