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, [email protected].
|
||||||
|
* 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 ;
|
SubDir HAIKU_TOP src servers package ;
|
||||||
|
|
||||||
UsePrivateSystemHeaders ;
|
UsePrivateSystemHeaders ;
|
||||||
UsePrivateHeaders app interface kernel shared ;
|
UsePrivateHeaders app interface kernel shared storage ;
|
||||||
|
|
||||||
Server package_daemon
|
Server package_daemon
|
||||||
:
|
:
|
||||||
DebugSupport.cpp
|
DebugSupport.cpp
|
||||||
|
Exception.cpp
|
||||||
|
FSTransaction.cpp
|
||||||
|
FSUtils.cpp
|
||||||
Job.cpp
|
Job.cpp
|
||||||
JobQueue.cpp
|
JobQueue.cpp
|
||||||
Package.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
|
void
|
||||||
Root::LastReferenceReleased()
|
Root::LastReferenceReleased()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -50,6 +50,9 @@ public:
|
|||||||
private:
|
private:
|
||||||
// Volume::Listener
|
// Volume::Listener
|
||||||
virtual void VolumeNodeMonitorEventOccurred(Volume* volume);
|
virtual void VolumeNodeMonitorEventOccurred(Volume* volume);
|
||||||
|
virtual status_t GetRootDirectoryRef(
|
||||||
|
PackageFSMountType mountType,
|
||||||
|
node_ref& _ref) ;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void LastReferenceReleased();
|
virtual void LastReferenceReleased();
|
||||||
|
|||||||
+582
-237
@@ -36,10 +36,15 @@
|
|||||||
|
|
||||||
#include <AutoDeleter.h>
|
#include <AutoDeleter.h>
|
||||||
#include <AutoLocker.h>
|
#include <AutoLocker.h>
|
||||||
|
#include <CopyEngine.h>
|
||||||
|
#include <NotOwningEntryRef.h>
|
||||||
#include <package/DaemonDefs.h>
|
#include <package/DaemonDefs.h>
|
||||||
#include <package/PackagesDirectoryDefs.h>
|
#include <package/PackagesDirectoryDefs.h>
|
||||||
|
#include <RemoveEngine.h>
|
||||||
|
|
||||||
#include "DebugSupport.h"
|
#include "DebugSupport.h"
|
||||||
|
#include "Exception.h"
|
||||||
|
#include "FSTransaction.h"
|
||||||
|
|
||||||
|
|
||||||
using namespace BPackageKit::BPrivate;
|
using namespace BPackageKit::BPrivate;
|
||||||
@@ -54,14 +59,14 @@ static const char* const kActivationFileName
|
|||||||
= PACKAGES_DIRECTORY_ACTIVATION_FILE;
|
= PACKAGES_DIRECTORY_ACTIVATION_FILE;
|
||||||
static const char* const kTemporaryActivationFileName
|
static const char* const kTemporaryActivationFileName
|
||||||
= PACKAGES_DIRECTORY_ACTIVATION_FILE ".tmp";
|
= 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 kHandleNodeMonitorEvents = 'nmon';
|
||||||
|
|
||||||
static const bigtime_t kNodeMonitorEventHandlingDelay = 500000;
|
static const bigtime_t kNodeMonitorEventHandlingDelay = 500000;
|
||||||
static const bigtime_t kCommunicationTimeout = 1000000;
|
static const bigtime_t kCommunicationTimeout = 1000000;
|
||||||
|
|
||||||
const char* const kShellEscapeCharacters = " ~`#$&*()\\|[]{};'\"<>?!";
|
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark - Listener
|
// #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
|
// #pragma mark - CommitTransactionHandler
|
||||||
|
|
||||||
|
|
||||||
@@ -269,7 +121,8 @@ struct Volume::CommitTransactionHandler {
|
|||||||
fPackagesAlreadyAdded(packagesAlreadyAdded),
|
fPackagesAlreadyAdded(packagesAlreadyAdded),
|
||||||
fPackagesAlreadyRemoved(packagesAlreadyRemoved),
|
fPackagesAlreadyRemoved(packagesAlreadyRemoved),
|
||||||
fAddedGroups(),
|
fAddedGroups(),
|
||||||
fAddedUsers()
|
fAddedUsers(),
|
||||||
|
fFSTransaction()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,8 +192,9 @@ struct Volume::CommitTransactionHandler {
|
|||||||
// revert user and group changes
|
// revert user and group changes
|
||||||
_RevertUserGroupChanges();
|
_RevertUserGroupChanges();
|
||||||
|
|
||||||
// remove old state directory
|
// Revert all other FS operations, i.e. the writable files changes as
|
||||||
_RemoveOldStateDirectory();
|
// well as the creation of the old state directory.
|
||||||
|
fFSTransaction.RollBack();
|
||||||
}
|
}
|
||||||
|
|
||||||
const BString& OldStateDirectoryName() const
|
const BString& OldStateDirectoryName() const
|
||||||
@@ -510,11 +364,16 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create the directory
|
// create the directory
|
||||||
|
FSTransaction::CreateOperation createOldStateDirectoryOperation(
|
||||||
|
&fFSTransaction, FSUtils::Entry(adminDirectory, directoryName));
|
||||||
|
|
||||||
error = adminDirectory.CreateDirectory(directoryName,
|
error = adminDirectory.CreateDirectory(directoryName,
|
||||||
&fOldStateDirectory);
|
&fOldStateDirectory);
|
||||||
if (error != B_OK)
|
if (error != B_OK)
|
||||||
throw Exception(error, "failed to create old state directory");
|
throw Exception(error, "failed to create old state directory");
|
||||||
|
|
||||||
|
createOldStateDirectoryOperation.Finished();
|
||||||
|
|
||||||
fOldStateDirectoryName = directoryName;
|
fOldStateDirectoryName = directoryName;
|
||||||
|
|
||||||
// write the old activation file
|
// write the old activation file
|
||||||
@@ -548,11 +407,8 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get a BEntry for the package
|
// get a BEntry for the package
|
||||||
entry_ref entryRef;
|
NotOwningEntryRef entryRef(fVolume->fPackagesDirectoryRef,
|
||||||
entryRef.device = fVolume->fPackagesDirectoryRef.device;
|
package->FileName());
|
||||||
entryRef.directory = fVolume->fPackagesDirectoryRef.node;
|
|
||||||
if (entryRef.set_name(package->FileName()) != B_OK)
|
|
||||||
throw Exception(B_NO_MEMORY);
|
|
||||||
|
|
||||||
BEntry entry;
|
BEntry entry;
|
||||||
status_t error = entry.SetTo(&entryRef);
|
status_t error = entry.SetTo(&entryRef);
|
||||||
@@ -665,7 +521,7 @@ private:
|
|||||||
fAddedGroups.insert(groupName.String());
|
fAddedGroups.insert(groupName.String());
|
||||||
|
|
||||||
std::string commandLine("groupadd ");
|
std::string commandLine("groupadd ");
|
||||||
commandLine += _ShellEscapeString(groupName).String();
|
commandLine += FSUtils::ShellEscapeString(groupName).String();
|
||||||
|
|
||||||
if (system(commandLine.c_str()) != 0) {
|
if (system(commandLine.c_str()) != 0) {
|
||||||
fAddedGroups.erase(groupName.String());
|
fAddedGroups.erase(groupName.String());
|
||||||
@@ -694,25 +550,26 @@ private:
|
|||||||
|
|
||||||
if (!user.RealName().IsEmpty()) {
|
if (!user.RealName().IsEmpty()) {
|
||||||
commandLine += std::string("-n ")
|
commandLine += std::string("-n ")
|
||||||
+ _ShellEscapeString(user.RealName()).String() + " ";
|
+ FSUtils::ShellEscapeString(user.RealName()).String() + " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!user.Home().IsEmpty()) {
|
if (!user.Home().IsEmpty()) {
|
||||||
commandLine += std::string("-d ")
|
commandLine += std::string("-d ")
|
||||||
+ _ShellEscapeString(user.Home()).String() + " ";
|
+ FSUtils::ShellEscapeString(user.Home()).String() + " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!user.Shell().IsEmpty()) {
|
if (!user.Shell().IsEmpty()) {
|
||||||
commandLine += std::string("-s ")
|
commandLine += std::string("-s ")
|
||||||
+ _ShellEscapeString(user.Shell()).String() + " ";
|
+ FSUtils::ShellEscapeString(user.Shell()).String() + " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!user.Groups().IsEmpty()) {
|
if (!user.Groups().IsEmpty()) {
|
||||||
commandLine += std::string("-g ")
|
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) {
|
if (system(commandLine.c_str()) != 0) {
|
||||||
fAddedUsers.erase(user.Name().String());
|
fAddedUsers.erase(user.Name().String());
|
||||||
@@ -726,9 +583,10 @@ private:
|
|||||||
int32 groupCount = user.Groups().CountStrings();
|
int32 groupCount = user.Groups().CountStrings();
|
||||||
for (int32 i = 1; i < groupCount; i++) {
|
for (int32 i = 1; i < groupCount; i++) {
|
||||||
commandLine = std::string("groupmod -A ")
|
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) {
|
if (system(commandLine.c_str()) != 0) {
|
||||||
fAddedUsers.erase(user.Name().String());
|
fAddedUsers.erase(user.Name().String());
|
||||||
throw Exception(error,
|
throw Exception(error,
|
||||||
@@ -743,7 +601,346 @@ private:
|
|||||||
void _AddGlobalWritableFile(Package* package,
|
void _AddGlobalWritableFile(Package* package,
|
||||||
const BGlobalWritableFileInfo& file)
|
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()
|
void _RevertAddPackagesToActivate()
|
||||||
@@ -774,13 +971,8 @@ private:
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// get BEntry for the package
|
// get BEntry for the package
|
||||||
entry_ref entryRef;
|
NotOwningEntryRef entryRef(fVolume->fPackagesDirectoryRef,
|
||||||
entryRef.device = fVolume->fPackagesDirectoryRef.device;
|
package->FileName());
|
||||||
entryRef.directory = fVolume->fPackagesDirectoryRef.node;
|
|
||||||
if (entryRef.set_name(package->FileName()) != B_OK) {
|
|
||||||
ERROR("out of memory\n");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
BEntry entry;
|
BEntry entry;
|
||||||
error = entry.SetTo(&entryRef);
|
error = entry.SetTo(&entryRef);
|
||||||
@@ -852,7 +1044,7 @@ private:
|
|||||||
for (StringSet::const_iterator it = fAddedUsers.begin();
|
for (StringSet::const_iterator it = fAddedUsers.begin();
|
||||||
it != fAddedUsers.end(); ++it) {
|
it != fAddedUsers.end(); ++it) {
|
||||||
std::string commandLine("userdel ");
|
std::string commandLine("userdel ");
|
||||||
commandLine += _ShellEscapeString(it->c_str()).String();
|
commandLine += FSUtils::ShellEscapeString(it->c_str()).String();
|
||||||
if (system(commandLine.c_str()) != 0)
|
if (system(commandLine.c_str()) != 0)
|
||||||
ERROR("failed to remove user \"%s\"\n", it->c_str());
|
ERROR("failed to remove user \"%s\"\n", it->c_str());
|
||||||
}
|
}
|
||||||
@@ -861,39 +1053,12 @@ private:
|
|||||||
for (StringSet::const_iterator it = fAddedGroups.begin();
|
for (StringSet::const_iterator it = fAddedGroups.begin();
|
||||||
it != fAddedGroups.end(); ++it) {
|
it != fAddedGroups.end(); ++it) {
|
||||||
std::string commandLine("groupdel ");
|
std::string commandLine("groupdel ");
|
||||||
commandLine += _ShellEscapeString(it->c_str()).String();
|
commandLine += FSUtils::ShellEscapeString(it->c_str()).String();
|
||||||
if (system(commandLine.c_str()) != 0)
|
if (system(commandLine.c_str()) != 0)
|
||||||
ERROR("failed to remove group \"%s\"\n", it->c_str());
|
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()
|
void _RunPostInstallScripts()
|
||||||
{
|
{
|
||||||
for (PackageSet::iterator it = fAddedPackages.begin();
|
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);
|
BString path = entry.Path();
|
||||||
result.CharacterEscape(kShellEscapeCharacters, '\\');
|
return path.IsEmpty() ? fallback : path;
|
||||||
if (result.IsEmpty())
|
}
|
||||||
|
|
||||||
|
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();
|
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:
|
private:
|
||||||
@@ -949,8 +1285,10 @@ private:
|
|||||||
BDirectory fOldStateDirectory;
|
BDirectory fOldStateDirectory;
|
||||||
BString fOldStateDirectoryName;
|
BString fOldStateDirectoryName;
|
||||||
node_ref fTransactionDirectoryRef;
|
node_ref fTransactionDirectoryRef;
|
||||||
|
BDirectory fWritableFilesDirectory;
|
||||||
StringSet fAddedGroups;
|
StringSet fAddedGroups;
|
||||||
StringSet fAddedUsers;
|
StringSet fAddedUsers;
|
||||||
|
FSTransaction fFSTransaction;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -1893,40 +2231,47 @@ Volume::_OpenPackagesSubDirectory(const RelativePath& path, bool create,
|
|||||||
BDirectory directory;
|
BDirectory directory;
|
||||||
status_t error = directory.SetTo(&fPackagesDirectoryRef);
|
status_t error = directory.SetTo(&fPackagesDirectoryRef);
|
||||||
if (error != B_OK) {
|
if (error != B_OK) {
|
||||||
ERROR("Volume::_OpenConfigSubDirectory(): failed to open packages "
|
ERROR("Volume::_OpenPackagesSubDirectory(): failed to open packages "
|
||||||
"directory: %s\n", strerror(error));
|
"directory: %s\n", strerror(error));
|
||||||
RETURN_ERROR(error);
|
RETURN_ERROR(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// get a string for the path
|
return FSUtils::OpenSubDirectory(directory, path, create, _directory);
|
||||||
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(&directory, pathString));
|
|
||||||
|
|
||||||
// get an absolute path and create the subdirectory
|
status_t
|
||||||
BPath absolutePath;
|
Volume::_OpenSettingsRootDirectory(BDirectory& _directory)
|
||||||
error = absolutePath.SetTo(&directory, pathString);
|
{
|
||||||
if (error != B_OK) {
|
switch (fMountType) {
|
||||||
ERROR("Volume::_OpenConfigSubDirectory(): failed to get absolute path "
|
case PACKAGE_FS_MOUNT_TYPE_SYSTEM:
|
||||||
"for subdirectory \"%s\": %s\n", pathString.String(),
|
{
|
||||||
strerror(error));
|
// try our sibling volume
|
||||||
RETURN_ERROR(error);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
error = create_directory(absolutePath.Path(),
|
// try a path relative to our root directory
|
||||||
S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
|
BDirectory rootDirectory;
|
||||||
if (error != B_OK) {
|
status_t error = rootDirectory.SetTo(&fRootDirectoryRef);
|
||||||
ERROR("Volume::_OpenConfigSubDirectory(): failed to create packages "
|
if (error != B_OK)
|
||||||
"subdirectory \"%s\": %s\n", pathString.String(),
|
return error;
|
||||||
strerror(error));
|
return _directory.SetTo(&rootDirectory, "../common");
|
||||||
RETURN_ERROR(error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RETURN_ERROR(_directory.SetTo(&directory, pathString));
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include <package/packagefs.h>
|
#include <package/packagefs.h>
|
||||||
#include <util/DoublyLinkedList.h>
|
#include <util/DoublyLinkedList.h>
|
||||||
|
|
||||||
|
#include "FSUtils.h"
|
||||||
#include "Package.h"
|
#include "Package.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -119,12 +120,11 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
struct NodeMonitorEvent;
|
struct NodeMonitorEvent;
|
||||||
struct RelativePath;
|
|
||||||
struct Exception;
|
|
||||||
struct CommitTransactionHandler;
|
struct CommitTransactionHandler;
|
||||||
|
|
||||||
friend struct CommitTransactionHandler;
|
friend struct CommitTransactionHandler;
|
||||||
|
|
||||||
|
typedef FSUtils::RelativePath RelativePath;
|
||||||
typedef DoublyLinkedList<NodeMonitorEvent> NodeMonitorEventList;
|
typedef DoublyLinkedList<NodeMonitorEvent> NodeMonitorEventList;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -160,6 +160,9 @@ private:
|
|||||||
const RelativePath& path, bool create,
|
const RelativePath& path, bool create,
|
||||||
BDirectory& _directory);
|
BDirectory& _directory);
|
||||||
|
|
||||||
|
status_t _OpenSettingsRootDirectory(
|
||||||
|
BDirectory& _directory);
|
||||||
|
|
||||||
status_t _CreateActivationFileContent(
|
status_t _CreateActivationFileContent(
|
||||||
const PackageSet& toActivate,
|
const PackageSet& toActivate,
|
||||||
const PackageSet& toDeactivate,
|
const PackageSet& toDeactivate,
|
||||||
@@ -181,6 +184,11 @@ private:
|
|||||||
const PackageSet& packagesToDeactivate);
|
const PackageSet& packagesToDeactivate);
|
||||||
// throws Exception
|
// throws Exception
|
||||||
|
|
||||||
|
status_t _ExtractPackageContent(Package* package,
|
||||||
|
const char* contentPath,
|
||||||
|
BDirectory& targetDirectory,
|
||||||
|
BDirectory& _extractedFilesDirectory);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BString fPath;
|
BString fPath;
|
||||||
PackageFSMountType fMountType;
|
PackageFSMountType fMountType;
|
||||||
@@ -206,6 +214,9 @@ public:
|
|||||||
|
|
||||||
virtual void VolumeNodeMonitorEventOccurred(Volume* volume)
|
virtual void VolumeNodeMonitorEventOccurred(Volume* volume)
|
||||||
= 0;
|
= 0;
|
||||||
|
virtual status_t GetRootDirectoryRef(
|
||||||
|
PackageFSMountType mountType,
|
||||||
|
node_ref& _ref) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user