⛏️ index : haiku.git

/*
 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */


#include "FSUtils.h"

#include <string.h>

#include <algorithm>
#include <string>

#include <Directory.h>
#include <File.h>
#include <Path.h>
#include <SymLink.h>

#include <AutoDeleter.h>

#include "DebugSupport.h"


static const size_t kCompareDataBufferSize = 64 * 1024;
const char* const kShellEscapeCharacters = " ~`#$&*()\\|[]{};'\"<>?!";


/*static*/ BString
FSUtils::ShellEscapeString(const BString& string)
{
	BString result(string);
	result.CharacterEscape(kShellEscapeCharacters, '\\');
	if (result.IsEmpty())
		throw std::bad_alloc();
	return result;
}


/*static*/ status_t
FSUtils::OpenSubDirectory(const BDirectory& baseDirectory,
	const RelativePath& path, bool create, BDirectory& _directory)
{
	// get a string for the path
	BString pathString = path.ToString();
	if (pathString.IsEmpty())
		RETURN_ERROR(B_NO_MEMORY);

	// If creating is not allowed, just try to open it.
	if (!create)
		RETURN_ERROR(_directory.SetTo(&baseDirectory, pathString));

	// get an absolute path and create the subdirectory
	BPath absolutePath;
	status_t error = absolutePath.SetTo(&baseDirectory, pathString);
	if (error != B_OK) {
		ERROR("Volume::OpenSubDirectory(): failed to get absolute path "
			"for subdirectory \"%s\": %s\n", pathString.String(),
			strerror(error));
		RETURN_ERROR(error);
	}

	error = create_directory(absolutePath.Path(),
		S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
	if (error != B_OK) {
		ERROR("Volume::OpenSubDirectory(): failed to create "
			"subdirectory \"%s\": %s\n", pathString.String(),
			strerror(error));
		RETURN_ERROR(error);
	}

	RETURN_ERROR(_directory.SetTo(&baseDirectory, pathString));
}


/*static*/ status_t
FSUtils::CompareFileContent(const Entry& entry1, const Entry& entry2,
	bool& _equal)
{
	BFile file1;
	status_t error = _OpenFile(entry1, file1);
	if (error != B_OK)
		return error;

	BFile file2;
	error = _OpenFile(entry2, file2);
	if (error != B_OK)
		return error;

	return CompareFileContent(file1, file2, _equal);
}


/*static*/ status_t
FSUtils::CompareFileContent(BPositionIO& content1, BPositionIO& content2,
	bool& _equal)
{
	// get and compare content size
	off_t size1;
	status_t error = content1.GetSize(&size1);
	if (error != B_OK)
		return error;

	off_t size2;
	error = content2.GetSize(&size2);
	if (error != B_OK)
		return error;

	if (size1 != size2) {
		_equal = false;
		return B_OK;
	}

	if (size1 == 0) {
		_equal = true;
		return B_OK;
	}

	// allocate a data buffer
	uint8* buffer1 = new(std::nothrow) uint8[2 * kCompareDataBufferSize];
	if (buffer1 == NULL)
		return B_NO_MEMORY;
	ArrayDeleter<uint8> bufferDeleter(buffer1);
	uint8* buffer2 = buffer1 + kCompareDataBufferSize;

	// compare the data
	off_t offset = 0;
	while (offset < size1) {
		size_t toCompare = std::min(size_t(size1 - offset),
			kCompareDataBufferSize);
		ssize_t bytesRead = content1.ReadAt(offset, buffer1, toCompare);
		if (bytesRead < 0)
			return bytesRead;
		if ((size_t)bytesRead != toCompare)
			return B_ERROR;

		bytesRead = content2.ReadAt(offset, buffer2, toCompare);
		if (bytesRead < 0)
			return bytesRead;
		if ((size_t)bytesRead != toCompare)
			return B_ERROR;

		if (memcmp(buffer1, buffer2, toCompare) != 0) {
			_equal = false;
			return B_OK;
		}

		offset += bytesRead;
	}

	_equal = true;
	return B_OK;
}


/*static*/ status_t
FSUtils::CompareSymLinks(const Entry& entry1, const Entry& entry2, bool& _equal)
{
	BSymLink symLink1;
	status_t error = _OpenSymLink(entry1, symLink1);
	if (error != B_OK)
		return error;

	BSymLink symLink2;
	error = _OpenSymLink(entry2, symLink2);
	if (error != B_OK)
		return error;

	return CompareSymLinks(symLink1, symLink2, _equal);
}


/*static*/ status_t
FSUtils::CompareSymLinks(BSymLink& symLink1, BSymLink& symLink2, bool& _equal)
{
	char buffer1[B_PATH_NAME_LENGTH];
	ssize_t bytesRead1 = symLink1.ReadLink(buffer1, sizeof(buffer1));
	if (bytesRead1 < 0)
		return bytesRead1;

	char buffer2[B_PATH_NAME_LENGTH];
	ssize_t bytesRead2 = symLink2.ReadLink(buffer2, sizeof(buffer2));
	if (bytesRead2 < 0)
		return bytesRead2;

	_equal = bytesRead1 == bytesRead2
		&& memcmp(buffer1, buffer2, bytesRead1) == 0;
	return B_OK;
}


/*static*/ status_t
FSUtils::ExtractPackageContent(const Entry& packageEntry,
	const char* contentPath, const Entry& targetDirectoryEntry)
{
	BPath packagePathBuffer;
	const char* packagePath;
	status_t error = packageEntry.GetPath(packagePathBuffer, packagePath);
	if (error != B_OK)
		return error;

	BPath targetPathBuffer;
	const char* targetPath;
	error = targetDirectoryEntry.GetPath(targetPathBuffer, targetPath);
	if (error != B_OK)
		return error;

	return ExtractPackageContent(packagePath, contentPath, targetPath);
}


/*static*/ status_t
FSUtils::ExtractPackageContent(const char* packagePath, const char* contentPath,
	const char* targetDirectoryPath)
{
	std::string commandLine = std::string("package extract -C ")
		+ ShellEscapeString(targetDirectoryPath).String()
		+ " "
		+ ShellEscapeString(packagePath).String()
		+ " "
		+ ShellEscapeString(contentPath).String();
	if (system(commandLine.c_str()) != 0)
		return B_ERROR;
	return B_OK;
}


/*static*/ status_t
FSUtils::_OpenFile(const Entry& entry, BFile& file)
{
	BPath pathBuffer;
	const char* path;
	status_t error = entry.GetPath(pathBuffer, path);
	if (error != B_OK)
		return error;

	return file.SetTo(path, B_READ_ONLY);
}


/*static*/ status_t
FSUtils::_OpenSymLink(const Entry& entry, BSymLink& symLink)
{
	BPath pathBuffer;
	const char* path;
	status_t error = entry.GetPath(pathBuffer, path);
	if (error != B_OK)
		return error;

	return symLink.SetTo(path);
}