251 lines
5.6 KiB
C++
251 lines
5.6 KiB
C++
/*
|
|
* 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);
|
|
}
|