haiku/src/apps/packageinstaller/PackageItem.cpp

1156 lines
27 KiB
C++

/*
* Copyright 2007-2009, Haiku, Inc.
* Distributed under the terms of the MIT license.
*
* Author:
* Łukasz 'Sil2100' Zemczak <sil2100@vexillium.org>
*/
#include "PackageItem.h"
#include <stdlib.h>
#include <string.h>
#include <Alert.h>
#include <ByteOrder.h>
#include <Catalog.h>
#include <Directory.h>
#include <FindDirectory.h>
#include <fs_info.h>
#include <Locale.h>
#include <NodeInfo.h>
#include <OS.h>
#include <SymLink.h>
#include <Volume.h>
#include "zlib.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "PackageItem"
enum {
P_CHUNK_SIZE = 256
};
static const uint32 kDefaultMode = 0777;
static const uint8 padding[7] = { 0, 0, 0, 0, 0, 0, 0 };
extern bool gVerbose;
enum {
P_DATA = 0,
P_ATTRIBUTE
};
status_t
inflate_data(uint8 *in, uint32 inSize, uint8 *out, uint32 outSize)
{
parser_debug("inflate_data() called - input_size: %ld, output_size: %ld\n",
inSize, outSize);
z_stream stream;
stream.zalloc = Z_NULL;
stream.zfree = Z_NULL;
stream.opaque = Z_NULL;
stream.avail_in = inSize;
stream.next_in = in;
status_t ret;
ret = inflateInit(&stream);
if (ret != Z_OK) {
parser_debug("inflatInit failed\n");
return B_ERROR;
}
stream.avail_out = outSize;
stream.next_out = out;
ret = inflate(&stream, Z_NO_FLUSH);
if (ret != Z_STREAM_END) {
// Uncompressed file size in package info corrupted
parser_debug("Left: %d\n", stream.avail_out);
return B_ERROR;
}
inflateEnd(&stream);
return B_OK;
}
static inline int
inflate_file_to_file(BFile *in, uint64 in_size, BFile *out, uint64 out_size)
{
z_stream stream;
stream.zalloc = Z_NULL;
stream.zfree = Z_NULL;
stream.opaque = Z_NULL;
stream.avail_in = 0;
stream.next_in = Z_NULL;
status_t ret;
uint8 buffer_out[P_CHUNK_SIZE], buffer_in[P_CHUNK_SIZE];
uint64 bytes_read = 0, read = P_CHUNK_SIZE, write = 0;
ret = inflateInit(&stream);
if (ret != Z_OK) {
parser_debug("inflate_file_to_file: inflateInit failed\n");
return B_ERROR;
}
do {
bytes_read += P_CHUNK_SIZE;
if (bytes_read > in_size) {
read = in_size - (bytes_read - P_CHUNK_SIZE);
bytes_read = in_size;
}
stream.avail_in = in->Read(buffer_in, read);
if (stream.avail_in != read) {
parser_debug("inflate_file_to_file: read failed\n");
(void)inflateEnd(&stream);
return B_ERROR;
}
stream.next_in = buffer_in;
do {
stream.avail_out = P_CHUNK_SIZE;
stream.next_out = buffer_out;
ret = inflate(&stream, Z_NO_FLUSH);
if (ret != Z_OK && ret != Z_STREAM_END && ret != Z_BUF_ERROR) {
parser_debug("inflate_file_to_file: inflate failed with '%s'\n",
stream.msg);
(void)inflateEnd(&stream);
return B_ERROR;
}
write = P_CHUNK_SIZE - stream.avail_out;
if (static_cast<uint64>(out->Write(buffer_out, write)) != write) {
parser_debug("inflate_file_to_file: write failed\n");
(void)inflateEnd(&stream);
return B_ERROR;
}
} while (stream.avail_out == 0);
} while (bytes_read != in_size);
(void)inflateEnd(&stream);
return B_OK;
}
// #pragma mark - PackageItem
PackageItem::PackageItem(BFile* parent, const BString& path, uint8 type,
uint32 ctime, uint32 mtime, uint64 offset, uint64 size)
{
SetTo(parent, path, type, ctime, mtime, offset, size);
}
PackageItem::~PackageItem()
{
}
void
PackageItem::SetTo(BFile* parent, const BString& path, uint8 type, uint32 ctime,
uint32 mtime, uint64 offset, uint64 size)
{
fPackage = parent;
fPath = path;
fOffset = offset;
fSize = size;
fPathType = type;
fCreationTime = ctime;
fModificationTime = mtime;
}
status_t
PackageItem::InitPath(const char* path, BPath* destination)
{
status_t ret = B_OK;
if (fPathType == P_INSTALL_PATH) {
if (gVerbose)
printf("InitPath - relative: %s + %s\n", path, fPath.String());
if (path == NULL) {
parser_debug("InitPath path is NULL\n");
return B_ERROR;
}
ret = destination->SetTo(path, fPath.String());
} else if (fPathType == P_SYSTEM_PATH) {
if (gVerbose)
printf("InitPath - absolute: %s\n", fPath.String());
if (fPath == "")
fPath = "/";
ret = destination->SetTo(fPath.String());
} else {
if (gVerbose)
printf("InitPath - volume: %s + %s\n", path, fPath.String());
if (path == NULL) {
parser_debug("InitPath path is NULL\n");
return B_ERROR;
}
BVolume volume(dev_for_path(path));
ret = volume.InitCheck();
if (ret == B_OK) {
BDirectory temp;
ret = volume.GetRootDirectory(&temp);
if (ret == B_OK) {
BPath mountPoint(&temp, NULL);
ret = destination->SetTo(mountPoint.Path(), fPath.String());
}
}
}
if (ret != B_OK) {
fprintf(stderr, "InitPath(%s): %s\n", path, strerror(ret));
return ret;
}
BString pathString(destination->Path());
// Hardcoded paths, the .pkg files hardcode this to the same
if (pathString.FindFirst("non-packaged") < 0) {
bool wasRewritten = false;
if (pathString.StartsWith("/boot/beos/system")) {
BPath systemNonPackagedDir;
find_directory(B_SYSTEM_NONPACKAGED_DIRECTORY,
&systemNonPackagedDir);
pathString.ReplaceFirst("/boot/beos/system",
systemNonPackagedDir.Path());
wasRewritten = true;
} else if (pathString.StartsWith("/boot/system")) {
BPath systemNonPackagedDir;
find_directory(B_SYSTEM_NONPACKAGED_DIRECTORY,
&systemNonPackagedDir);
pathString.ReplaceFirst("/boot/system",
systemNonPackagedDir.Path());
wasRewritten = true;
} else if (pathString.StartsWith("/boot/home/config")) {
BPath userNonPackagedDir;
find_directory(B_USER_NONPACKAGED_DIRECTORY, &userNonPackagedDir);
pathString.ReplaceFirst("/boot/home/config",
userNonPackagedDir.Path());
wasRewritten = true;
}
if (wasRewritten) {
if (gVerbose)
printf("rewritten: %s\n", pathString.String());
destination->SetTo(pathString.String());
}
}
return ret;
}
status_t
PackageItem::HandleAttributes(BPath *destination, BNode *node,
const char *header)
{
status_t ret = B_OK;
BVolume volume(dev_for_path(destination->Path()));
if (volume.KnowsAttr()) {
parser_debug("We have an offset\n");
if (!fPackage)
return B_ERROR;
ret = fPackage->InitCheck();
if (ret != B_OK)
return ret;
// We need to parse the data section now
fPackage->Seek(fOffset, SEEK_SET);
uint8 buffer[7];
if (fPackage->Read(buffer, 7) != 7 || memcmp(buffer, header, 5))
return B_ERROR;
parser_debug("Header validated!\n");
char *attrName = 0;
uint32 nameSize = 0;
uint8 *attrData = new uint8[P_CHUNK_SIZE];
uint64 dataSize = P_CHUNK_SIZE;
uint8 *temp = new uint8[P_CHUNK_SIZE];
uint64 tempSize = P_CHUNK_SIZE;
uint64 attrCSize = 0, attrOSize = 0;
uint32 attrType = 0; // type_code type
bool attrStarted = false, done = false;
while (fPackage->Read(buffer, 7) == 7) {
if (!memcmp(buffer, "FBeA", 5))
continue;
ret = ParseAttribute(buffer, node, &attrName, &nameSize, &attrType,
&attrData, &dataSize, &temp, &tempSize, &attrCSize, &attrOSize,
&attrStarted, &done);
if (ret != B_OK || done) {
if (ret != B_OK) {
parser_debug("_ParseAttribute failed for %s\n",
destination->Path());
}
break;
}
}
delete[] attrData;
delete[] temp;
}
return ret;
}
status_t
PackageItem::ParseAttribute(uint8* buffer, BNode* node, char** attrName,
uint32* nameSize, uint32* attrType, uint8** attrData, uint64* dataSize,
uint8** temp, uint64* tempSize, uint64* attrCSize, uint64* attrOSize,
bool* attrStarted, bool* done)
{
status_t ret = B_OK;
uint32 length;
if (!memcmp(buffer, "BeAI", 5)) {
parser_debug(" Attribute started.\n");
if (*attrName)
*attrName[0] = 0;
*attrCSize = 0;
*attrOSize = 0;
*attrStarted = true;
} else if (!memcmp(buffer, "BeAN", 5)) {
if (!*attrStarted) {
ret = B_ERROR;
return ret;
}
parser_debug(" BeAN.\n");
fPackage->Read(&length, 4);
swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
B_SWAP_BENDIAN_TO_HOST);
if (*nameSize < (length + 1)) {
delete[] *attrName;
*nameSize = length + 1;
*attrName = new char[*nameSize];
}
fPackage->Read(*attrName, length);
(*attrName)[length] = 0;
parser_debug(" (%ld) = %s\n", length, *attrName);
} else if (!memcmp(buffer, "BeAT", 5)) {
if (!*attrStarted) {
ret = B_ERROR;
return ret;
}
parser_debug(" BeAT.\n");
fPackage->Read(attrType, 4);
swap_data(B_UINT32_TYPE, attrType, sizeof(*attrType),
B_SWAP_BENDIAN_TO_HOST);
} else if (!memcmp(buffer, "BeAD", 5)) {
if (!*attrStarted) {
ret = B_ERROR;
return ret;
}
parser_debug(" BeAD.\n");
fPackage->Read(attrCSize, 8);
swap_data(B_UINT64_TYPE, attrCSize, sizeof(*attrCSize),
B_SWAP_BENDIAN_TO_HOST);
fPackage->Read(attrOSize, 8);
swap_data(B_UINT64_TYPE, attrOSize, sizeof(*attrOSize),
B_SWAP_BENDIAN_TO_HOST);
fPackage->Seek(4, SEEK_CUR); // TODO: Check what this means
if (*tempSize < *attrCSize) {
delete[] *temp;
*tempSize = *attrCSize;
*temp = new uint8[*tempSize];
}
if (*dataSize < *attrOSize) {
delete[] *attrData;
*dataSize = *attrOSize;
*attrData = new uint8[*dataSize];
}
if (fPackage->Read(*temp, *attrCSize)
!= static_cast<ssize_t>(*attrCSize)) {
ret = B_ERROR;
return ret;
}
parser_debug(" Data read successfuly. Inflating!\n");
ret = inflate_data(*temp, *tempSize, *attrData, *dataSize);
if (ret != B_OK)
return ret;
} else if (!memcmp(buffer, padding, 7)) {
if (!*attrStarted) {
*done = true;
return ret;
}
parser_debug(" Padding.\n");
ssize_t wrote = node->WriteAttr(*attrName, *attrType, 0, *attrData,
*attrOSize);
if (wrote != static_cast<ssize_t>(*attrOSize)) {
parser_debug("Failed to write attribute %s %s\n", *attrName, strerror(wrote));
return B_ERROR;
}
*attrStarted = false;
if (*attrName)
*attrName[0] = 0;
*attrCSize = 0;
*attrOSize = 0;
parser_debug(" > Attribute added.\n");
} else {
parser_debug(" Unknown attribute\n");
ret = B_ERROR;
}
return ret;
}
status_t
PackageItem::SkipAttribute(uint8* buffer, bool* attrStarted, bool* done)
{
status_t ret = B_OK;
uint32 length;
if (!memcmp(buffer, "BeAI", 5)) {
parser_debug(" Attribute started.\n");
*attrStarted = true;
} else if (!memcmp(buffer, "BeAN", 5)) {
if (!*attrStarted) {
ret = B_ERROR;
return ret;
}
parser_debug(" BeAN.\n");
fPackage->Read(&length, 4);
swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
B_SWAP_BENDIAN_TO_HOST);
fPackage->Seek(length, SEEK_CUR);
} else if (!memcmp(buffer, "BeAT", 5)) {
if (!*attrStarted) {
ret = B_ERROR;
return ret;
}
parser_debug(" BeAT.\n");
fPackage->Seek(4, SEEK_CUR);
} else if (!memcmp(buffer, "BeAD", 5)) {
if (!*attrStarted) {
ret = B_ERROR;
return ret;
}
parser_debug(" BeAD.\n");
uint64 length64;
fPackage->Read(&length64, 8);
swap_data(B_UINT64_TYPE, &length64, sizeof(length64),
B_SWAP_BENDIAN_TO_HOST);
fPackage->Seek(12 + length64, SEEK_CUR);
parser_debug(" Data skipped successfuly.\n");
} else if (!memcmp(buffer, padding, 7)) {
if (!*attrStarted) {
*done = true;
return ret;
}
parser_debug(" Padding.\n");
*attrStarted = false;
parser_debug(" > Attribute skipped.\n");
} else {
parser_debug(" Unknown attribute\n");
ret = B_ERROR;
}
return ret;
}
status_t
PackageItem::ParseData(uint8* buffer, BFile* file, uint64 originalSize,
bool *done)
{
status_t ret = B_OK;
if (!memcmp(buffer, "FiMF", 5)) {
parser_debug(" Found file data.\n");
uint64 compressed, original;
fPackage->Read(&compressed, 8);
swap_data(B_UINT64_TYPE, &compressed, sizeof(uint64),
B_SWAP_BENDIAN_TO_HOST);
fPackage->Read(&original, 8);
swap_data(B_UINT64_TYPE, &original, sizeof(uint64),
B_SWAP_BENDIAN_TO_HOST);
parser_debug(" Still good... (%llu : %llu)\n", original,
originalSize);
if (original != originalSize) {
parser_debug(" File size mismatch\n");
return B_ERROR; // File size mismatch
}
parser_debug(" Still good...\n");
if (fPackage->Read(buffer, 4) != 4) {
parser_debug(" Read(buffer, 4) failed\n");
return B_ERROR;
}
parser_debug(" Still good...\n");
ret = inflate_file_to_file(fPackage, compressed, file, original);
if (ret != B_OK) {
parser_debug(" inflate_file_to_file failed\n");
return ret;
}
parser_debug(" File data inflation complete!\n");
} else if (!memcmp(buffer, padding, 7)) {
*done = true;
return ret;
} else {
parser_debug("_ParseData unknown tag\n");
ret = B_ERROR;
}
return ret;
}
// #pragma mark - PackageScript
PackageScript::PackageScript(BFile* parent, const BString& path, uint8 type,
uint64 offset, uint64 size, uint64 originalSize)
:
PackageItem(parent, path, type, 0, 0, offset, size),
fOriginalSize(originalSize),
fThreadId(-1)
{
}
status_t
PackageScript::DoInstall(const char* path, ItemState* state)
{
status_t ret = B_OK;
parser_debug("Script: DoInstall() called!\n");
if (fOffset) {
parser_debug("We have an offset\n");
if (!fPackage)
return B_ERROR;
ret = fPackage->InitCheck();
if (ret != B_OK)
return ret;
// We need to parse the data section now
fPackage->Seek(fOffset, SEEK_SET);
uint8 buffer[7];
bool attrStarted = false, done = false;
uint8 section = P_ATTRIBUTE;
while (fPackage->Read(buffer, 7) == 7) {
if (!memcmp(buffer, "FBeA", 5)) {
parser_debug("-> Attribute\n");
section = P_ATTRIBUTE;
continue;
} else if (!memcmp(buffer, "FiDa", 5)) {
parser_debug("-> File data\n");
section = P_DATA;
continue;
}
switch (section) {
case P_ATTRIBUTE:
ret = SkipAttribute(buffer, &attrStarted, &done);
break;
case P_DATA:
{
BString script;
ret = _ParseScript(buffer, fOriginalSize, script, &done);
if (ret == B_OK) {
// Rewrite Deskbar entry targets. NOTE: It would
// also work to Replace("/config/be", "/config...")
// but it would be less save. For example, an app
// could have a folder named "config/be..." inside
// its installation folder.
// TODO: Use find_paths() or we are no better than
// these scripts.
script.ReplaceAll(
"/boot/beos/system/",
"/boot/system/");
script.ReplaceAll(
"~/config/be",
"~/config/settings/deskbar/menu");
script.ReplaceAll(
"/boot/home/config/be",
"/boot/home/config/settings/deskbar/menu");
// Rewrite all sorts of other old BeOS paths
script.ReplaceAll(
"/boot/preferences",
"/boot/system/preferences");
script.ReplaceAll(
"/boot/apps",
"/boot/system/non-packaged/apps");
script.ReplaceAll(
"~/config/add-ons/Screen\\ Savers",
"~/config/non-packaged/add-ons/Screen\\ Savers");
// TODO: More. These should also be put into a
// common source location, since it can also be used
// for the retargetting of install file locations.
// Packages seem to declare which system paths they
// use, and then package items can reference one of
// those global paths by index. A more elegent solution
// compared to what happens now in InitPath() would be
// to replace those global package paths. Or maybe
// that's more fragile... but a common source for
// the rewriting of BeOS paths is needed.
if (gVerbose)
printf("%s\n", script.String());
BPath workingDirectory;
if (path != NULL)
ret = InitPath(path, &workingDirectory);
else
ret = workingDirectory.SetTo(".");
if (ret == B_OK)
ret = _RunScript(workingDirectory.Path(), script);
}
break;
}
default:
return B_ERROR;
}
if (ret != B_OK || done)
break;
}
}
parser_debug("Ret: %ld %s\n", ret, strerror(ret));
return ret;
}
const uint32
PackageScript::ItemKind()
{
return P_KIND_SCRIPT;
}
status_t
PackageScript::_ParseScript(uint8 *buffer, uint64 originalSize,
BString& _script, bool *done)
{
status_t ret = B_OK;
if (!memcmp(buffer, "FiMF", 5)) {
parser_debug(" Found file (script) data.\n");
uint64 compressed, original;
fPackage->Read(&compressed, 8);
swap_data(B_UINT64_TYPE, &compressed, sizeof(uint64),
B_SWAP_BENDIAN_TO_HOST);
fPackage->Read(&original, 8);
swap_data(B_UINT64_TYPE, &original, sizeof(uint64),
B_SWAP_BENDIAN_TO_HOST);
parser_debug(" Still good... (%llu : %llu)\n", original,
originalSize);
if (original != originalSize) {
parser_debug(" File size mismatch\n");
return B_ERROR; // File size mismatch
}
parser_debug(" Still good...\n");
if (fPackage->Read(buffer, 4) != 4) {
parser_debug(" Read(buffer, 4) failed\n");
return B_ERROR;
}
parser_debug(" Still good...\n");
uint8 *temp = new uint8[compressed];
if (fPackage->Read(temp, compressed) != (int64)compressed) {
parser_debug(" Read(temp, compressed) failed\n");
delete[] temp;
return B_ERROR;
}
uint8* script = new uint8[original];
ret = inflate_data(temp, compressed, script, original);
if (ret != B_OK) {
parser_debug(" inflate_data failed\n");
delete[] temp;
delete[] script;
return ret;
}
_script.SetTo((char*)script, originalSize);
delete[] script;
delete[] temp;
parser_debug(" Script data inflation complete!\n");
} else if (!memcmp(buffer, padding, 7)) {
*done = true;
return ret;
} else {
parser_debug("_ParseData unknown tag\n");
ret = B_ERROR;
}
return ret;
}
status_t
PackageScript::_RunScript(const char* workingDirectory, const BString& script)
{
// This function written by Peter Folk <pfolk@uni.uiuc.edu>
// and published in the BeDevTalk FAQ, modified for use in the
// PackageInstaller
// http://www.abisoft.com/faq/BeDevTalk_FAQ.html#FAQ-209
// Change current working directory to install path
char oldWorkingDirectory[B_PATH_NAME_LENGTH];
getcwd(oldWorkingDirectory, sizeof(oldWorkingDirectory));
chdir(workingDirectory);
// Save current FDs
int old_in = dup(0);
int old_out = dup(1);
int old_err = dup(2);
int filedes[2];
/* Create new pipe FDs as stdin, stdout, stderr */
pipe(filedes); dup2(filedes[0], 0); close(filedes[0]);
int in = filedes[1]; // Write to in, appears on cmd's stdin
pipe(filedes); dup2(filedes[1], 1); close(filedes[1]);
pipe(filedes); dup2(filedes[1], 2); close(filedes[1]);
const char **argv = new const char * [3];
argv[0] = strdup("/bin/sh");
argv[1] = strdup("-s");
argv[2] = NULL;
// "load" command.
fThreadId = load_image(2, argv, (const char**)environ);
int i;
for (i = 0; i < 2; i++)
delete argv[i];
delete [] argv;
if (fThreadId < B_OK)
return fThreadId;
// thread id is now suspended.
setpgid(fThreadId, fThreadId);
// Restore old FDs
close(0); dup(old_in); close(old_in);
close(1); dup(old_out); close(old_out);
close(2); dup(old_err); close(old_err);
set_thread_priority(fThreadId, B_LOW_PRIORITY);
resume_thread(fThreadId);
// Write the script
if (write(in, script.String(), script.Length() - 1) != script.Length() - 1
|| write(in, "\nexit\n", 6) != 6) {
parser_debug("Writing script failed\n");
kill_thread(fThreadId);
return B_ERROR;
}
// Restore current working directory
chdir(oldWorkingDirectory);
return B_OK;
}
// #pragma mark - PackageDirectory
PackageDirectory::PackageDirectory(BFile* parent, const BString& path,
uint8 type, uint32 ctime, uint32 mtime, uint64 offset, uint64 size)
:
PackageItem(parent, path, type, ctime, mtime, offset, size)
{
}
status_t
PackageDirectory::DoInstall(const char* path, ItemState* state)
{
BPath &destination = state->destination;
status_t ret;
parser_debug("Directory: %s DoInstall() called!\n", fPath.String());
ret = InitPath(path, &destination);
parser_debug("Ret: %ld %s\n", ret, strerror(ret));
if (ret != B_OK)
return ret;
// Since Haiku is single-user right now, we give the newly
// created directory default permissions
ret = create_directory(destination.Path(), kDefaultMode);
parser_debug("Create dir ret: %ld %s\n", ret, strerror(ret));
if (ret != B_OK)
return ret;
BDirectory dir(destination.Path());
parser_debug("Directory created!\n");
if (fCreationTime)
dir.SetCreationTime(static_cast<time_t>(fCreationTime));
if (fModificationTime)
dir.SetModificationTime(static_cast<time_t>(fModificationTime));
// Since directories can only have attributes in the offset section,
// we can check here whether it is necessary to continue
if (fOffset)
ret = HandleAttributes(&destination, &dir, "FoDa");
parser_debug("Ret: %ld %s\n", ret, strerror(ret));
return ret;
}
const uint32
PackageDirectory::ItemKind()
{
return P_KIND_DIRECTORY;
}
// #pragma mark - PackageFile
PackageFile::PackageFile(BFile *parent, const BString &path, uint8 type,
uint32 ctime, uint32 mtime, uint64 offset, uint64 size,
uint64 originalSize, uint32 platform, const BString &mime,
const BString &signature, uint32 mode)
:
PackageItem(parent, path, type, ctime, mtime, offset, size),
fOriginalSize(originalSize),
fPlatform(platform),
fMode(mode),
fMimeType(mime),
fSignature(signature)
{
}
status_t
PackageFile::DoInstall(const char* path, ItemState* state)
{
if (state == NULL)
return B_ERROR;
BPath& destination = state->destination;
status_t ret = B_OK;
parser_debug("File: %s DoInstall() called!\n", fPath.String());
BFile file;
if (state->status == B_NO_INIT || destination.InitCheck() != B_OK) {
ret = InitPath(path, &destination);
if (ret != B_OK)
return ret;
ret = file.SetTo(destination.Path(),
B_WRITE_ONLY | B_CREATE_FILE | B_FAIL_IF_EXISTS);
if (ret == B_ENTRY_NOT_FOUND) {
BPath directory;
destination.GetParent(&directory);
if (create_directory(directory.Path(), kDefaultMode) != B_OK)
return B_ERROR;
ret = file.SetTo(destination.Path(), B_WRITE_ONLY | B_CREATE_FILE);
} else if (ret == B_FILE_EXISTS)
state->status = B_FILE_EXISTS;
if (ret != B_OK)
return ret;
}
if (state->status == B_FILE_EXISTS) {
switch (state->policy) {
case P_EXISTS_OVERWRITE:
ret = file.SetTo(destination.Path(),
B_WRITE_ONLY | B_ERASE_FILE);
break;
case P_EXISTS_NONE:
case P_EXISTS_ASK:
ret = B_FILE_EXISTS;
break;
case P_EXISTS_SKIP:
return B_OK;
}
}
if (ret != B_OK)
return ret;
parser_debug(" File created!\n");
// Set the file permissions, creation and modification times
ret = file.SetPermissions(static_cast<mode_t>(fMode));
if (fCreationTime && ret == B_OK)
ret = file.SetCreationTime(static_cast<time_t>(fCreationTime));
if (fModificationTime && ret == B_OK)
ret = file.SetModificationTime(static_cast<time_t>(fModificationTime));
if (ret != B_OK)
return ret;
// Set the mimetype and application signature if present
BNodeInfo info(&file);
if (fMimeType.Length() > 0) {
ret = info.SetType(fMimeType.String());
if (ret != B_OK)
return ret;
}
if (fSignature.Length() > 0) {
ret = info.SetPreferredApp(fSignature.String());
if (ret != B_OK)
return ret;
}
if (fOffset) {
parser_debug("We have an offset\n");
if (!fPackage)
return B_ERROR;
ret = fPackage->InitCheck();
if (ret != B_OK)
return ret;
// We need to parse the data section now
fPackage->Seek(fOffset, SEEK_SET);
uint8 buffer[7];
char *attrName = 0;
uint32 nameSize = 0;
uint8 *attrData = new uint8[P_CHUNK_SIZE];
uint64 dataSize = P_CHUNK_SIZE;
uint8 *temp = new uint8[P_CHUNK_SIZE];
uint64 tempSize = P_CHUNK_SIZE;
uint64 attrCSize = 0, attrOSize = 0;
uint32 attrType = 0; // type_code type
bool attrStarted = false, done = false;
uint8 section = P_ATTRIBUTE;
while (fPackage->Read(buffer, 7) == 7) {
if (!memcmp(buffer, "FBeA", 5)) {
parser_debug("-> Attribute\n");
section = P_ATTRIBUTE;
continue;
} else if (!memcmp(buffer, "FiDa", 5)) {
parser_debug("-> File data\n");
section = P_DATA;
continue;
}
switch (section) {
case P_ATTRIBUTE:
ret = ParseAttribute(buffer, &file, &attrName, &nameSize,
&attrType, &attrData, &dataSize, &temp, &tempSize,
&attrCSize, &attrOSize, &attrStarted, &done);
break;
case P_DATA:
ret = ParseData(buffer, &file, fOriginalSize, &done);
break;
default:
return B_ERROR;
}
if (ret != B_OK || done)
break;
}
delete[] attrData;
delete[] temp;
}
return ret;
}
const uint32
PackageFile::ItemKind()
{
return P_KIND_FILE;
}
// #pragma mark -
PackageLink::PackageLink(BFile *parent, const BString &path,
const BString &link, uint8 type, uint32 ctime, uint32 mtime,
uint32 mode, uint64 offset, uint64 size)
:
PackageItem(parent, path, type, ctime, mtime, offset, size),
fMode(mode),
fLink(link)
{
}
status_t
PackageLink::DoInstall(const char *path, ItemState *state)
{
if (state == NULL)
return B_ERROR;
status_t ret = B_OK;
BSymLink symlink;
parser_debug("Symlink: %s DoInstall() called!\n", fPath.String());
BPath &destination = state->destination;
BDirectory *dir = &state->parent;
if (state->status == B_NO_INIT || destination.InitCheck() != B_OK
|| dir->InitCheck() != B_OK) {
// Not yet initialized
ret = InitPath(path, &destination);
if (ret != B_OK)
return ret;
BString linkName(destination.Leaf());
parser_debug("%s:%s:%s\n", fPath.String(), destination.Path(),
linkName.String());
BPath dirPath;
ret = destination.GetParent(&dirPath);
ret = dir->SetTo(dirPath.Path());
if (ret == B_ENTRY_NOT_FOUND) {
ret = create_directory(dirPath.Path(), kDefaultMode);
if (ret != B_OK) {
parser_debug("create_directory()) failed\n");
return B_ERROR;
}
}
if (ret != B_OK) {
parser_debug("destination InitCheck failed %s for %s\n",
strerror(ret), dirPath.Path());
return ret;
}
ret = dir->CreateSymLink(destination.Path(), fLink.String(), &symlink);
if (ret == B_FILE_EXISTS) {
// We need to check if the existing symlink is pointing at the same path
// as our new one - if not, let's prompt the user
symlink.SetTo(destination.Path());
BPath oldLink;
ret = symlink.MakeLinkedPath(dir, &oldLink);
chdir(dirPath.Path());
if (ret == B_BAD_VALUE || oldLink != fLink.String())
state->status = ret = B_FILE_EXISTS;
else
ret = B_OK;
}
}
if (state->status == B_FILE_EXISTS) {
switch (state->policy) {
case P_EXISTS_OVERWRITE:
{
BEntry entry;
ret = entry.SetTo(destination.Path());
if (ret != B_OK)
return ret;
entry.Remove();
ret = dir->CreateSymLink(destination.Path(), fLink.String(),
&symlink);
break;
}
case P_EXISTS_NONE:
case P_EXISTS_ASK:
ret = B_FILE_EXISTS;
break;
case P_EXISTS_SKIP:
return B_OK;
}
}
if (ret != B_OK) {
parser_debug("CreateSymLink failed\n");
return ret;
}
parser_debug(" Symlink created!\n");
ret = symlink.SetPermissions(static_cast<mode_t>(fMode));
if (fCreationTime && ret == B_OK)
ret = symlink.SetCreationTime(static_cast<time_t>(fCreationTime));
if (fModificationTime && ret == B_OK) {
ret = symlink.SetModificationTime(static_cast<time_t>(
fModificationTime));
}
if (ret != B_OK) {
parser_debug("Failed to set symlink attributes\n");
return ret;
}
if (fOffset) {
// Symlinks also seem to have attributes - so parse them
ret = HandleAttributes(&destination, &symlink, "LnDa");
}
return ret;
}
const uint32
PackageLink::ItemKind()
{
return P_KIND_SYM_LINK;
}