blob: da284a6cb3fa9a7e5182947e15bededdae5fc4a8 [file] [log] [blame]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "dart_archive.h"
#include "entry.h"
#include "messaging.h"
#include "reader.h"
/** The enumeration of request types for communicating with Dart. */
enum RequestType {
kArchiveReadNew = 0,
kArchiveReadSupportFilterAll,
kArchiveReadSupportFilterBzip2,
kArchiveReadSupportFilterCompress,
kArchiveReadSupportFilterGzip,
kArchiveReadSupportFilterLzma,
kArchiveReadSupportFilterXz,
kArchiveReadSupportFilterProgram,
kArchiveReadSupportFilterProgramSignature,
kArchiveReadSupportFormatAll,
kArchiveReadSupportFormatAr,
kArchiveReadSupportFormatCpio,
kArchiveReadSupportFormatEmpty,
kArchiveReadSupportFormatIso9660,
kArchiveReadSupportFormatMtree,
kArchiveReadSupportFormatRaw,
kArchiveReadSupportFormatTar,
kArchiveReadSupportFormatZip,
kArchiveReadSetFilterOption,
kArchiveReadSetFormatOption,
kArchiveReadSetOption,
kArchiveReadOpenFilename,
kArchiveReadOpenMemory,
kArchiveReadNextHeader,
kArchiveReadDataBlock,
kArchiveReadDataSkip,
kArchiveReadClose,
kArchiveReadFree,
kArchiveEntryClone,
kArchiveEntryFree,
kArchiveEntryNew,
kArchiveEntrySetHardlink,
kArchiveEntrySetPathname,
kArchiveEntrySetSymlink,
kArchiveEntrySetGid,
kArchiveEntrySetUid,
kArchiveEntrySetPerm,
kArchiveEntrySetGname,
kArchiveEntrySetUname,
kArchiveEntrySetFflagsSet,
kArchiveEntrySetFflagsClear,
kArchiveEntrySetFflagsText,
kArchiveEntrySetFiletype,
kArchiveEntrySetMode,
kArchiveEntrySetSize,
kArchiveEntrySetDev,
kArchiveEntrySetDevmajor,
kArchiveEntrySetDevminor,
kArchiveEntrySetIno,
kArchiveEntrySetNlink,
kArchiveEntrySetRdev,
kArchiveEntrySetRdevmajor,
kArchiveEntrySetRdevminor,
kArchiveEntrySetAtime,
kArchiveEntrySetBirthtime,
kArchiveEntrySetCtime,
kArchiveEntrySetMtime,
kNumberOfRequestTypes
};
/**
* Dispatches a message from Dart to its native function equivalent.
*
* In addition to matching up a message with its respective function, this
* parses out the standard archive struct argument from the message and resolves
* it to an actual pointer to an archive struct.
*/
static void archiveDispatch(Dart_Port dest_port_id,
Dart_Port reply_port_id,
Dart_CObject* message) {
if (message->type != kArray) {
postInvalidArgument(reply_port_id, "Message was not an array.");
return;
} else if (message->value.as_array.length < 2) {
postInvalidArgument(reply_port_id, "Message array had %d elements, " \
"expected at least 2.", message->value.as_array.length);
return;
}
Dart_CObject* wrapped_request_type = message->value.as_array.values[0];
if (wrapped_request_type->type != kInt32) {
postInvalidArgument(reply_port_id, "Invalid request type %d.",
wrapped_request_type->type);
return;
}
enum RequestType request_type = wrapped_request_type->value.as_int32;
Dart_CObject* id = message->value.as_array.values[1];
void* ptr;
if (id->type == kNull) {
ptr = NULL;
} else if (id->type == kInt64 || id->type == kInt32) {
ptr = (void*) (intptr_t) getInteger(id);
} else {
postInvalidArgument(reply_port_id, "Invalid id type %d.", id->type);
return;
}
switch (request_type) {
case kArchiveReadNew:
archiveReadNew(reply_port_id);
break;
case kArchiveReadSupportFilterAll:
archiveReadSupportFilterAll(reply_port_id, (struct archive*) ptr);
break;
case kArchiveReadSupportFilterBzip2:
archiveReadSupportFilterBzip2(reply_port_id, (struct archive*) ptr);
break;
case kArchiveReadSupportFilterCompress:
archiveReadSupportFilterCompress(reply_port_id, (struct archive*) ptr);
break;
case kArchiveReadSupportFilterGzip:
archiveReadSupportFilterGzip(reply_port_id, (struct archive*) ptr);
break;
case kArchiveReadSupportFilterLzma:
archiveReadSupportFilterLzma(reply_port_id, (struct archive*) ptr);
break;
case kArchiveReadSupportFilterXz:
archiveReadSupportFilterXz(reply_port_id, (struct archive*) ptr);
break;
case kArchiveReadSupportFilterProgram:
archiveReadSupportFilterProgram(
reply_port_id, (struct archive*) ptr, message);
break;
case kArchiveReadSupportFilterProgramSignature:
archiveReadSupportFilterProgramSignature(
reply_port_id, (struct archive*) ptr, message);
break;
case kArchiveReadSupportFormatAll:
archiveReadSupportFormatAll(reply_port_id, (struct archive*) ptr);
break;
case kArchiveReadSupportFormatAr:
archiveReadSupportFormatAr(reply_port_id, (struct archive*) ptr);
break;
case kArchiveReadSupportFormatCpio:
archiveReadSupportFormatCpio(reply_port_id, (struct archive*) ptr);
break;
case kArchiveReadSupportFormatEmpty:
archiveReadSupportFormatEmpty(reply_port_id, (struct archive*) ptr);
break;
case kArchiveReadSupportFormatIso9660:
archiveReadSupportFormatIso9660(reply_port_id, (struct archive*) ptr);
break;
case kArchiveReadSupportFormatMtree:
archiveReadSupportFormatMtree(reply_port_id, (struct archive*) ptr);
break;
case kArchiveReadSupportFormatRaw:
archiveReadSupportFormatRaw(reply_port_id, (struct archive*) ptr);
break;
case kArchiveReadSupportFormatTar:
archiveReadSupportFormatTar(reply_port_id, (struct archive*) ptr);
break;
case kArchiveReadSupportFormatZip:
archiveReadSupportFormatZip(reply_port_id, (struct archive*) ptr);
break;
case kArchiveReadSetFilterOption:
archiveReadSetFilterOption(reply_port_id, (struct archive*) ptr, message);
break;
case kArchiveReadSetFormatOption:
archiveReadSetFormatOption(reply_port_id, (struct archive*) ptr, message);
break;
case kArchiveReadSetOption:
archiveReadSetOption(reply_port_id, (struct archive*) ptr, message);
break;
case kArchiveReadOpenFilename:
archiveReadOpenFilename(reply_port_id, (struct archive*) ptr, message);
break;
case kArchiveReadOpenMemory:
archiveReadOpenMemory(reply_port_id, (struct archive*) ptr, message);
break;
case kArchiveReadNextHeader:
archiveReadNextHeader(reply_port_id, (struct archive*) ptr);
break;
case kArchiveReadDataBlock:
archiveReadDataBlock(reply_port_id, (struct archive*) ptr);
break;
case kArchiveReadDataSkip:
archiveReadDataSkip(reply_port_id, (struct archive*) ptr);
break;
case kArchiveReadClose:
archiveReadClose(reply_port_id, (struct archive*) ptr);
break;
case kArchiveReadFree:
archiveReadFree(reply_port_id, (struct archive*) ptr);
break;
case kArchiveEntryClone:
archiveEntryClone(reply_port_id, (struct archive_entry*) ptr);
break;
case kArchiveEntryFree:
archiveEntryFree(reply_port_id, (struct archive_entry*) ptr);
break;
case kArchiveEntryNew:
archiveEntryNew(reply_port_id);
break;
case kArchiveEntrySetHardlink:
archiveEntrySetHardlink(
reply_port_id, (struct archive_entry*) ptr, message);
break;
case kArchiveEntrySetPathname:
archiveEntrySetPathname(
reply_port_id, (struct archive_entry*) ptr, message);
break;
case kArchiveEntrySetSymlink:
archiveEntrySetSymlink(reply_port_id, (struct archive_entry*) ptr, message);
break;
case kArchiveEntrySetGid:
archiveEntrySetGid(reply_port_id, (struct archive_entry*) ptr, message);
break;
case kArchiveEntrySetUid:
archiveEntrySetUid(reply_port_id, (struct archive_entry*) ptr, message);
break;
case kArchiveEntrySetPerm:
archiveEntrySetPerm(reply_port_id, (struct archive_entry*) ptr, message);
break;
case kArchiveEntrySetGname:
archiveEntrySetGname(reply_port_id, (struct archive_entry*) ptr, message);
break;
case kArchiveEntrySetUname:
archiveEntrySetUname(reply_port_id, (struct archive_entry*) ptr, message);
break;
case kArchiveEntrySetFflagsSet:
archiveEntrySetFflagsSet(
reply_port_id, (struct archive_entry*) ptr, message);
break;
case kArchiveEntrySetFflagsClear:
archiveEntrySetFflagsClear(
reply_port_id, (struct archive_entry*) ptr, message);
break;
case kArchiveEntrySetFiletype:
archiveEntrySetFiletype(
reply_port_id, (struct archive_entry*) ptr, message);
break;
case kArchiveEntrySetMode:
archiveEntrySetMode(reply_port_id, (struct archive_entry*) ptr, message);
break;
case kArchiveEntrySetSize:
archiveEntrySetSize(reply_port_id, (struct archive_entry*) ptr, message);
break;
case kArchiveEntrySetDev:
archiveEntrySetDev(reply_port_id, (struct archive_entry*) ptr, message);
break;
case kArchiveEntrySetDevmajor:
archiveEntrySetDevmajor(
reply_port_id, (struct archive_entry*) ptr, message);
break;
case kArchiveEntrySetDevminor:
archiveEntrySetDevminor(
reply_port_id, (struct archive_entry*) ptr, message);
break;
case kArchiveEntrySetIno:
archiveEntrySetIno(reply_port_id, (struct archive_entry*) ptr, message);
break;
case kArchiveEntrySetNlink:
archiveEntrySetNlink(reply_port_id, (struct archive_entry*) ptr, message);
break;
case kArchiveEntrySetRdev:
archiveEntrySetRdev(reply_port_id, (struct archive_entry*) ptr, message);
break;
case kArchiveEntrySetRdevmajor:
archiveEntrySetRdevmajor(
reply_port_id, (struct archive_entry*) ptr, message);
break;
case kArchiveEntrySetRdevminor:
archiveEntrySetRdevminor(
reply_port_id, (struct archive_entry*) ptr, message);
break;
case kArchiveEntrySetAtime:
archiveEntrySetAtime(reply_port_id, (struct archive_entry*) ptr, message);
break;
case kArchiveEntrySetBirthtime:
archiveEntrySetBirthtime(
reply_port_id, (struct archive_entry*) ptr, message);
break;
case kArchiveEntrySetCtime:
archiveEntrySetCtime(reply_port_id, (struct archive_entry*) ptr, message);
break;
case kArchiveEntrySetMtime:
archiveEntrySetMtime(reply_port_id, (struct archive_entry*) ptr, message);
break;
default:
postInvalidArgument(reply_port_id, "Invalid request id %d.", request_type);
break;
}
}
/**
* Checks if [handle] represents an error and, if so, propagates it to Dart.
* Otherwise, returns [handle].
*/
static Dart_Handle handleError(Dart_Handle handle) {
if (Dart_IsError(handle)) Dart_PropagateError(handle);
return handle;
}
/**
* A function exposed to Dart that creates a [ServicePort] for two-way
* communication between Dart and C.
*
* Takes no arguments and returns a [ServicePort].
*/
static void archiveServicePort(Dart_NativeArguments arguments) {
Dart_EnterScope();
Dart_SetReturnValue(arguments, Dart_Null());
Dart_Port service_port =
Dart_NewNativePort("ArchiveService", archiveDispatch, false);
if (service_port != ILLEGAL_PORT) {
Dart_Handle send_port = handleError(Dart_NewSendPort(service_port));
Dart_SetReturnValue(arguments, send_port);
}
Dart_ExitScope();
}
/**
* The C callback that runs the Dart finalizer for an object. Set up by
* [attachDartFinalizer]. [handle] is the object that's been collected, and
* [peerPtr] is a Dart list containing the callback and its argument.
*/
static void runDartFinalizer(Dart_Handle handle, void* peerPtr) {
Dart_EnterScope();
Dart_Handle wrappedPeer = (Dart_Handle) peerPtr;
Dart_Handle callback = handleError(Dart_ListGetAt(wrappedPeer, 0));
Dart_Handle peer = handleError(Dart_ListGetAt(wrappedPeer, 1));
handleError(Dart_InvokeClosure(callback, 1, &peer));
Dart_DeletePersistentHandle(wrappedPeer);
Dart_ExitScope();
}
/**
* Attaches a finalizer callback to a Dart object.
*
* This takes a Dart object, a callback function, and an argument to pass to the
* callback function. The callback will be called with the given argument some
* time after the object has been garbage collected.
*/
static void attachDartFinalizer(Dart_NativeArguments arguments) {
Dart_EnterScope();
Dart_SetReturnValue(arguments, Dart_Null());
Dart_Handle object = handleError(Dart_GetNativeArgument(arguments, 0));
Dart_Handle callback = handleError(Dart_GetNativeArgument(arguments, 1));
Dart_Handle peer = handleError(Dart_GetNativeArgument(arguments, 2));
Dart_Handle wrappedPeer = handleError(Dart_NewList(2));
handleError(Dart_ListSetAt(wrappedPeer, 0, callback));
handleError(Dart_ListSetAt(wrappedPeer, 1, peer));
wrappedPeer = handleError(Dart_NewPersistentHandle(wrappedPeer));
handleError(Dart_NewWeakPersistentHandle(
object, wrappedPeer, runDartFinalizer));
Dart_ExitScope();
}
/**
* A struct representing a function exposed to Dart and the name under which it
* can be looked up.
*/
struct FunctionLookup {
const char* name;
Dart_NativeFunction function;
};
/** The list of functions exposed to Dart. */
struct FunctionLookup function_list[] = {
{"Archive_ServicePort", archiveServicePort},
{"Archive_AttachFinalizer", attachDartFinalizer},
{NULL, NULL}
};
/**
* Resolves a Dart name as provided in a `native` declaration and returns the
* C function that should be invoked for that name.
*/
static Dart_NativeFunction resolveName(Dart_Handle name, int argc) {
if (!Dart_IsString8(name)) return NULL;
Dart_EnterScope();
const char* cname;
handleError(Dart_StringToCString(name, &cname));
Dart_NativeFunction result = NULL;
int i;
for (i = 0; function_list[i].name != NULL; ++i) {
if (strcmp(function_list[i].name, cname) == 0) {
result = function_list[i].function;
break;
}
}
Dart_ExitScope();
return result;
}
/** Initializes the C extension. */
DART_EXPORT Dart_Handle dart_archive_Init(Dart_Handle parent_library) {
if (Dart_IsError(parent_library)) return parent_library;
Dart_Handle result_code = Dart_SetNativeResolver(parent_library, resolveName);
if (Dart_IsError(result_code)) return result_code;
return Dart_Null();
}