package daemon: Implement writable files handling
* Move Volume::Exception to top level and into own files.
* Add utility class FSUtils, move Volume::RelativePath there and add a
bunch of FS utility functionality.
* Add FSTransaction, a helper class to record FS operations and revert
them.
* When activating a package we now extract the writable
files/directories it declares. The handling is not quite complete:
- We don't handle merges yet. I.e. the user will have to do that
manually for now.
- We don't propagate issues/infos regarding the writable files (e.g.
that a manual intervention is required) to the user yet.
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2013, Ingo Weinhold, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "Exception.h"
|
||||
|
||||
|
||||
using namespace BPackageKit::BPrivate;
|
||||
|
||||
|
||||
Exception::Exception(int32 error, const char* errorMessage,
|
||||
const char* packageName)
|
||||
:
|
||||
fError(error),
|
||||
fErrorMessage(errorMessage),
|
||||
fPackageName(packageName)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
BString
|
||||
Exception::ToString() const
|
||||
{
|
||||
const char* error;
|
||||
if (fError >= 0) {
|
||||
switch (fError) {
|
||||
case B_DAEMON_OK:
|
||||
error = "no error";
|
||||
break;
|
||||
case B_DAEMON_CHANGE_COUNT_MISMATCH:
|
||||
error = "transaction out of date";
|
||||
break;
|
||||
case B_DAEMON_BAD_REQUEST:
|
||||
error = "invalid transaction";
|
||||
break;
|
||||
case B_DAEMON_NO_SUCH_PACKAGE:
|
||||
error = "no such package";
|
||||
break;
|
||||
case B_DAEMON_PACKAGE_ALREADY_EXISTS:
|
||||
error = "package already exists";
|
||||
break;
|
||||
default:
|
||||
error = "unknown error";
|
||||
break;
|
||||
}
|
||||
} else
|
||||
error = strerror(fError);
|
||||
|
||||
BString string;
|
||||
if (!fErrorMessage.IsEmpty()) {
|
||||
string = fErrorMessage;
|
||||
string << ": ";
|
||||
}
|
||||
|
||||
string << error;
|
||||
|
||||
if (!fPackageName.IsEmpty())
|
||||
string << ", package: \"" << fPackageName << '"';
|
||||
|
||||
return string;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2013, Ingo Weinhold, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef EXCEPTION_H
|
||||
#define EXCEPTION_H
|
||||
|
||||
|
||||
#include <String.h>
|
||||
|
||||
#include <package/DaemonDefs.h>
|
||||
|
||||
|
||||
class Exception {
|
||||
public:
|
||||
Exception(int32 error,
|
||||
const char* errorMessage = NULL,
|
||||
const char* packageName = NULL);
|
||||
|
||||
int32 Error() const
|
||||
{ return fError; }
|
||||
|
||||
const BString& ErrorMessage() const
|
||||
{ return fErrorMessage; }
|
||||
|
||||
const BString& PackageName() const
|
||||
{ return fPackageName; }
|
||||
|
||||
BString ToString() const;
|
||||
|
||||
private:
|
||||
int32 fError;
|
||||
BString fErrorMessage;
|
||||
BString fPackageName;
|
||||
};
|
||||
|
||||
|
||||
#endif // EXCEPTION_H
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright 2013, Ingo Weinhold, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "FSTransaction.h"
|
||||
|
||||
#include <Entry.h>
|
||||
#include <Path.h>
|
||||
|
||||
#include <CopyEngine.h>
|
||||
#include <RemoveEngine.h>
|
||||
|
||||
#include "DebugSupport.h"
|
||||
#include "Exception.h"
|
||||
|
||||
|
||||
// #pragma mark - OperationInfo
|
||||
|
||||
|
||||
struct FSTransaction::OperationInfo {
|
||||
public:
|
||||
enum Type {
|
||||
TYPE_CREATE,
|
||||
TYPE_REMOVE,
|
||||
TYPE_MOVE,
|
||||
};
|
||||
|
||||
public:
|
||||
OperationInfo(Type type, const std::string& fromPath,
|
||||
const std::string& toPath, int32 modifiedOperation)
|
||||
:
|
||||
fType(type),
|
||||
fFromPath(fromPath),
|
||||
fToPath(toPath),
|
||||
fModifiedOperation(modifiedOperation),
|
||||
fEnabled(true)
|
||||
{
|
||||
}
|
||||
|
||||
const std::string& FromPath() const
|
||||
{
|
||||
return fFromPath;
|
||||
}
|
||||
|
||||
const std::string& ToPath() const
|
||||
{
|
||||
return fToPath;
|
||||
}
|
||||
|
||||
int32 ModifiedOperation() const
|
||||
{
|
||||
return fModifiedOperation;
|
||||
}
|
||||
|
||||
void SetModifiedOperation(int32 modifiedOperation)
|
||||
{
|
||||
fModifiedOperation = modifiedOperation;
|
||||
}
|
||||
|
||||
bool IsEnabled() const
|
||||
{
|
||||
return fEnabled;
|
||||
}
|
||||
|
||||
void SetEnabled(bool enabled)
|
||||
{
|
||||
fEnabled = enabled;
|
||||
}
|
||||
|
||||
status_t RollBack() const
|
||||
{
|
||||
switch (fType) {
|
||||
case TYPE_CREATE:
|
||||
{
|
||||
status_t error = BRemoveEngine().RemoveEntry(
|
||||
Entry(fFromPath.c_str()));
|
||||
if (error != B_OK) {
|
||||
ERROR("Failed to remove \"%s\": %s\n", fFromPath.c_str(),
|
||||
strerror(error));
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
case TYPE_REMOVE:
|
||||
{
|
||||
if (fToPath.empty())
|
||||
return B_NOT_SUPPORTED;
|
||||
|
||||
status_t error = BCopyEngine(
|
||||
BCopyEngine::COPY_RECURSIVELY
|
||||
| BCopyEngine::UNLINK_DESTINATION)
|
||||
.CopyEntry(fToPath.c_str(), fFromPath.c_str());
|
||||
if (error != B_OK) {
|
||||
ERROR("Failed to copy \"%s\" to \"%s\": %s\n",
|
||||
fToPath.c_str(), fFromPath.c_str(), strerror(error));
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
case TYPE_MOVE:
|
||||
{
|
||||
BEntry entry;
|
||||
status_t error = entry.SetTo(fToPath.c_str());
|
||||
if (error != B_OK) {
|
||||
ERROR("Failed to init entry for \"%s\": %s\n",
|
||||
fToPath.c_str(), strerror(error));
|
||||
return error;
|
||||
}
|
||||
|
||||
error = entry.Rename(fFromPath.c_str(), true);
|
||||
if (error != B_OK) {
|
||||
ERROR("Failed to move \"%s\" to \"%s\": %s\n",
|
||||
fToPath.c_str(), fFromPath.c_str(), strerror(error));
|
||||
return error;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
private:
|
||||
Type fType;
|
||||
std::string fFromPath;
|
||||
std::string fToPath;
|
||||
int32 fModifiedOperation;
|
||||
bool fEnabled;
|
||||
};
|
||||
|
||||
|
||||
// #pragma mark - FSTransaction
|
||||
|
||||
|
||||
FSTransaction::FSTransaction()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
FSTransaction::~FSTransaction()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FSTransaction::RollBack()
|
||||
{
|
||||
int32 count = (int32)fOperations.size();
|
||||
for (int32 i = count - 1; i >= 0; i--) {
|
||||
const OperationInfo& operation = fOperations[i];
|
||||
bool rolledBack = false;
|
||||
if (operation.IsEnabled())
|
||||
rolledBack = operation.RollBack() == B_OK;
|
||||
|
||||
if (!rolledBack && operation.ModifiedOperation() >= 0)
|
||||
fOperations[operation.ModifiedOperation()].SetEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
FSTransaction::CreateEntry(const Entry& entry, int32 modifiedOperation)
|
||||
{
|
||||
fOperations.push_back(
|
||||
OperationInfo(OperationInfo::TYPE_CREATE, _GetPath(entry),
|
||||
std::string(), modifiedOperation));
|
||||
return (int32)fOperations.size() - 1;
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
FSTransaction::RemoveEntry(const Entry& entry, const Entry& backupEntry,
|
||||
int32 modifiedOperation)
|
||||
{
|
||||
fOperations.push_back(
|
||||
OperationInfo(OperationInfo::TYPE_REMOVE, _GetPath(entry),
|
||||
_GetPath(backupEntry), modifiedOperation));
|
||||
return (int32)fOperations.size() - 1;
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
FSTransaction::MoveEntry(const Entry& fromEntry, const Entry& toEntry,
|
||||
int32 modifiedOperation)
|
||||
{
|
||||
fOperations.push_back(
|
||||
OperationInfo(OperationInfo::TYPE_MOVE, _GetPath(fromEntry),
|
||||
_GetPath(toEntry), modifiedOperation));
|
||||
return (int32)fOperations.size() - 1;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FSTransaction::RemoveOperationAt(int32 index)
|
||||
{
|
||||
int32 count = fOperations.size();
|
||||
if (index < 0 || index >= count) {
|
||||
throw Exception(B_ERROR,
|
||||
BString().SetToFormat("FSTransaction::RemoveOperationAt(): invalid "
|
||||
"operation index %" B_PRId32 "/%" B_PRId32, index, count));
|
||||
}
|
||||
|
||||
fOperations.erase(fOperations.begin() + index);
|
||||
|
||||
for (int32 i = index; i < count; i++) {
|
||||
int32 modifiedOperation = fOperations[i].ModifiedOperation();
|
||||
if (modifiedOperation == index)
|
||||
fOperations[i].SetModifiedOperation(-1);
|
||||
else if (modifiedOperation > index)
|
||||
fOperations[i].SetModifiedOperation(modifiedOperation - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*static*/ std::string
|
||||
FSTransaction::_GetPath(const Entry& entry)
|
||||
{
|
||||
BPath pathBuffer;
|
||||
const char* path;
|
||||
status_t error = entry.GetPath(pathBuffer, path);
|
||||
if (error == B_OK && path[0] != '/') {
|
||||
// make absolute
|
||||
error = pathBuffer.SetTo(path);
|
||||
}
|
||||
|
||||
if (error != B_OK)
|
||||
throw Exception(error);
|
||||
|
||||
return path;
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright 2013, Ingo Weinhold, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef FS_TRANSACTION_H
|
||||
#define FS_TRANSACTION_H
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "FSUtils.h"
|
||||
|
||||
|
||||
class FSTransaction {
|
||||
public:
|
||||
typedef FSUtils::Entry Entry;
|
||||
|
||||
class Operation;
|
||||
class CreateOperation;
|
||||
class RemoveOperation;
|
||||
class MoveOperation;
|
||||
|
||||
public:
|
||||
FSTransaction();
|
||||
~FSTransaction();
|
||||
|
||||
void RollBack();
|
||||
|
||||
int32 CreateEntry(const Entry& entry,
|
||||
int32 modifiedOperation = -1);
|
||||
int32 RemoveEntry(const Entry& entry,
|
||||
const Entry& backupEntry,
|
||||
int32 modifiedOperation = -1);
|
||||
int32 MoveEntry(const Entry& fromEntry,
|
||||
const Entry& toEntry,
|
||||
int32 modifiedOperation = -1);
|
||||
|
||||
void RemoveOperationAt(int32 index);
|
||||
|
||||
private:
|
||||
struct OperationInfo;
|
||||
typedef std::vector<OperationInfo> OperationList;
|
||||
|
||||
private:
|
||||
static std::string _GetPath(const Entry& entry);
|
||||
|
||||
private:
|
||||
OperationList fOperations;
|
||||
};
|
||||
|
||||
|
||||
class FSTransaction::Operation {
|
||||
public:
|
||||
Operation(FSTransaction* transaction, int32 operation)
|
||||
:
|
||||
fTransaction(transaction),
|
||||
fOperation(operation)
|
||||
{
|
||||
}
|
||||
|
||||
~Operation()
|
||||
{
|
||||
if (fTransaction != NULL && fOperation >= 0 && !fIsFinished)
|
||||
fTransaction->RemoveOperationAt(fOperation);
|
||||
}
|
||||
|
||||
/*! Arms the operation rollback, i.e. rolling back the transaction will
|
||||
revert this operation.
|
||||
*/
|
||||
void Finished()
|
||||
{
|
||||
fIsFinished = true;
|
||||
}
|
||||
|
||||
/*! Unregisters the operation rollback, i.e. rolling back the transaction
|
||||
will not revert this operation.
|
||||
*/
|
||||
void Unregister()
|
||||
{
|
||||
if (fTransaction != NULL && fOperation >= 0) {
|
||||
fTransaction->RemoveOperationAt(fOperation);
|
||||
fIsFinished = false;
|
||||
fTransaction = NULL;
|
||||
fOperation = -1;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
FSTransaction* fTransaction;
|
||||
int32 fOperation;
|
||||
bool fIsFinished;
|
||||
};
|
||||
|
||||
|
||||
class FSTransaction::CreateOperation : public FSTransaction::Operation {
|
||||
public:
|
||||
CreateOperation(FSTransaction* transaction, const Entry& entry,
|
||||
int32 modifiedOperation = -1)
|
||||
:
|
||||
Operation(transaction,
|
||||
transaction->CreateEntry(entry, modifiedOperation))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class FSTransaction::RemoveOperation : public FSTransaction::Operation {
|
||||
public:
|
||||
RemoveOperation(FSTransaction* transaction, const Entry& entry,
|
||||
const Entry& backupEntry, int32 modifiedOperation = -1)
|
||||
:
|
||||
Operation(transaction,
|
||||
transaction->RemoveEntry(entry, backupEntry, modifiedOperation))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class FSTransaction::MoveOperation : public FSTransaction::Operation {
|
||||
public:
|
||||
MoveOperation(FSTransaction* transaction, const Entry& fromEntry,
|
||||
const Entry& toEntry, int32 modifiedOperation = -1)
|
||||
:
|
||||
Operation(transaction,
|
||||
transaction->MoveEntry(fromEntry, toEntry, modifiedOperation))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // FS_TRANSACTION_H
|
||||
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* Copyright 2013, Ingo Weinhold, [email protected].
|
||||
* 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(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;
|
||||
MemoryDeleter 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();
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef FS_UTILS_H
|
||||
#define FS_UTILS_H
|
||||
|
||||
|
||||
#include <new>
|
||||
|
||||
#include <String.h>
|
||||
|
||||
#include <EntryOperationEngineBase.h>
|
||||
|
||||
|
||||
class BDirectory;
|
||||
class BFile;
|
||||
class BPositionIO;
|
||||
class BSymLink;
|
||||
|
||||
|
||||
class FSUtils {
|
||||
public:
|
||||
typedef ::BPrivate::BEntryOperationEngineBase::Entry Entry;
|
||||
|
||||
public:
|
||||
struct RelativePath {
|
||||
RelativePath(const char* component1 = NULL,
|
||||
const char* component2 = NULL, const char* component3 = NULL)
|
||||
:
|
||||
fComponentCount(kMaxComponentCount)
|
||||
{
|
||||
fComponents[0] = component1;
|
||||
fComponents[1] = component2;
|
||||
fComponents[2] = component3;
|
||||
|
||||
for (size_t i = 0; i < kMaxComponentCount; i++) {
|
||||
if (fComponents[i] == NULL) {
|
||||
fComponentCount = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IsEmpty() const
|
||||
{
|
||||
return fComponentCount == 0;
|
||||
}
|
||||
|
||||
RelativePath HeadPath(size_t componentsToDropCount = 1)
|
||||
{
|
||||
RelativePath result;
|
||||
if (componentsToDropCount < fComponentCount) {
|
||||
result.fComponentCount
|
||||
= fComponentCount - componentsToDropCount;
|
||||
for (size_t i = 0; i < result.fComponentCount; i++)
|
||||
result.fComponents[i] = fComponents[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const char* LastComponent() const
|
||||
{
|
||||
return fComponentCount > 0
|
||||
? fComponents[fComponentCount - 1] : NULL;
|
||||
}
|
||||
|
||||
BString ToString() const
|
||||
{
|
||||
if (fComponentCount == 0)
|
||||
return BString();
|
||||
|
||||
size_t length = fComponentCount - 1;
|
||||
for (size_t i = 0; i < fComponentCount; i++)
|
||||
length += strlen(fComponents[i]);
|
||||
|
||||
BString result;
|
||||
char* buffer = result.LockBuffer(length + 1);
|
||||
if (buffer == NULL)
|
||||
return BString();
|
||||
|
||||
for (size_t i = 0; i < fComponentCount; i++) {
|
||||
if (i > 0) {
|
||||
*buffer = '/';
|
||||
buffer++;
|
||||
}
|
||||
strcpy(buffer, fComponents[i]);
|
||||
buffer += strlen(buffer);
|
||||
}
|
||||
|
||||
return result.UnlockBuffer();
|
||||
}
|
||||
|
||||
private:
|
||||
static const size_t kMaxComponentCount = 3;
|
||||
|
||||
const char* fComponents[kMaxComponentCount];
|
||||
size_t fComponentCount;
|
||||
};
|
||||
|
||||
// throwing std::bad_alloc()
|
||||
class Path {
|
||||
public:
|
||||
Path(const char* path)
|
||||
:
|
||||
fPath(path)
|
||||
{
|
||||
if (fPath.IsEmpty()) {
|
||||
if (path[0] != '\0')
|
||||
throw std::bad_alloc();
|
||||
} else {
|
||||
// remove duplicate '/'s
|
||||
char* buffer = fPath.LockBuffer(fPath.Length());
|
||||
int32 k = 0;
|
||||
for (int32 i = 0; buffer[i] != '\0'; i++) {
|
||||
if (buffer[i] == '/' && k > 0 && buffer[k - 1] == '/')
|
||||
continue;
|
||||
buffer[k++] = buffer[i];
|
||||
}
|
||||
|
||||
// remove trailing '/'
|
||||
if (k > 1 && buffer[k - 1] == '/')
|
||||
k--;
|
||||
|
||||
fPath.LockBuffer(k);
|
||||
}
|
||||
}
|
||||
|
||||
Path& AppendComponent(const char* component)
|
||||
{
|
||||
if (fPath.IsEmpty()) {
|
||||
fPath = component;
|
||||
if (fPath.IsEmpty() && component[0] != '\0')
|
||||
throw std::bad_alloc();
|
||||
} else {
|
||||
int32 length = fPath.Length();
|
||||
if (fPath[length - 1] != '/') {
|
||||
fPath += '/';
|
||||
if (++length != fPath.Length())
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
fPath += component;
|
||||
if (fPath.Length() <= length)
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Path& RemoveLastComponent()
|
||||
{
|
||||
int32 index = fPath.FindLast('/');
|
||||
if (index < 0 || (index == 0 && fPath.Length() == 1))
|
||||
fPath.Truncate(0);
|
||||
else if (index == 0)
|
||||
fPath.Truncate(1);
|
||||
else
|
||||
fPath.Truncate(index - 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const char* Leaf() const
|
||||
{
|
||||
int32 index = fPath.FindLast('/');
|
||||
if (index < 0 || (index == 0 && fPath.Length() == 1))
|
||||
return fPath.String();
|
||||
return fPath.String() + index + 1;
|
||||
}
|
||||
|
||||
const BString ToString() const
|
||||
{
|
||||
return fPath;
|
||||
}
|
||||
|
||||
const char* ToCString() const
|
||||
{
|
||||
return fPath.String();
|
||||
}
|
||||
|
||||
operator const BString&() const
|
||||
{
|
||||
return fPath;
|
||||
}
|
||||
|
||||
operator const char*() const
|
||||
{
|
||||
return fPath;
|
||||
}
|
||||
|
||||
private:
|
||||
BString fPath;
|
||||
};
|
||||
|
||||
public:
|
||||
static BString ShellEscapeString(const BString& string);
|
||||
// throw std::bad_alloc
|
||||
|
||||
static status_t OpenSubDirectory(BDirectory& baseDirectory,
|
||||
const RelativePath& path, bool create,
|
||||
BDirectory& _directory);
|
||||
|
||||
static status_t CompareFileContent(const Entry& entry1,
|
||||
const Entry& entry2, bool& _equal);
|
||||
static status_t CompareFileContent(BPositionIO& content1,
|
||||
BPositionIO& content2, bool& _equal);
|
||||
|
||||
static status_t CompareSymLinks(const Entry& entry1,
|
||||
const Entry& entry2, bool& _equal);
|
||||
static status_t CompareSymLinks(BSymLink& symLink1,
|
||||
BSymLink& symLink2, bool& _equal);
|
||||
|
||||
static status_t ExtractPackageContent(const Entry& packageEntry,
|
||||
const char* contentPath,
|
||||
const Entry& targetDirectoryEntry);
|
||||
static status_t ExtractPackageContent(const char* packagePath,
|
||||
const char* contentPath,
|
||||
const char* targetDirectoryPath);
|
||||
|
||||
private:
|
||||
static status_t _OpenFile(const Entry& entry, BFile& file);
|
||||
static status_t _OpenSymLink(const Entry& entry,
|
||||
BSymLink& symLink);
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // FS_UTILS_H
|
||||
@@ -1,11 +1,14 @@
|
||||
SubDir HAIKU_TOP src servers package ;
|
||||
|
||||
UsePrivateSystemHeaders ;
|
||||
UsePrivateHeaders app interface kernel shared ;
|
||||
UsePrivateHeaders app interface kernel shared storage ;
|
||||
|
||||
Server package_daemon
|
||||
:
|
||||
DebugSupport.cpp
|
||||
Exception.cpp
|
||||
FSTransaction.cpp
|
||||
FSUtils.cpp
|
||||
Job.cpp
|
||||
JobQueue.cpp
|
||||
Package.cpp
|
||||
|
||||
@@ -257,6 +257,19 @@ Root::VolumeNodeMonitorEventOccurred(Volume* volume)
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Root::GetRootDirectoryRef(PackageFSMountType mountType, node_ref& _ref)
|
||||
{
|
||||
AutoLocker<BLocker> locker(fLock);
|
||||
Volume** volume = _GetVolume(mountType);
|
||||
if (volume == NULL)
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
|
||||
_ref = (*volume)->RootDirectoryRef();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Root::LastReferenceReleased()
|
||||
{
|
||||
|
||||
@@ -50,6 +50,9 @@ public:
|
||||
private:
|
||||
// Volume::Listener
|
||||
virtual void VolumeNodeMonitorEventOccurred(Volume* volume);
|
||||
virtual status_t GetRootDirectoryRef(
|
||||
PackageFSMountType mountType,
|
||||
node_ref& _ref) ;
|
||||
|
||||
protected:
|
||||
virtual void LastReferenceReleased();
|
||||
|
||||
+585
-240
@@ -36,10 +36,15 @@
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
#include <AutoLocker.h>
|
||||
#include <CopyEngine.h>
|
||||
#include <NotOwningEntryRef.h>
|
||||
#include <package/DaemonDefs.h>
|
||||
#include <package/PackagesDirectoryDefs.h>
|
||||
#include <RemoveEngine.h>
|
||||
|
||||
#include "DebugSupport.h"
|
||||
#include "Exception.h"
|
||||
#include "FSTransaction.h"
|
||||
|
||||
|
||||
using namespace BPackageKit::BPrivate;
|
||||
@@ -54,14 +59,14 @@ static const char* const kActivationFileName
|
||||
= PACKAGES_DIRECTORY_ACTIVATION_FILE;
|
||||
static const char* const kTemporaryActivationFileName
|
||||
= PACKAGES_DIRECTORY_ACTIVATION_FILE ".tmp";
|
||||
static const char* const kWritableFilesDirectoryName = "writable-files";
|
||||
static const char* const kPackageFileAttribute = "SYS:PACKAGE";
|
||||
|
||||
static const bigtime_t kHandleNodeMonitorEvents = 'nmon';
|
||||
|
||||
static const bigtime_t kNodeMonitorEventHandlingDelay = 500000;
|
||||
static const bigtime_t kCommunicationTimeout = 1000000;
|
||||
|
||||
const char* const kShellEscapeCharacters = " ~`#$&*()\\|[]{};'\"<>?!";
|
||||
|
||||
|
||||
// #pragma mark - Listener
|
||||
|
||||
@@ -100,159 +105,6 @@ private:
|
||||
};
|
||||
|
||||
|
||||
// #pragma mark - RelativePath
|
||||
|
||||
|
||||
struct Volume::RelativePath {
|
||||
RelativePath(const char* component1 = NULL, const char* component2 = NULL,
|
||||
const char* component3 = NULL)
|
||||
:
|
||||
fComponentCount(kMaxComponentCount)
|
||||
{
|
||||
fComponents[0] = component1;
|
||||
fComponents[1] = component2;
|
||||
fComponents[2] = component3;
|
||||
|
||||
for (size_t i = 0; i < kMaxComponentCount; i++) {
|
||||
if (fComponents[i] == NULL) {
|
||||
fComponentCount = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IsEmpty() const
|
||||
{
|
||||
return fComponentCount == 0;
|
||||
}
|
||||
|
||||
RelativePath HeadPath(size_t componentsToDropCount = 1)
|
||||
{
|
||||
RelativePath result;
|
||||
if (componentsToDropCount < fComponentCount) {
|
||||
result.fComponentCount = fComponentCount - componentsToDropCount;
|
||||
for (size_t i = 0; i < result.fComponentCount; i++)
|
||||
result.fComponents[i] = fComponents[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const char* LastComponent() const
|
||||
{
|
||||
return fComponentCount > 0 ? fComponents[fComponentCount - 1] : NULL;
|
||||
}
|
||||
|
||||
BString ToString() const
|
||||
{
|
||||
if (fComponentCount == 0)
|
||||
return BString();
|
||||
|
||||
size_t length = fComponentCount - 1;
|
||||
for (size_t i = 0; i < fComponentCount; i++)
|
||||
length += strlen(fComponents[i]);
|
||||
|
||||
BString result;
|
||||
char* buffer = result.LockBuffer(length + 1);
|
||||
if (buffer == NULL)
|
||||
return BString();
|
||||
|
||||
for (size_t i = 0; i < fComponentCount; i++) {
|
||||
if (i > 0) {
|
||||
*buffer = '/';
|
||||
buffer++;
|
||||
}
|
||||
strcpy(buffer, fComponents[i]);
|
||||
buffer += strlen(buffer);
|
||||
}
|
||||
|
||||
return result.UnlockBuffer();
|
||||
}
|
||||
|
||||
private:
|
||||
static const size_t kMaxComponentCount = 3;
|
||||
|
||||
const char* fComponents[kMaxComponentCount];
|
||||
size_t fComponentCount;
|
||||
};
|
||||
|
||||
|
||||
// #pragma mark - Exception
|
||||
|
||||
|
||||
struct Volume::Exception {
|
||||
Exception(int32 error, const char* errorMessage = NULL,
|
||||
const char* packageName = NULL)
|
||||
:
|
||||
fError(error),
|
||||
fErrorMessage(errorMessage),
|
||||
fPackageName(packageName)
|
||||
{
|
||||
}
|
||||
|
||||
int32 Error() const
|
||||
{
|
||||
return fError;
|
||||
}
|
||||
|
||||
const BString& ErrorMessage() const
|
||||
{
|
||||
return fErrorMessage;
|
||||
}
|
||||
|
||||
const BString& PackageName() const
|
||||
{
|
||||
return fPackageName;
|
||||
}
|
||||
|
||||
BString ToString() const
|
||||
{
|
||||
const char* error;
|
||||
if (fError >= 0) {
|
||||
switch (fError) {
|
||||
case B_DAEMON_OK:
|
||||
error = "no error";
|
||||
break;
|
||||
case B_DAEMON_CHANGE_COUNT_MISMATCH:
|
||||
error = "transaction out of date";
|
||||
break;
|
||||
case B_DAEMON_BAD_REQUEST:
|
||||
error = "invalid transaction";
|
||||
break;
|
||||
case B_DAEMON_NO_SUCH_PACKAGE:
|
||||
error = "no such package";
|
||||
break;
|
||||
case B_DAEMON_PACKAGE_ALREADY_EXISTS:
|
||||
error = "package already exists";
|
||||
break;
|
||||
default:
|
||||
error = "unknown error";
|
||||
break;
|
||||
}
|
||||
} else
|
||||
error = strerror(fError);
|
||||
|
||||
BString string;
|
||||
if (!fErrorMessage.IsEmpty()) {
|
||||
string = fErrorMessage;
|
||||
string << ": ";
|
||||
}
|
||||
|
||||
string << error;
|
||||
|
||||
if (!fPackageName.IsEmpty())
|
||||
string << ", package: \"" << fPackageName << '"';
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
private:
|
||||
int32 fError;
|
||||
BString fErrorMessage;
|
||||
BString fPackageName;
|
||||
};
|
||||
|
||||
|
||||
// #pragma mark - CommitTransactionHandler
|
||||
|
||||
|
||||
@@ -269,7 +121,8 @@ struct Volume::CommitTransactionHandler {
|
||||
fPackagesAlreadyAdded(packagesAlreadyAdded),
|
||||
fPackagesAlreadyRemoved(packagesAlreadyRemoved),
|
||||
fAddedGroups(),
|
||||
fAddedUsers()
|
||||
fAddedUsers(),
|
||||
fFSTransaction()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -339,8 +192,9 @@ struct Volume::CommitTransactionHandler {
|
||||
// revert user and group changes
|
||||
_RevertUserGroupChanges();
|
||||
|
||||
// remove old state directory
|
||||
_RemoveOldStateDirectory();
|
||||
// Revert all other FS operations, i.e. the writable files changes as
|
||||
// well as the creation of the old state directory.
|
||||
fFSTransaction.RollBack();
|
||||
}
|
||||
|
||||
const BString& OldStateDirectoryName() const
|
||||
@@ -510,11 +364,16 @@ private:
|
||||
}
|
||||
|
||||
// create the directory
|
||||
FSTransaction::CreateOperation createOldStateDirectoryOperation(
|
||||
&fFSTransaction, FSUtils::Entry(adminDirectory, directoryName));
|
||||
|
||||
error = adminDirectory.CreateDirectory(directoryName,
|
||||
&fOldStateDirectory);
|
||||
if (error != B_OK)
|
||||
throw Exception(error, "failed to create old state directory");
|
||||
|
||||
createOldStateDirectoryOperation.Finished();
|
||||
|
||||
fOldStateDirectoryName = directoryName;
|
||||
|
||||
// write the old activation file
|
||||
@@ -548,11 +407,8 @@ private:
|
||||
}
|
||||
|
||||
// get a BEntry for the package
|
||||
entry_ref entryRef;
|
||||
entryRef.device = fVolume->fPackagesDirectoryRef.device;
|
||||
entryRef.directory = fVolume->fPackagesDirectoryRef.node;
|
||||
if (entryRef.set_name(package->FileName()) != B_OK)
|
||||
throw Exception(B_NO_MEMORY);
|
||||
NotOwningEntryRef entryRef(fVolume->fPackagesDirectoryRef,
|
||||
package->FileName());
|
||||
|
||||
BEntry entry;
|
||||
status_t error = entry.SetTo(&entryRef);
|
||||
@@ -665,7 +521,7 @@ private:
|
||||
fAddedGroups.insert(groupName.String());
|
||||
|
||||
std::string commandLine("groupadd ");
|
||||
commandLine += _ShellEscapeString(groupName).String();
|
||||
commandLine += FSUtils::ShellEscapeString(groupName).String();
|
||||
|
||||
if (system(commandLine.c_str()) != 0) {
|
||||
fAddedGroups.erase(groupName.String());
|
||||
@@ -694,25 +550,26 @@ private:
|
||||
|
||||
if (!user.RealName().IsEmpty()) {
|
||||
commandLine += std::string("-n ")
|
||||
+ _ShellEscapeString(user.RealName()).String() + " ";
|
||||
+ FSUtils::ShellEscapeString(user.RealName()).String() + " ";
|
||||
}
|
||||
|
||||
if (!user.Home().IsEmpty()) {
|
||||
commandLine += std::string("-d ")
|
||||
+ _ShellEscapeString(user.Home()).String() + " ";
|
||||
+ FSUtils::ShellEscapeString(user.Home()).String() + " ";
|
||||
}
|
||||
|
||||
if (!user.Shell().IsEmpty()) {
|
||||
commandLine += std::string("-s ")
|
||||
+ _ShellEscapeString(user.Shell()).String() + " ";
|
||||
+ FSUtils::ShellEscapeString(user.Shell()).String() + " ";
|
||||
}
|
||||
|
||||
if (!user.Groups().IsEmpty()) {
|
||||
commandLine += std::string("-g ")
|
||||
+ _ShellEscapeString(user.Groups().First()).String() + " ";
|
||||
+ FSUtils::ShellEscapeString(user.Groups().First()).String()
|
||||
+ " ";
|
||||
}
|
||||
|
||||
commandLine += _ShellEscapeString(user.Name()).String();
|
||||
commandLine += FSUtils::ShellEscapeString(user.Name()).String();
|
||||
|
||||
if (system(commandLine.c_str()) != 0) {
|
||||
fAddedUsers.erase(user.Name().String());
|
||||
@@ -726,9 +583,10 @@ private:
|
||||
int32 groupCount = user.Groups().CountStrings();
|
||||
for (int32 i = 1; i < groupCount; i++) {
|
||||
commandLine = std::string("groupmod -A ")
|
||||
+ _ShellEscapeString(user.Name()).String()
|
||||
+ FSUtils::ShellEscapeString(user.Name()).String()
|
||||
+ " "
|
||||
+ _ShellEscapeString(user.Groups().StringAt(i)).String();
|
||||
+ FSUtils::ShellEscapeString(user.Groups().StringAt(i))
|
||||
.String();
|
||||
if (system(commandLine.c_str()) != 0) {
|
||||
fAddedUsers.erase(user.Name().String());
|
||||
throw Exception(error,
|
||||
@@ -743,7 +601,346 @@ private:
|
||||
void _AddGlobalWritableFile(Package* package,
|
||||
const BGlobalWritableFileInfo& file)
|
||||
{
|
||||
// TODO:...
|
||||
if (!file.IsIncluded())
|
||||
return;
|
||||
|
||||
// Open the root directory of the installation location where we will
|
||||
// extract the files -- that's the volume's root directory save for
|
||||
// "system" where it is "common".
|
||||
BDirectory rootDirectory;
|
||||
status_t error = fVolume->_OpenSettingsRootDirectory(rootDirectory);
|
||||
if (error != B_OK) {
|
||||
throw Exception(error,
|
||||
BString().SetToFormat("failed to get the root directory "
|
||||
"for writable files"),
|
||||
package->FileName());
|
||||
}
|
||||
|
||||
// Open writable-files directory in the administrative directory.
|
||||
if (fWritableFilesDirectory.InitCheck() != B_OK) {
|
||||
error = fVolume->_OpenPackagesSubDirectory(
|
||||
RelativePath(kAdminDirectoryName, kWritableFilesDirectoryName),
|
||||
true, fWritableFilesDirectory);
|
||||
|
||||
if (error != B_OK) {
|
||||
throw Exception(error,
|
||||
BString().SetToFormat("failed to get the backup directory "
|
||||
"for writable files"),
|
||||
package->FileName());
|
||||
}
|
||||
}
|
||||
|
||||
// extract files into a subdir of the writable-files directory
|
||||
BDirectory extractedFilesDirectory;
|
||||
_ExtractPackageContent(package, file.Path(),
|
||||
fWritableFilesDirectory, extractedFilesDirectory);
|
||||
|
||||
// Map the path name to the actual target location. Currently this only
|
||||
// concerns "settings/", which is mapped to "settings/global/".
|
||||
BString targetPath(file.Path());
|
||||
if (fVolume->fMountType == PACKAGE_FS_MOUNT_TYPE_HOME) {
|
||||
if (targetPath == "settings"
|
||||
|| targetPath.StartsWith("settings/")) {
|
||||
targetPath.Insert("/global", 8);
|
||||
if (targetPath.Length() == file.Path().Length())
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
}
|
||||
|
||||
// open parent directory of the source entry
|
||||
const char* lastSlash = strrchr(file.Path(), '/');
|
||||
BDirectory* sourceDirectory;
|
||||
BDirectory stackSourceDirectory;
|
||||
if (lastSlash != NULL) {
|
||||
sourceDirectory = &stackSourceDirectory;
|
||||
BString sourceParentPath(file.Path(),
|
||||
lastSlash - file.Path().String());
|
||||
if (sourceParentPath.Length() == 0)
|
||||
throw std::bad_alloc();
|
||||
|
||||
error = stackSourceDirectory.SetTo(&extractedFilesDirectory,
|
||||
sourceParentPath);
|
||||
if (error != B_OK) {
|
||||
throw Exception(error,
|
||||
BString().SetToFormat("failed to open directory \"%s\"",
|
||||
_GetPath(
|
||||
FSUtils::Entry(extractedFilesDirectory,
|
||||
sourceParentPath),
|
||||
sourceParentPath).String()),
|
||||
package->FileName());
|
||||
}
|
||||
} else {
|
||||
sourceDirectory = &extractedFilesDirectory;
|
||||
}
|
||||
|
||||
// open parent directory of the target entry -- create, if necessary
|
||||
FSUtils::Path relativeSourcePath(file.Path());
|
||||
lastSlash = strrchr(targetPath, '/');
|
||||
if (lastSlash != NULL) {
|
||||
BString targetParentPath(targetPath,
|
||||
lastSlash - targetPath.String());
|
||||
if (targetParentPath.Length() == 0)
|
||||
throw std::bad_alloc();
|
||||
|
||||
BDirectory targetDirectory;
|
||||
error = FSUtils::OpenSubDirectory(rootDirectory,
|
||||
RelativePath(targetParentPath), true, targetDirectory);
|
||||
if (error != B_OK) {
|
||||
throw Exception(error,
|
||||
BString().SetToFormat("failed to open/create directory "
|
||||
"\"%s\"",
|
||||
_GetPath(
|
||||
FSUtils::Entry(rootDirectory,targetParentPath),
|
||||
targetParentPath).String()),
|
||||
package->FileName());
|
||||
}
|
||||
_AddGlobalWritableFileRecurse(package, *sourceDirectory,
|
||||
relativeSourcePath, targetDirectory, lastSlash + 1,
|
||||
file.UpdateType());
|
||||
} else {
|
||||
_AddGlobalWritableFileRecurse(package, *sourceDirectory,
|
||||
relativeSourcePath, rootDirectory, targetPath,
|
||||
file.UpdateType());
|
||||
}
|
||||
}
|
||||
|
||||
void _AddGlobalWritableFileRecurse(Package* package,
|
||||
BDirectory& sourceDirectory, FSUtils::Path& relativeSourcePath,
|
||||
BDirectory& targetDirectory, const char* targetName,
|
||||
BWritableFileUpdateType updateType)
|
||||
{
|
||||
// * If the file doesn't exist, just copy the extracted one.
|
||||
// * If the file does exist, compare with the previous original version:
|
||||
// * If unchanged, just overwrite it.
|
||||
// * If changed, leave it to the user for now. When we support merging
|
||||
// first back the file up, then try the merge.
|
||||
|
||||
// Check whether the target location exists and what type the entry at
|
||||
// both locations are.
|
||||
struct stat targetStat;
|
||||
if (targetDirectory.GetStatFor(targetName, &targetStat) != B_OK) {
|
||||
// target doesn't exist -- just copy
|
||||
PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
|
||||
"couldn't get stat for writable file, copying...\n");
|
||||
FSTransaction::CreateOperation copyOperation(&fFSTransaction,
|
||||
FSUtils::Entry(targetDirectory, targetName));
|
||||
status_t error = BCopyEngine(BCopyEngine::COPY_RECURSIVELY)
|
||||
.CopyEntry(
|
||||
FSUtils::Entry(sourceDirectory, relativeSourcePath.Leaf()),
|
||||
FSUtils::Entry(targetDirectory, targetName));
|
||||
if (error != B_OK) {
|
||||
if (targetDirectory.GetStatFor(targetName, &targetStat) == B_OK)
|
||||
copyOperation.Finished();
|
||||
|
||||
throw Exception(error,
|
||||
BString().SetToFormat("failed to copy entry \"%s\"",
|
||||
_GetPath(
|
||||
FSUtils::Entry(sourceDirectory,
|
||||
relativeSourcePath.Leaf()),
|
||||
relativeSourcePath).String()),
|
||||
package->FileName());
|
||||
}
|
||||
copyOperation.Finished();
|
||||
return;
|
||||
}
|
||||
|
||||
struct stat sourceStat;
|
||||
status_t error = sourceDirectory.GetStatFor(relativeSourcePath.Leaf(),
|
||||
&sourceStat);
|
||||
if (error != B_OK) {
|
||||
throw Exception(error,
|
||||
BString().SetToFormat("failed to get stat data for entry "
|
||||
"\"%s\"",
|
||||
_GetPath(
|
||||
FSUtils::Entry(targetDirectory, targetName),
|
||||
targetName).String()),
|
||||
package->FileName());
|
||||
}
|
||||
|
||||
if ((sourceStat.st_mode & S_IFMT) != (targetStat.st_mode & S_IFMT)
|
||||
|| (!S_ISDIR(sourceStat.st_mode) && !S_ISREG(sourceStat.st_mode)
|
||||
&& !S_ISLNK(sourceStat.st_mode))) {
|
||||
// Source and target entry types don't match or this is an entry
|
||||
// we cannot handle. The user must handle this manually.
|
||||
PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
|
||||
"writable file exists, but type doesn't match previous type\n");
|
||||
// TODO: Notify user!
|
||||
return;
|
||||
}
|
||||
|
||||
if (S_ISDIR(sourceStat.st_mode)) {
|
||||
// entry is a directory -- recurse
|
||||
BDirectory sourceSubDirectory;
|
||||
error = sourceSubDirectory.SetTo(&sourceDirectory,
|
||||
relativeSourcePath.Leaf());
|
||||
if (error != B_OK) {
|
||||
throw Exception(error,
|
||||
BString().SetToFormat("failed to open directory \"%s\"",
|
||||
_GetPath(
|
||||
FSUtils::Entry(sourceDirectory,
|
||||
relativeSourcePath.Leaf()),
|
||||
relativeSourcePath).String()),
|
||||
package->FileName());
|
||||
}
|
||||
|
||||
BDirectory targetSubDirectory;
|
||||
error = targetSubDirectory.SetTo(&targetDirectory, targetName);
|
||||
if (error != B_OK) {
|
||||
throw Exception(error,
|
||||
BString().SetToFormat("failed to open directory \"%s\"",
|
||||
_GetPath(
|
||||
FSUtils::Entry(targetDirectory, targetName),
|
||||
targetName).String()),
|
||||
package->FileName());
|
||||
}
|
||||
|
||||
entry_ref entry;
|
||||
while (sourceSubDirectory.GetNextRef(&entry) == B_OK) {
|
||||
relativeSourcePath.AppendComponent(entry.name);
|
||||
_AddGlobalWritableFileRecurse(package, sourceSubDirectory,
|
||||
relativeSourcePath, targetSubDirectory, entry.name,
|
||||
updateType);
|
||||
relativeSourcePath.RemoveLastComponent();
|
||||
}
|
||||
|
||||
PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
|
||||
"writable directory, recursion done\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// get the package the target file originated from
|
||||
BString originalPackage;
|
||||
if (BNode(&targetDirectory, targetName).ReadAttrString(
|
||||
kPackageFileAttribute, &originalPackage) != B_OK) {
|
||||
// Can't determine the original package. The user must handle this
|
||||
// manually.
|
||||
// TODO: Notify user, if not B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD!
|
||||
PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
|
||||
"failed to get SYS:PACKAGE attribute\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// If that's our package, we're happy.
|
||||
if (originalPackage == package->RevisionedNameThrows()) {
|
||||
PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
|
||||
"file tagged with same package version we're activating\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check, whether the writable-files directory for the original package
|
||||
// exists.
|
||||
BString originalRelativeSourcePath = BString().SetToFormat("%s/%s",
|
||||
originalPackage.String(), relativeSourcePath.ToCString());
|
||||
if (originalRelativeSourcePath.IsEmpty())
|
||||
throw std::bad_alloc();
|
||||
|
||||
struct stat originalPackageStat;
|
||||
if (fWritableFilesDirectory.GetStatFor(originalRelativeSourcePath,
|
||||
&originalPackageStat) != B_OK
|
||||
|| (sourceStat.st_mode & S_IFMT)
|
||||
!= (originalPackageStat.st_mode & S_IFMT)) {
|
||||
// Original entry doesn't exist (either we don't have the data from
|
||||
// the original package or the entry really didn't exist) or its
|
||||
// type differs from the expected one. The user must handle this
|
||||
// manually.
|
||||
PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
|
||||
"original \"%s\" doesn't exist or has other type\n",
|
||||
_GetPath(FSUtils::Entry(fWritableFilesDirectory,
|
||||
originalRelativeSourcePath),
|
||||
originalRelativeSourcePath).String());
|
||||
return;
|
||||
// TODO: Notify user!
|
||||
}
|
||||
|
||||
if (S_ISREG(sourceStat.st_mode)) {
|
||||
// compare file content
|
||||
bool equal;
|
||||
error = FSUtils::CompareFileContent(
|
||||
FSUtils::Entry(fWritableFilesDirectory,
|
||||
originalRelativeSourcePath),
|
||||
FSUtils::Entry(targetDirectory, targetName),
|
||||
equal);
|
||||
// TODO: Merge support!
|
||||
if (error != B_OK || !equal) {
|
||||
// The comparison failed or the files differ. The user must
|
||||
// handle this manually.
|
||||
PRINT("Volume::CommitTransactionHandler::"
|
||||
"_AddGlobalWritableFile(): "
|
||||
"file comparison failed (%s) or files aren't equal\n",
|
||||
strerror(error));
|
||||
return;
|
||||
// TODO: Notify user, if not B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD!
|
||||
}
|
||||
} else {
|
||||
// compare symlinks
|
||||
bool equal;
|
||||
error = FSUtils::CompareSymLinks(
|
||||
FSUtils::Entry(fWritableFilesDirectory,
|
||||
originalRelativeSourcePath),
|
||||
FSUtils::Entry(targetDirectory, targetName),
|
||||
equal);
|
||||
if (error != B_OK || !equal) {
|
||||
// The comparison failed or the symlinks differ. The user must
|
||||
// handle this manually.
|
||||
PRINT("Volume::CommitTransactionHandler::"
|
||||
"_AddGlobalWritableFile(): "
|
||||
"symlink comparison failed (%s) or symlinks aren't equal\n",
|
||||
strerror(error));
|
||||
return;
|
||||
// TODO: Notify user, if not B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD!
|
||||
}
|
||||
}
|
||||
|
||||
// Replace the existing file/symlink. We do that in two steps: First
|
||||
// copy the new file to a neighoring location, then move-replace the
|
||||
// old file.
|
||||
BString tempTargetName;
|
||||
tempTargetName.SetToFormat("%s.%s", targetName,
|
||||
package->RevisionedNameThrows().String());
|
||||
if (tempTargetName.IsEmpty())
|
||||
throw std::bad_alloc();
|
||||
|
||||
// copy
|
||||
FSTransaction::CreateOperation copyOperation(&fFSTransaction,
|
||||
FSUtils::Entry(targetDirectory, tempTargetName));
|
||||
|
||||
error = BCopyEngine(BCopyEngine::UNLINK_DESTINATION).CopyEntry(
|
||||
FSUtils::Entry(sourceDirectory, relativeSourcePath.Leaf()),
|
||||
FSUtils::Entry(targetDirectory, tempTargetName));
|
||||
if (error != B_OK) {
|
||||
throw Exception(error,
|
||||
BString().SetToFormat("failed to copy entry \"%s\"",
|
||||
_GetPath(
|
||||
FSUtils::Entry(sourceDirectory,
|
||||
relativeSourcePath.Leaf()),
|
||||
relativeSourcePath).String()),
|
||||
package->FileName());
|
||||
}
|
||||
|
||||
copyOperation.Finished();
|
||||
|
||||
// rename
|
||||
FSTransaction::RemoveOperation renameOperation(&fFSTransaction,
|
||||
FSUtils::Entry(targetDirectory, targetName),
|
||||
FSUtils::Entry(fWritableFilesDirectory,
|
||||
originalRelativeSourcePath));
|
||||
|
||||
BEntry targetEntry;
|
||||
error = targetEntry.SetTo(&targetDirectory, tempTargetName);
|
||||
if (error == B_OK)
|
||||
error = targetEntry.Rename(targetName, true);
|
||||
if (error != B_OK) {
|
||||
throw Exception(error,
|
||||
BString().SetToFormat("failed to rename entry \"%s\" to \"%s\"",
|
||||
_GetPath(
|
||||
FSUtils::Entry(targetDirectory, tempTargetName),
|
||||
tempTargetName).String(),
|
||||
targetName),
|
||||
package->FileName());
|
||||
}
|
||||
|
||||
renameOperation.Finished();
|
||||
copyOperation.Unregister();
|
||||
}
|
||||
|
||||
void _RevertAddPackagesToActivate()
|
||||
@@ -774,13 +971,8 @@ private:
|
||||
continue;
|
||||
|
||||
// get BEntry for the package
|
||||
entry_ref entryRef;
|
||||
entryRef.device = fVolume->fPackagesDirectoryRef.device;
|
||||
entryRef.directory = fVolume->fPackagesDirectoryRef.node;
|
||||
if (entryRef.set_name(package->FileName()) != B_OK) {
|
||||
ERROR("out of memory\n");
|
||||
continue;
|
||||
}
|
||||
NotOwningEntryRef entryRef(fVolume->fPackagesDirectoryRef,
|
||||
package->FileName());
|
||||
|
||||
BEntry entry;
|
||||
error = entry.SetTo(&entryRef);
|
||||
@@ -852,7 +1044,7 @@ private:
|
||||
for (StringSet::const_iterator it = fAddedUsers.begin();
|
||||
it != fAddedUsers.end(); ++it) {
|
||||
std::string commandLine("userdel ");
|
||||
commandLine += _ShellEscapeString(it->c_str()).String();
|
||||
commandLine += FSUtils::ShellEscapeString(it->c_str()).String();
|
||||
if (system(commandLine.c_str()) != 0)
|
||||
ERROR("failed to remove user \"%s\"\n", it->c_str());
|
||||
}
|
||||
@@ -861,39 +1053,12 @@ private:
|
||||
for (StringSet::const_iterator it = fAddedGroups.begin();
|
||||
it != fAddedGroups.end(); ++it) {
|
||||
std::string commandLine("groupdel ");
|
||||
commandLine += _ShellEscapeString(it->c_str()).String();
|
||||
commandLine += FSUtils::ShellEscapeString(it->c_str()).String();
|
||||
if (system(commandLine.c_str()) != 0)
|
||||
ERROR("failed to remove group \"%s\"\n", it->c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void _RemoveOldStateDirectory()
|
||||
{
|
||||
if (fOldStateDirectory.InitCheck() != B_OK)
|
||||
return;
|
||||
|
||||
// remove the old activation file (it won't exist, if creating it
|
||||
// failed)
|
||||
BEntry(&fOldStateDirectory, kActivationFileName).Remove();
|
||||
|
||||
// Now the directory should be empty. If it isn't, it still contains
|
||||
// some old package file, which we failed to move back.
|
||||
BEntry entry;
|
||||
status_t error = fOldStateDirectory.GetEntry(&entry);
|
||||
if (error != B_OK) {
|
||||
ERROR("failed to get entry for old state directory: %s\n",
|
||||
strerror(error));
|
||||
return;
|
||||
}
|
||||
|
||||
error = entry.Remove();
|
||||
if (error != B_OK) {
|
||||
ERROR("failed to remove old state directory: %s\n",
|
||||
strerror(error));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void _RunPostInstallScripts()
|
||||
{
|
||||
for (PackageSet::iterator it = fAddedPackages.begin();
|
||||
@@ -929,13 +1094,184 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
BString _ShellEscapeString(const BString& string)
|
||||
static BString _GetPath(const FSUtils::Entry& entry,
|
||||
const BString& fallback)
|
||||
{
|
||||
BString result(string);
|
||||
result.CharacterEscape(kShellEscapeCharacters, '\\');
|
||||
if (result.IsEmpty())
|
||||
BString path = entry.Path();
|
||||
return path.IsEmpty() ? fallback : path;
|
||||
}
|
||||
|
||||
void _ExtractPackageContent(Package* package, const char* contentPath,
|
||||
BDirectory& targetDirectory, BDirectory& _extractedFilesDirectory)
|
||||
{
|
||||
// check whether the subdirectory already exists
|
||||
BString targetName(package->RevisionedNameThrows());
|
||||
|
||||
BEntry targetEntry;
|
||||
status_t error = targetEntry.SetTo(&targetDirectory, targetName);
|
||||
if (error != B_OK) {
|
||||
throw Exception(error,
|
||||
BString().SetToFormat("failed to init entry \"%s\"",
|
||||
_GetPath(
|
||||
FSUtils::Entry(targetDirectory, targetName),
|
||||
targetName).String()),
|
||||
package->FileName());
|
||||
}
|
||||
if (targetEntry.Exists()) {
|
||||
// nothing to do -- the very same version of the package has already
|
||||
// been extracted
|
||||
error = _extractedFilesDirectory.SetTo(&targetDirectory,
|
||||
targetName);
|
||||
if (error != B_OK) {
|
||||
throw Exception(error,
|
||||
BString().SetToFormat("failed to open directory \"%s\"",
|
||||
_GetPath(
|
||||
FSUtils::Entry(targetDirectory, targetName),
|
||||
targetName).String()),
|
||||
package->FileName());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// create the subdirectory with a temporary name (remove, if it already
|
||||
// exists)
|
||||
BString temporaryTargetName = BString().SetToFormat("%s.tmp",
|
||||
targetName.String());
|
||||
if (temporaryTargetName.IsEmpty())
|
||||
throw std::bad_alloc();
|
||||
return result;
|
||||
|
||||
error = targetEntry.SetTo(&targetDirectory, temporaryTargetName);
|
||||
if (error != B_OK) {
|
||||
throw Exception(error,
|
||||
BString().SetToFormat("failed to init entry \"%s\"",
|
||||
_GetPath(
|
||||
FSUtils::Entry(targetDirectory, temporaryTargetName),
|
||||
temporaryTargetName).String()),
|
||||
package->FileName());
|
||||
}
|
||||
|
||||
if (targetEntry.Exists()) {
|
||||
// remove pre-existing
|
||||
error = BRemoveEngine().RemoveEntry(FSUtils::Entry(targetEntry));
|
||||
if (error != B_OK) {
|
||||
throw Exception(error,
|
||||
BString().SetToFormat("failed to remove directory \"%s\"",
|
||||
_GetPath(
|
||||
FSUtils::Entry(targetDirectory,
|
||||
temporaryTargetName),
|
||||
temporaryTargetName).String()),
|
||||
package->FileName());
|
||||
}
|
||||
}
|
||||
|
||||
BDirectory& subDirectory = _extractedFilesDirectory;
|
||||
FSTransaction::CreateOperation createSubDirectoryOperation(
|
||||
&fFSTransaction,
|
||||
FSUtils::Entry(targetDirectory, temporaryTargetName));
|
||||
error = targetDirectory.CreateDirectory(temporaryTargetName,
|
||||
&subDirectory);
|
||||
if (error != B_OK) {
|
||||
throw Exception(error,
|
||||
BString().SetToFormat("failed to create directory \"%s\"",
|
||||
_GetPath(
|
||||
FSUtils::Entry(targetDirectory, temporaryTargetName),
|
||||
temporaryTargetName).String()),
|
||||
package->FileName());
|
||||
}
|
||||
|
||||
createSubDirectoryOperation.Finished();
|
||||
|
||||
// extract
|
||||
NotOwningEntryRef packageRef(fVolume->fPackagesDirectoryRef,
|
||||
package->FileName());
|
||||
|
||||
error = FSUtils::ExtractPackageContent(FSUtils::Entry(packageRef),
|
||||
contentPath, FSUtils::Entry(subDirectory));
|
||||
if (error != B_OK) {
|
||||
throw Exception(error,
|
||||
BString().SetToFormat("failed to extracted \"%s\" from package",
|
||||
contentPath),
|
||||
package->FileName());
|
||||
}
|
||||
|
||||
// tag all entries with the package attribute
|
||||
error = _TagPackageEntriesRecursively(subDirectory, targetName, true);
|
||||
if (error != B_OK) {
|
||||
throw Exception(error,
|
||||
BString().SetToFormat("failed to tag extract files in \"%s\" "
|
||||
"with package attribute",
|
||||
_GetPath(
|
||||
FSUtils::Entry(targetDirectory, temporaryTargetName),
|
||||
temporaryTargetName).String()),
|
||||
package->FileName());
|
||||
}
|
||||
|
||||
// rename the subdirectory
|
||||
error = targetEntry.Rename(targetName);
|
||||
if (error != B_OK) {
|
||||
throw Exception(error,
|
||||
BString().SetToFormat("failed to rename entry \"%s\" to \"%s\"",
|
||||
_GetPath(
|
||||
FSUtils::Entry(targetDirectory, temporaryTargetName),
|
||||
temporaryTargetName).String(),
|
||||
targetName.String()),
|
||||
package->FileName());
|
||||
}
|
||||
|
||||
// keep the directory, regardless of whether the transaction is rolled
|
||||
// back
|
||||
createSubDirectoryOperation.Unregister();
|
||||
}
|
||||
|
||||
static status_t _TagPackageEntriesRecursively(BDirectory& directory,
|
||||
const BString& value, bool nonDirectoriesOnly)
|
||||
{
|
||||
char buffer[sizeof(dirent) + B_FILE_NAME_LENGTH];
|
||||
dirent *entry = (dirent*)buffer;
|
||||
while (directory.GetNextDirents(entry, sizeof(buffer), 1) == 1) {
|
||||
if (strcmp(entry->d_name, ".") == 0
|
||||
|| strcmp(entry->d_name, "..") == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// determine type
|
||||
struct stat st;
|
||||
status_t error = directory.GetStatFor(entry->d_name, &st);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
bool isDirectory = S_ISDIR(st.st_mode);
|
||||
|
||||
// open the node and set the attribute
|
||||
BNode stackNode;
|
||||
BDirectory stackDirectory;
|
||||
BNode* node;
|
||||
if (isDirectory) {
|
||||
node = &stackDirectory;
|
||||
error = stackDirectory.SetTo(&directory, entry->d_name);
|
||||
} else {
|
||||
node = &stackNode;
|
||||
error = stackNode.SetTo(&directory, entry->d_name);
|
||||
}
|
||||
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
if (!isDirectory || !nonDirectoriesOnly) {
|
||||
error = node->WriteAttrString(kPackageFileAttribute, &value);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
}
|
||||
|
||||
// recurse
|
||||
if (isDirectory) {
|
||||
error = _TagPackageEntriesRecursively(stackDirectory, value,
|
||||
nonDirectoriesOnly);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -949,8 +1285,10 @@ private:
|
||||
BDirectory fOldStateDirectory;
|
||||
BString fOldStateDirectoryName;
|
||||
node_ref fTransactionDirectoryRef;
|
||||
BDirectory fWritableFilesDirectory;
|
||||
StringSet fAddedGroups;
|
||||
StringSet fAddedUsers;
|
||||
FSTransaction fFSTransaction;
|
||||
};
|
||||
|
||||
|
||||
@@ -1893,40 +2231,47 @@ Volume::_OpenPackagesSubDirectory(const RelativePath& path, bool create,
|
||||
BDirectory directory;
|
||||
status_t error = directory.SetTo(&fPackagesDirectoryRef);
|
||||
if (error != B_OK) {
|
||||
ERROR("Volume::_OpenConfigSubDirectory(): failed to open packages "
|
||||
ERROR("Volume::_OpenPackagesSubDirectory(): failed to open packages "
|
||||
"directory: %s\n", strerror(error));
|
||||
RETURN_ERROR(error);
|
||||
}
|
||||
|
||||
// get a string for the path
|
||||
BString pathString = path.ToString();
|
||||
if (pathString.IsEmpty())
|
||||
RETURN_ERROR(B_NO_MEMORY);
|
||||
return FSUtils::OpenSubDirectory(directory, path, create, _directory);
|
||||
}
|
||||
|
||||
// If creating is not allowed, just try to open it.
|
||||
if (!create)
|
||||
RETURN_ERROR(_directory.SetTo(&directory, pathString));
|
||||
|
||||
// get an absolute path and create the subdirectory
|
||||
BPath absolutePath;
|
||||
error = absolutePath.SetTo(&directory, pathString);
|
||||
if (error != B_OK) {
|
||||
ERROR("Volume::_OpenConfigSubDirectory(): failed to get absolute path "
|
||||
"for subdirectory \"%s\": %s\n", pathString.String(),
|
||||
strerror(error));
|
||||
RETURN_ERROR(error);
|
||||
status_t
|
||||
Volume::_OpenSettingsRootDirectory(BDirectory& _directory)
|
||||
{
|
||||
switch (fMountType) {
|
||||
case PACKAGE_FS_MOUNT_TYPE_SYSTEM:
|
||||
{
|
||||
// try our sibling volume
|
||||
if (fListener != NULL) {
|
||||
node_ref ref;
|
||||
status_t error = fListener->GetRootDirectoryRef(
|
||||
PACKAGE_FS_MOUNT_TYPE_COMMON, ref);
|
||||
if (error != B_ENTRY_NOT_FOUND)
|
||||
return error;
|
||||
return _directory.SetTo(&ref);
|
||||
}
|
||||
|
||||
// try a path relative to our root directory
|
||||
BDirectory rootDirectory;
|
||||
status_t error = rootDirectory.SetTo(&fRootDirectoryRef);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
return _directory.SetTo(&rootDirectory, "../common");
|
||||
}
|
||||
|
||||
case PACKAGE_FS_MOUNT_TYPE_COMMON:
|
||||
case PACKAGE_FS_MOUNT_TYPE_HOME:
|
||||
return _directory.SetTo(&fRootDirectoryRef);
|
||||
|
||||
case PACKAGE_FS_MOUNT_TYPE_CUSTOM:
|
||||
default:
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
error = create_directory(absolutePath.Path(),
|
||||
S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
|
||||
if (error != B_OK) {
|
||||
ERROR("Volume::_OpenConfigSubDirectory(): failed to create packages "
|
||||
"subdirectory \"%s\": %s\n", pathString.String(),
|
||||
strerror(error));
|
||||
RETURN_ERROR(error);
|
||||
}
|
||||
|
||||
RETURN_ERROR(_directory.SetTo(&directory, pathString));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <package/packagefs.h>
|
||||
#include <util/DoublyLinkedList.h>
|
||||
|
||||
#include "FSUtils.h"
|
||||
#include "Package.h"
|
||||
|
||||
|
||||
@@ -119,12 +120,11 @@ public:
|
||||
|
||||
private:
|
||||
struct NodeMonitorEvent;
|
||||
struct RelativePath;
|
||||
struct Exception;
|
||||
struct CommitTransactionHandler;
|
||||
|
||||
friend struct CommitTransactionHandler;
|
||||
|
||||
typedef FSUtils::RelativePath RelativePath;
|
||||
typedef DoublyLinkedList<NodeMonitorEvent> NodeMonitorEventList;
|
||||
|
||||
private:
|
||||
@@ -160,6 +160,9 @@ private:
|
||||
const RelativePath& path, bool create,
|
||||
BDirectory& _directory);
|
||||
|
||||
status_t _OpenSettingsRootDirectory(
|
||||
BDirectory& _directory);
|
||||
|
||||
status_t _CreateActivationFileContent(
|
||||
const PackageSet& toActivate,
|
||||
const PackageSet& toDeactivate,
|
||||
@@ -181,6 +184,11 @@ private:
|
||||
const PackageSet& packagesToDeactivate);
|
||||
// throws Exception
|
||||
|
||||
status_t _ExtractPackageContent(Package* package,
|
||||
const char* contentPath,
|
||||
BDirectory& targetDirectory,
|
||||
BDirectory& _extractedFilesDirectory);
|
||||
|
||||
private:
|
||||
BString fPath;
|
||||
PackageFSMountType fMountType;
|
||||
@@ -206,6 +214,9 @@ public:
|
||||
|
||||
virtual void VolumeNodeMonitorEventOccurred(Volume* volume)
|
||||
= 0;
|
||||
virtual status_t GetRootDirectoryRef(
|
||||
PackageFSMountType mountType,
|
||||
node_ref& _ref) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user