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:
Ingo Weinhold
2013-09-27 00:51:30 +02:00
parent 99f1939fd8
commit 23733521a7
11 changed files with 1560 additions and 243 deletions
+63
View File
@@ -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;
}
+38
View File
@@ -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
+232
View File
@@ -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;
}
+132
View File
@@ -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
+248
View File
@@ -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);
}
+229
View File
@@ -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
+4 -1
View File
@@ -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
+13
View File
@@ -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()
{
+3
View File
@@ -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
View File
@@ -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));
}
+13 -2
View File
@@ -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;
};