Beispiel #1
0
status_t
read_table_of_contents(int fd, scsi_toc_toc *toc, size_t length)
{
	status_t status = read_table_of_contents(fd, 1, SCSI_TOC_FORMAT_TOC,
		(uint8*)toc, length);
	if (status < B_OK)
		return status;

	// make sure the values in the TOC make sense

	int32 lastTrack = toc->last_track + 1 - toc->first_track;
	size_t dataLength = B_BENDIAN_TO_HOST_INT16(toc->data_length) + 2;
	if (dataLength < sizeof(scsi_toc_toc) || lastTrack <= 0)
		return B_BAD_DATA;

	if (length > dataLength)
		length = dataLength;

	length -= sizeof(scsi_toc_general);

	if (lastTrack * sizeof(scsi_toc_track) > length)
		toc->last_track = length / sizeof(scsi_toc_track) + toc->first_track;

	dump_toc(toc);
	return B_OK;
}
Beispiel #2
0
void
AHCIPort::ScsiUnmap(scsi_ccb* request, scsi_unmap_parameter_list* unmapBlocks)
{
	// Determine how many ranges we'll need
	// We assume that the SCSI unmap ranges cannot be merged together
	uint32 scsiRangeCount = B_BENDIAN_TO_HOST_INT16(
		unmapBlocks->block_data_length) / sizeof(scsi_unmap_block_descriptor);
	uint32 lbaRangeCount = 0;
	for (uint32 i = 0; i < scsiRangeCount; i++) {
		lbaRangeCount += ((uint32)B_BENDIAN_TO_HOST_INT32(
			unmapBlocks->blocks[i].block_count) + 65534) / 65535;
	}

	uint32 lbaRangesSize = lbaRangeCount * sizeof(uint64);
	uint64* lbaRanges = (uint64*)malloc(lbaRangesSize);
	if (lbaRanges == NULL) {
		TRACE("out of memory when allocating %" B_PRIu32 " unmap ranges\n",
			lbaRangeCount);
		request->subsys_status = SCSI_REQ_ABORTED;
		gSCSI->finished(request, 1);
		return;
	}

	MemoryDeleter deleter(lbaRanges);

	for (uint32 i = 0, scsiIndex = 0; scsiIndex < scsiRangeCount; scsiIndex++) {
		uint64 lba = (uint64)B_BENDIAN_TO_HOST_INT64(
			unmapBlocks->blocks[scsiIndex].lba);
		uint64 blocksLeft = (uint32)B_BENDIAN_TO_HOST_INT32(
			unmapBlocks->blocks[scsiIndex].block_count);
		while (blocksLeft > 0) {
			uint16 blocks = blocksLeft > 65535 ? 65535 : (uint16)blocksLeft;
			lbaRanges[i++] = B_HOST_TO_LENDIAN_INT64(
				((uint64)blocks << 48) | lba);
			lba += blocks;
			blocksLeft -= blocks;
		}
	}

	sata_request sreq;
	sreq.SetATA48Command(ATA_COMMAND_DATA_SET_MANAGEMENT, 0,
		(lbaRangesSize + 511) / 512);
	sreq.SetFeature(1);
	sreq.SetData(lbaRanges, lbaRangesSize);

	ExecuteSataRequest(&sreq);
	sreq.WaitForCompletion();

	if ((sreq.CompletionStatus() & ATA_ERR) != 0) {
		TRACE("trim failed (%" B_PRIu32 " ranges)!\n", lbaRangeCount);
		request->subsys_status = SCSI_REQ_CMP_ERR;
	} else
		request->subsys_status = SCSI_REQ_CMP;

	request->data_resid = 0;
	request->device_status = SCSI_STATUS_GOOD;
	gSCSI->finished(request, 1);
}
Beispiel #3
0
int32
ethernet_deframe(net_device* device, net_buffer* buffer)
{
	//dprintf("asked to deframe buffer for device %s\n", device->name);

	NetBufferHeaderRemover<ether_header> bufferHeader(buffer);
	if (bufferHeader.Status() != B_OK)
		return bufferHeader.Status();

	ether_header& header = bufferHeader.Data();
	uint16 type = B_BENDIAN_TO_HOST_INT16(header.type);

	struct sockaddr_dl& source = *(struct sockaddr_dl*)buffer->source;
	struct sockaddr_dl& destination = *(struct sockaddr_dl*)buffer->destination;

	source.sdl_len = sizeof(sockaddr_dl);
	source.sdl_family = AF_LINK;
	source.sdl_index = device->index;
	source.sdl_type = IFT_ETHER;
	source.sdl_e_type = header.type;
	source.sdl_nlen = source.sdl_slen = 0;
	source.sdl_alen = ETHER_ADDRESS_LENGTH;
	memcpy(source.sdl_data, header.source, ETHER_ADDRESS_LENGTH);

	destination.sdl_len = sizeof(sockaddr_dl);
	destination.sdl_family = AF_LINK;
	destination.sdl_index = device->index;
	destination.sdl_type = IFT_ETHER;
	destination.sdl_e_type = header.type;
	destination.sdl_nlen = destination.sdl_slen = 0;
	destination.sdl_alen = ETHER_ADDRESS_LENGTH;
	memcpy(destination.sdl_data, header.destination, ETHER_ADDRESS_LENGTH);

	// Mark buffer if it was a broadcast/multicast packet
	if (!memcmp(header.destination, kBroadcastAddress, ETHER_ADDRESS_LENGTH))
		buffer->flags |= MSG_BCAST;
	else if ((header.destination[0] & 0x01) != 0)
		buffer->flags |= MSG_MCAST;

	// Translate the ethernet specific type to a generic one if possible
	switch (type) {
		case ETHER_TYPE_IP:
			buffer->type = B_NET_FRAME_TYPE_IPV4;
			break;
		case ETHER_TYPE_IPV6:
			buffer->type = B_NET_FRAME_TYPE_IPV6;
			break;
		case ETHER_TYPE_IPX:
			buffer->type = B_NET_FRAME_TYPE_IPX;
			break;

		default:
			buffer->type = B_NET_FRAME_TYPE(IFT_ETHER, type);
			break;
	}

	return B_OK;
}
Beispiel #4
0
// --------------------------------------------------
static uint16 ttf_get_uint16(FILE * ttf)
{
    uint16 v;

	if (fread(&v, 1, 2, ttf) != 2)
		return 0;

	return B_BENDIAN_TO_HOST_INT16(v);
}
Beispiel #5
0
void
swap_words(void *data, size_t size)
{
	uint16 *word = (uint16 *)data;
	size_t count = size / 2;
	while (count--) {
		*word = B_BENDIAN_TO_HOST_INT16(*word);
		word++;
	}
}
Beispiel #6
0
/*! \brief Creates a new Disc object by parsing the given table of contents
	entries and checking the resultant data structure for errors and
	warnings.

	If successful, subsequent calls to InitCheck() will return \c B_OK,
	elsewise they will return an error code.
*/
Disc::Disc(int fd)
	:
	fInitStatus(B_NO_INIT),
	fSessionList(new List)
{
	DEBUG_INIT_ETC("Disc", ("fd: %d", fd));

	uchar data[kBlockSize];
/*
	if (!error)
		error = sessionInfo && index >= 0 ? B_OK : B_BAD_VALUE;
	int32 session = index+1;
		// Check for a valid session index
		if (session < 1 || session > 99)
			error = B_ENTRY_NOT_FOUND;
*/

	status_t error = fSessionList ? B_OK : B_NO_MEMORY;

	// Attempt to read the table of contents, first in lba mode, then in msf
	// mode
	if (!error)
		error = read_table_of_contents(fd, 1, data, kBlockSize, false);
	if (error) {
		TRACE(("%s: lba read_toc failed, trying msf instead\n",
			kModuleDebugName));
		error = read_table_of_contents(fd, 1, data, kBlockSize, true);
	}

	// Interpret the data returned, if successful
	if (!error) {
		cdrom_table_of_contents_header* header;
		cdrom_full_table_of_contents_entry* entries;
		int count;

		header = (cdrom_table_of_contents_header*)data;
		entries = (cdrom_full_table_of_contents_entry*)(data + 4);
		header->length = B_BENDIAN_TO_HOST_INT16(header->length);

		count = (header->length - 2)
			/ sizeof(cdrom_full_table_of_contents_entry);

		count = _AdjustForYellowBook(entries, count);
		error = _ParseTableOfContents(entries, count);
//		Dump();
		if (!error) {
			_SortAndRemoveDuplicates();
			error = _CheckForErrorsAndWarnings();
		}
	}

	PRINT(("Setting init status to 0x%" B_PRIx32 ", `%s'\n", error,
		strerror(error)));
	fInitStatus = error;
}
Beispiel #7
0
status_t
BRawNetBuffer::ReadUint16(uint16& value)
{
	uint16 netVal;
	ssize_t sizeW = fBuffer.ReadAt(fReadPosition, &netVal, sizeof(uint16));
	if (sizeW == 0)
		return B_ERROR;
	value= B_BENDIAN_TO_HOST_INT16(netVal);
	fReadPosition += sizeof(uint16);
	return B_OK;
}
Beispiel #8
0
status_t
BNetBuffer::RemoveUint16(uint16& data)
{
	uint16 be_data;
	status_t result = RemoveData((void*)&be_data, sizeof(uint16));
	if (result != B_OK)
		return result;

	data = B_BENDIAN_TO_HOST_INT16(be_data);

	return B_OK;
}
Beispiel #9
0
static void
dump_full_table_of_contents(uchar* data, uint16 dataLength)
{
	cdrom_table_of_contents_header* header
		= (cdrom_table_of_contents_header*)data;
	cdrom_full_table_of_contents_entry* entries
		= (cdrom_full_table_of_contents_entry*)(data + 4);
	int headerLength = B_BENDIAN_TO_HOST_INT16(header->length);

	if (dataLength < headerLength) {
		TRACE(("dump_full_table_of_contents: warning, data buffer not large "
			"enough (%d < %d)\n", dataLength, headerLength));
		headerLength = dataLength;
	}

	TRACE(("%s: table of contents dump:\n", kModuleDebugName));
	TRACE(("--------------------------------------------------\n"));
	TRACE(("header:\n"));
	TRACE(("  length = %d\n", headerLength));
	TRACE(("  first  = %d\n", header->first));
	TRACE(("  last   = %d\n", header->last));

	int count = (headerLength - 2) / sizeof(cdrom_full_table_of_contents_entry);
	TRACE(("\n"));
	TRACE(("entry count = %d\n", count));

	for (int i = 0; i < count; i++) {
		TRACE(("\n"));
		TRACE(("entry #%d:\n", i));
		TRACE(("  session  = %d\n", entries[i].session));
		TRACE(("  adr      = %d\n", entries[i].adr));
		TRACE(("  control  = %d (%s track, copy %s)\n", entries[i].control,
			(entries[i].control & kControlDataTrack ? "data" : "audio"),
			(entries[i].control & kControlCopyPermitted
				? "permitted" : "prohibited")));
		TRACE(("  tno      = %d\n", entries[i].tno));
		TRACE(("  point    = %d (0x%.2x)\n", entries[i].point,
			entries[i].point));
		TRACE(("  minutes  = %d\n", entries[i].minutes));
		TRACE(("  frames   = %d\n", entries[i].seconds));
		TRACE(("  seconds  = %d\n", entries[i].frames));
		TRACE(("  zero     = %d\n", entries[i].zero));
		TRACE(("  pminutes = %d\n", entries[i].pminutes));
		TRACE(("  pseconds = %d\n", entries[i].pseconds));
		TRACE(("  pframes  = %d\n", entries[i].pframes));
		TRACE(("  lba      = %lld\n",
			msf_to_lba(make_msf_address(entries[i].pminutes,
			entries[i].pseconds, entries[i].pframes))));
	}
	TRACE(("--------------------------------------------------\n"));
}
Beispiel #10
0
void
AHCIPort::ScsiExecuteRequest(scsi_ccb *request)
{
//	TRACE("AHCIPort::ScsiExecuteRequest port %d, opcode 0x%02x, length %u\n", fIndex, request->cdb[0], request->cdb_length);

    if (fIsATAPI) {
        bool isWrite = false;
        switch (request->flags & SCSI_DIR_MASK) {
        case SCSI_DIR_NONE:
            ASSERT(request->data_length == 0);
            break;
        case SCSI_DIR_IN:
            ASSERT(request->data_length > 0);
            break;
        case SCSI_DIR_OUT:
            isWrite = true;
            ASSERT(request->data_length > 0);
            break;
        default:
            panic("CDB has invalid direction mask");
        }

//		TRACE("AHCIPort::ScsiExecuteRequest ATAPI: port %d, opcode 0x%02x, length %u\n", fIndex, request->cdb[0], request->cdb_length);

        sata_request *sreq = new(std::nothrow) sata_request(request);
        if (sreq == NULL) {
            TRACE("out of memory when allocating atapi request\n");
            request->subsys_status = SCSI_REQ_ABORTED;
            gSCSI->finished(request, 1);
            return;
        }

        sreq->set_atapi_cmd(request->data_length);
//		uint8 *data = (uint8*) sreq->ccb()->cdb;
//		for (int i = 0; i < 16; i += 8) {
//			TRACE("  %02x %02x %02x %02x %02x %02x %02x %02x\n", data[i], data[i+1], data[i+2], data[i+3], data[i+4], data[i+5], data[i+6], data[i+7]);
//		}
        ExecuteSataRequest(sreq, isWrite);
        return;
    }

    if (request->cdb[0] == SCSI_OP_REQUEST_SENSE) {
        panic("ahci: SCSI_OP_REQUEST_SENSE not yet supported\n");
        return;
    }

    if (!fDevicePresent) {
        TRACE("no device present on port %d\n", fIndex);
        request->subsys_status = SCSI_DEV_NOT_THERE;
        gSCSI->finished(request, 1);
        return;
    }

    request->subsys_status = SCSI_REQ_CMP;

    switch (request->cdb[0]) {
    case SCSI_OP_TEST_UNIT_READY:
        ScsiTestUnitReady(request);
        break;
    case SCSI_OP_INQUIRY:
        ScsiInquiry(request);
        break;
    case SCSI_OP_READ_CAPACITY:
        ScsiReadCapacity(request);
        break;
    case SCSI_OP_SERVICE_ACTION_IN:
        if ((request->cdb[1] & 0x1f) == SCSI_SAI_READ_CAPACITY_16)
            ScsiReadCapacity16(request);
        else {
            request->subsys_status = SCSI_REQ_INVALID;
            gSCSI->finished(request, 1);
        }
        break;
    case SCSI_OP_SYNCHRONIZE_CACHE:
        ScsiSynchronizeCache(request);
        break;
    case SCSI_OP_READ_6:
    case SCSI_OP_WRITE_6:
    {
        const scsi_cmd_rw_6 *cmd = (const scsi_cmd_rw_6 *)request->cdb;
        uint32 position = ((uint32)cmd->high_lba << 16)
                          | ((uint32)cmd->mid_lba << 8) | (uint32)cmd->low_lba;
        size_t length = cmd->length != 0 ? cmd->length : 256;
        bool isWrite = request->cdb[0] == SCSI_OP_WRITE_6;
        ScsiReadWrite(request, position, length, isWrite);
        break;
    }
    case SCSI_OP_READ_10:
    case SCSI_OP_WRITE_10:
    {
        const scsi_cmd_rw_10 *cmd = (const scsi_cmd_rw_10 *)request->cdb;
        uint32 position = B_BENDIAN_TO_HOST_INT32(cmd->lba);
        size_t length = B_BENDIAN_TO_HOST_INT16(cmd->length);
        bool isWrite = request->cdb[0] == SCSI_OP_WRITE_10;
        if (length) {
            ScsiReadWrite(request, position, length, isWrite);
        } else {
            TRACE("AHCIPort::ScsiExecuteRequest error: transfer without "
                  "data!\n");
            request->subsys_status = SCSI_REQ_INVALID;
            gSCSI->finished(request, 1);
        }
        break;
    }
    case SCSI_OP_READ_12:
    case SCSI_OP_WRITE_12:
    {
        const scsi_cmd_rw_12 *cmd = (const scsi_cmd_rw_12 *)request->cdb;
        uint32 position = B_BENDIAN_TO_HOST_INT32(cmd->lba);
        size_t length = B_BENDIAN_TO_HOST_INT32(cmd->length);
        bool isWrite = request->cdb[0] == SCSI_OP_WRITE_12;
        if (length) {
            ScsiReadWrite(request, position, length, isWrite);
        } else {
            TRACE("AHCIPort::ScsiExecuteRequest error: transfer without "
                  "data!\n");
            request->subsys_status = SCSI_REQ_INVALID;
            gSCSI->finished(request, 1);
        }
        break;
    }
    case SCSI_OP_READ_16:
    case SCSI_OP_WRITE_16:
    {
        const scsi_cmd_rw_16 *cmd = (const scsi_cmd_rw_16 *)request->cdb;
        uint64 position = B_BENDIAN_TO_HOST_INT64(cmd->lba);
        size_t length = B_BENDIAN_TO_HOST_INT32(cmd->length);
        bool isWrite = request->cdb[0] == SCSI_OP_WRITE_16;
        if (length) {
            ScsiReadWrite(request, position, length, isWrite);
        } else {
            TRACE("AHCIPort::ScsiExecuteRequest error: transfer without "
                  "data!\n");
            request->subsys_status = SCSI_REQ_INVALID;
            gSCSI->finished(request, 1);
        }
        break;
    }
    case SCSI_OP_WRITE_SAME_16:
    {
        const scsi_cmd_wsame_16 *cmd = (const scsi_cmd_wsame_16 *)request->cdb;

        // SCSI unmap is used for trim, otherwise we don't support it
        if (!cmd->unmap) {
            TRACE("%s port %d: unsupported request opcode 0x%02x\n",
                  __func__, fIndex, request->cdb[0]);
            request->subsys_status = SCSI_REQ_ABORTED;
            gSCSI->finished(request, 1);
            break;
        }

        if (!fTrim) {
            // Drive doesn't support trim (or atapi)
            // Just say it was successful and quit
            request->subsys_status = SCSI_REQ_CMP;
        } else {
            TRACE("%s unimplemented: TRIM call\n", __func__);
            // TODO: Make Serial ATA (sata_request?) trim call here.
            request->subsys_status = SCSI_REQ_ABORTED;
        }
        gSCSI->finished(request, 1);
        break;
    }
    default:
        TRACE("AHCIPort::ScsiExecuteRequest port %d unsupported request "
              "opcode 0x%02x\n", fIndex, request->cdb[0]);
        request->subsys_status = SCSI_REQ_ABORTED;
        gSCSI->finished(request, 1);
    }
}
Beispiel #11
0
void
AHCIPort::ScsiExecuteRequest(scsi_ccb *request)
{
//	TRACE("AHCIPort::ScsiExecuteRequest port %d, opcode 0x%02x, length %u\n", fIndex, request->cdb[0], request->cdb_length);

	if (fIsATAPI) {
		bool isWrite = false;
		switch (request->flags & SCSI_DIR_MASK) {
			case SCSI_DIR_NONE:
				ASSERT(request->data_length == 0);
				break;
			case SCSI_DIR_IN:
				ASSERT(request->data_length > 0);
				break;
			case SCSI_DIR_OUT:
				isWrite = true;
				ASSERT(request->data_length > 0);
				break;
			default:
				panic("CDB has invalid direction mask");
		}
	
//		TRACE("AHCIPort::ScsiExecuteRequest ATAPI: port %d, opcode 0x%02x, length %u\n", fIndex, request->cdb[0], request->cdb_length);

		sata_request *sreq = new(std::nothrow) sata_request(request);
		sreq->set_atapi_cmd(request->data_length);
//		uint8 *data = (uint8*) sreq->ccb()->cdb;
//		for (int i = 0; i < 16; i += 8) {
//			TRACE("  %02x %02x %02x %02x %02x %02x %02x %02x\n", data[i], data[i+1], data[i+2], data[i+3], data[i+4], data[i+5], data[i+6], data[i+7]);
//		}
		ExecuteSataRequest(sreq, isWrite);
		return;
	}

	if (request->cdb[0] == SCSI_OP_REQUEST_SENSE) {
		panic("ahci: SCSI_OP_REQUEST_SENSE not yet supported\n");
		return;
	}

	if (!fDevicePresent) {
		TRACE("no device present on port %d\n", fIndex);
		request->subsys_status = SCSI_DEV_NOT_THERE;
		gSCSI->finished(request, 1);
		return;
	}
	
	request->subsys_status = SCSI_REQ_CMP;

	switch (request->cdb[0]) {
		case SCSI_OP_TEST_UNIT_READY:
			ScsiTestUnitReady(request);
			break;
		case SCSI_OP_INQUIRY:
			ScsiInquiry(request);
			break;
		case SCSI_OP_READ_CAPACITY:
			ScsiReadCapacity(request);
			break;
		case SCSI_OP_SYNCHRONIZE_CACHE:
			ScsiSynchronizeCache(request);
			break;
		case SCSI_OP_READ_6:
		case SCSI_OP_WRITE_6:
		{
			scsi_cmd_rw_6 *cmd = (scsi_cmd_rw_6 *)request->cdb;
			uint32 position = ((uint32)cmd->high_lba << 16) | ((uint32)cmd->mid_lba << 8) | (uint32)cmd->low_lba;
			size_t length = cmd->length != 0 ? cmd->length : 256;
			bool isWrite = request->cdb[0] == SCSI_OP_WRITE_6;
			ScsiReadWrite(request, position, length, isWrite);
			break;
		}
		case SCSI_OP_READ_10:
		case SCSI_OP_WRITE_10:
		{
			scsi_cmd_rw_10 *cmd = (scsi_cmd_rw_10 *)request->cdb;
			uint32 position = B_BENDIAN_TO_HOST_INT32(cmd->lba);
			size_t length = B_BENDIAN_TO_HOST_INT16(cmd->length);
			bool isWrite = request->cdb[0] == SCSI_OP_WRITE_10;
			if (length) {
				ScsiReadWrite(request, position, length, isWrite);
			} else {
				TRACE("AHCIPort::ScsiExecuteRequest error: transfer without data!\n");
				request->subsys_status = SCSI_REQ_INVALID;
				gSCSI->finished(request, 1);
			}
			break;
		}
		case SCSI_OP_READ_12:
		case SCSI_OP_WRITE_12:
		{
			scsi_cmd_rw_12 *cmd = (scsi_cmd_rw_12 *)request->cdb;
			uint32 position = B_BENDIAN_TO_HOST_INT32(cmd->lba);
			size_t length = B_BENDIAN_TO_HOST_INT32(cmd->length);
			bool isWrite = request->cdb[0] == SCSI_OP_WRITE_12;
			if (length) {
				ScsiReadWrite(request, position, length, isWrite);
			} else {
				TRACE("AHCIPort::ScsiExecuteRequest error: transfer without data!\n");
				request->subsys_status = SCSI_REQ_INVALID;
				gSCSI->finished(request, 1);
			}
			break;
		}
		default:
			TRACE("AHCIPort::ScsiExecuteRequest port %d unsupported request opcode 0x%02x\n", fIndex, request->cdb[0]);
			request->subsys_status = SCSI_REQ_ABORTED;
			gSCSI->finished(request, 1);
	}
}
Beispiel #12
0
/*! Execute SCSI command */
void
ata_exec_io(ide_device_info *device, ide_qrequest *qrequest)
{
	scsi_ccb *request = qrequest->request;
	
	SHOW_FLOW(3, "command=%x", request->cdb[0]);
		
	// ATA devices have one LUN only
	if (request->target_lun != 0) {
		request->subsys_status = SCSI_SEL_TIMEOUT;
		finish_request(qrequest, false);
		return;
	}

	// starting a request means deleting sense, so don't do it if
	// the command wants to read it
	if (request->cdb[0] != SCSI_OP_REQUEST_SENSE)	
		start_request(device, qrequest);

	switch (request->cdb[0]) {
		case SCSI_OP_TEST_UNIT_READY:
			ata_test_unit_ready(device, qrequest);
			break;

		case SCSI_OP_REQUEST_SENSE:
			ide_request_sense(device, qrequest);
			return;

		case SCSI_OP_FORMAT: /* FORMAT UNIT */
			// we could forward request to disk, but modern disks cannot
			// be formatted anyway, so we just refuse request
			// (exceptions are removable media devices, but to my knowledge
			// they don't have to be formatted as well)
			set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_OPCODE);
			break;

		case SCSI_OP_INQUIRY: 
			ata_inquiry(device, qrequest);
			break;

		case SCSI_OP_MODE_SELECT_10:
			ata_mode_select_10(device, qrequest);
			break;

		case SCSI_OP_MODE_SENSE_10:
			ata_mode_sense_10(device, qrequest);
			break;

		case SCSI_OP_MODE_SELECT_6:
		case SCSI_OP_MODE_SENSE_6:
			// we've told SCSI bus manager to emulates these commands
			set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_OPCODE);
			break;

		case SCSI_OP_RESERVE:
		case SCSI_OP_RELEASE:
			// though mandatory, this doesn't make much sense in a
			// single initiator environment; so what
			set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_OPCODE);
			break;

		case SCSI_OP_START_STOP: {
			scsi_cmd_ssu *cmd = (scsi_cmd_ssu *)request->cdb;

			// with no LoEj bit set, we should only allow/deny further access
			// we ignore that (unsupported for ATA)
			// with LoEj bit set, we should additionally either load or eject the medium
			// (start = 0 - eject; start = 1 - load)

			if (!cmd->start)
				// we must always flush cache if start = 0
				ata_flush_cache(device, qrequest);

			if (cmd->load_eject)
				ata_load_eject(device, qrequest, cmd->start);

			break;
		}

		case SCSI_OP_PREVENT_ALLOW: {
			scsi_cmd_prevent_allow *cmd = (scsi_cmd_prevent_allow *)request->cdb;

			ata_prevent_allow(device, cmd->prevent);
			break;
		}

		case SCSI_OP_READ_CAPACITY:
			read_capacity(device, qrequest);
			break;

		case SCSI_OP_VERIFY:
			// does anyone uses this function?
			// effectly, it does a read-and-compare, which IDE doesn't support
			set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_OPCODE);
			break;

		case SCSI_OP_SYNCHRONIZE_CACHE:
			// we ignore range and immediate bit, we always immediately flush everything 
			ata_flush_cache(device, qrequest);
			break;

		// sadly, there are two possible read/write operation codes;
		// at least, the third one, read/write(12), is not valid for DAS
		case SCSI_OP_READ_6:
		case SCSI_OP_WRITE_6:
		{
			scsi_cmd_rw_6 *cmd = (scsi_cmd_rw_6 *)request->cdb;
			uint32 pos;
			size_t length;

			pos = ((uint32)cmd->high_lba << 16) | ((uint32)cmd->mid_lba << 8)
				| (uint32)cmd->low_lba;
			length = cmd->length != 0 ? cmd->length : 256;

			SHOW_FLOW(3, "READ6/WRITE6 pos=%lx, length=%lx", pos, length);

			ata_send_rw(device, qrequest, pos, length, cmd->opcode == SCSI_OP_WRITE_6);
			return;
		}

		case SCSI_OP_READ_10:
		case SCSI_OP_WRITE_10:
		{
			scsi_cmd_rw_10 *cmd = (scsi_cmd_rw_10 *)request->cdb;
			uint32 pos;
			size_t length;

			pos = B_BENDIAN_TO_HOST_INT32(cmd->lba);
			length = B_BENDIAN_TO_HOST_INT16(cmd->length);

			if (length != 0) {
				ata_send_rw(device, qrequest, pos, length, cmd->opcode == SCSI_OP_WRITE_10);
			} else {
				// we cannot transfer zero blocks (apart from LBA48)
				finish_request(qrequest, false);
			}
			return;
		}

		default:
			set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_OPCODE);
	}

	finish_checksense(qrequest);
}
Beispiel #13
0
static void
ata_mode_sense_10(ide_device_info *device, ide_qrequest *qrequest)
{
	scsi_ccb *request = qrequest->request;
	scsi_cmd_mode_sense_10 *cmd = (scsi_cmd_mode_sense_10 *)request->cdb;
	scsi_mode_param_header_10 param_header;
	scsi_modepage_control control;
	scsi_mode_param_block_desc block_desc;
	size_t totalLength = sizeof(scsi_mode_param_header_10)
		+ sizeof(scsi_mode_param_block_desc)
		+ sizeof(scsi_modepage_control);
	scsi_mode_param_dev_spec_da devspec = {
		_res0_0 : 0,
		dpo_fua : 0,
		_res0_6 : 0,
		write_protected : 0
	};
	uint32 allocationLength;

	SHOW_FLOW0(1, "Hi!");

	allocationLength = B_BENDIAN_TO_HOST_INT16(cmd->allocation_length);

	// we answer control page requests and "all pages" requests
	// (as the latter are the same as the first)
	if ((cmd->page_code != SCSI_MODEPAGE_CONTROL && cmd->page_code != SCSI_MODEPAGE_ALL)
		|| (cmd->page_control != SCSI_MODE_SENSE_PC_CURRENT
			&& cmd->page_control != SCSI_MODE_SENSE_PC_SAVED)) {
		set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_CDB_FIELD);
		return;
	}

	//param_header = (scsi_mode_param_header_10 *)request->data;
	param_header.mode_data_length = B_HOST_TO_BENDIAN_INT16(totalLength - 1);
	param_header.medium_type = 0; 		// XXX standard is a bit vague here
	param_header.dev_spec_parameter = *(uint8 *)&devspec;
	param_header.block_desc_length
		= B_HOST_TO_BENDIAN_INT16(sizeof(scsi_mode_param_block_desc));

	copy_sg_data(request, 0, allocationLength, &param_header,
		sizeof(param_header), false);

	/*block_desc = (scsi_mode_param_block_desc *)(request->data 
		+ sizeof(*param_header));*/
	memset(&block_desc, 0, sizeof(block_desc));
	// density is reserved (0), descriptor apply to entire medium (num_blocks=0)
	// remains the blocklen to be set
	block_desc.high_blocklen = 0;
	block_desc.med_blocklen = 512 >> 8;
	block_desc.low_blocklen = 512 & 0xff;

	copy_sg_data(request, sizeof(param_header), allocationLength,
		&block_desc, sizeof(block_desc), false);

	/*contr = (scsi_modepage_contr *)(request->data 
		+ sizeof(*param_header)
		+ ((uint16)param_header->high_block_desc_len << 8) 
		+ param_header->low_block_desc_len);*/

	memset(&control, 0, sizeof(control));
	control.RLEC = false;
	control.DQue = !device->CQ_enabled;
	control.QErr = false;
		// when a command fails we requeue all 
		// lost commands automagically
	control.QAM = SCSI_QAM_UNRESTRICTED;

	copy_sg_data(request, sizeof(param_header)
		+ B_BENDIAN_TO_HOST_INT16(param_header.block_desc_length),
		allocationLength, &control, sizeof(control), false);

	// the number of bytes that were transferred to buffer is
	// restricted by allocation length and by request data buffer size
	totalLength = min(totalLength, allocationLength);
	totalLength = min(totalLength, request->data_length);

	request->data_resid = request->data_length - totalLength;
}


/*! Emulate modifying control page */
static bool
ata_mode_select_control_page(ide_device_info *device, ide_qrequest *qrequest,
	scsi_modepage_control *page)
{
	if (page->header.page_length != sizeof(*page) - sizeof(page->header)) {
		set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_PARAM_LIST_LENGTH_ERR);
		return false;
	}

	// we only support enabling/disabling command queuing
	enable_CQ(device, !page->DQue);
	return true;
}


/*! Emulate MODE SELECT 10 command */
static void
ata_mode_select_10(ide_device_info *device, ide_qrequest *qrequest)
{
	scsi_ccb *request = qrequest->request;
	scsi_cmd_mode_select_10 *cmd = (scsi_cmd_mode_select_10 *)request->cdb;
	scsi_mode_param_header_10 param_header;
	scsi_modepage_header page_header;
	uint32 totalLength;
	uint32 modepageOffset;
	char modepage_buffer[64];	// !!! enlarge this to support longer mode pages

	if (cmd->save_pages || cmd->pf != 1) {
		set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_CDB_FIELD);
		return;
	}

	totalLength = min(request->data_length,
		B_BENDIAN_TO_HOST_INT16(cmd->param_list_length));

	// first, retrieve page header to get size of different chunks
	//param_header = (scsi_mode_param_header_10 *)request->data;
	if (!copy_sg_data(request, 0, totalLength, &param_header, sizeof(param_header), true))
		goto err;

	totalLength = min(totalLength,
		B_BENDIAN_TO_HOST_INT16(param_header.mode_data_length) + 1UL);

	// this is the start of the first mode page;
	// we ignore the block descriptor silently
	modepageOffset = sizeof(param_header)
		+ B_BENDIAN_TO_HOST_INT16(param_header.block_desc_length);

	// go through list of pages		
	while (modepageOffset < totalLength) {
		uint32 pageLength;

		// get header to know how long page is
		if (!copy_sg_data(request, modepageOffset, totalLength,
				&page_header, sizeof(page_header), true))
			goto err;

		// get size of one page and copy it to buffer
		pageLength = page_header.page_length + sizeof(scsi_modepage_header);

		// the buffer has a maximum size - this is really standard compliant but
		// sufficient for our needs
		if (pageLength > sizeof(modepage_buffer))
			goto err;

		if (!copy_sg_data(request, modepageOffset, totalLength,
				&modepage_buffer, min(pageLength, sizeof(modepage_buffer)), true))
			goto err;

		// modify page;
		// currently, we only support the control mode page
		switch (page_header.page_code) {
			case SCSI_MODEPAGE_CONTROL:
				if (!ata_mode_select_control_page(device, qrequest,
						(scsi_modepage_control *)modepage_buffer))
					return;
				break;

			default:
				set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST,
					SCSIS_ASC_INV_PARAM_LIST_FIELD);
				return;
		}

		modepageOffset += pageLength;
	}

	if (modepageOffset != totalLength)
		goto err;

	request->data_resid = request->data_length - totalLength;
	return;

	// if we arrive here, data length was incorrect
err:
	set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_PARAM_LIST_LENGTH_ERR);
}
Beispiel #14
0
/*!	get inquiry data
	returns true on success
*/
static bool
scsi_scan_get_inquiry(scsi_ccb *worker_req, scsi_res_inquiry *new_inquiry_data)
{
	scsi_cmd_inquiry *cmd = (scsi_cmd_inquiry *)worker_req->cdb;
	scsi_device_info *device = worker_req->device;

	SHOW_FLOW0(3, "");

	// in case not whole structure gets transferred, we set remaining data to zero
	memset(new_inquiry_data, 0, sizeof(*new_inquiry_data));

	cmd->opcode = SCSI_OP_INQUIRY;
	cmd->lun = device->target_lun;
	cmd->evpd = 0;
	cmd->page_code = 0;
	cmd->allocation_length = sizeof(*new_inquiry_data);

	worker_req->sg_list = NULL;
	worker_req->data = (uchar *)new_inquiry_data;
	worker_req->data_length = sizeof(*new_inquiry_data);
	worker_req->cdb_length = 6;
	worker_req->timeout = SCSI_STD_TIMEOUT;
	worker_req->sort = -1;
	worker_req->flags = SCSI_DIR_IN;

	scsi_sync_io(worker_req);

	switch (worker_req->subsys_status) {
		case SCSI_REQ_CMP: {
			char vendor[9], product[17], rev[5];

			SHOW_FLOW0(3, "send successfully");

			// we could check transmission length here, but as we reset
			// missing bytes before, we get kind of valid data anyway (hopefully)

			strlcpy(vendor, new_inquiry_data->vendor_ident, sizeof(vendor));
			strlcpy(product, new_inquiry_data->product_ident, sizeof(product));
			strlcpy(rev, new_inquiry_data->product_rev, sizeof(rev));

			SHOW_INFO(3, "device type: %d, qualifier: %d, removable: %d, ANSI version: %d, response data format: %d\n"
				"vendor: %s, product: %s, rev: %s",
				new_inquiry_data->device_type, new_inquiry_data->device_qualifier,
				new_inquiry_data->removable_medium, new_inquiry_data->ansi_version,
				new_inquiry_data->response_data_format,
				vendor, product, rev);

			SHOW_INFO(3, "additional_length: %d", new_inquiry_data->additional_length + 4);

			// time to show standards the device conforms to;
			// unfortunately, ATAPI CD-ROM drives tend to tell that they have
			// only minimal info (36 bytes), but still they return (valid!) 96 bytes -
			// bad luck
			if (std::min((int)cmd->allocation_length,
						new_inquiry_data->additional_length + 4)
					>= (int)offsetof(scsi_res_inquiry, _res74)) {
				int i, previousStandard = -1;

				for (i = 0; i < 8; ++i) {
					int standard = B_BENDIAN_TO_HOST_INT16(
						new_inquiry_data->version_descriptor[i]);

					// omit standards reported twice
					if (standard != previousStandard && standard != 0)
						SHOW_INFO(3, "standard: %04x", standard);

					previousStandard = standard;
				}
			}

			//snooze( 1000000 );

	/*		{
				unsigned int i;

				for( i = 0; i < worker_req->data_length - worker_req->data_resid; ++i ) {
					dprintf( "%2x ", *((char *)new_inquiry_data + i) );
				}

				dprintf( "\n" );
			}*/

			return true;
		}

		default:
			return false;
	}
}
Beispiel #15
0
void
AHCIPort::ScsiExecuteRequest(scsi_ccb* request)
{
//	TRACE("AHCIPort::ScsiExecuteRequest port %d, opcode 0x%02x, length %u\n", fIndex, request->cdb[0], request->cdb_length);

	if (fIsATAPI) {
		bool isWrite = false;
		switch (request->flags & SCSI_DIR_MASK) {
			case SCSI_DIR_NONE:
				ASSERT(request->data_length == 0);
				break;
			case SCSI_DIR_IN:
				ASSERT(request->data_length > 0);
				break;
			case SCSI_DIR_OUT:
				isWrite = true;
				ASSERT(request->data_length > 0);
				break;
			default:
				panic("CDB has invalid direction mask");
		}

//		TRACE("AHCIPort::ScsiExecuteRequest ATAPI: port %d, opcode 0x%02x, length %u\n", fIndex, request->cdb[0], request->cdb_length);

		sata_request* sreq = new(std::nothrow) sata_request(request);
		if (sreq == NULL) {
			TRACE("out of memory when allocating atapi request\n");
			request->subsys_status = SCSI_REQ_ABORTED;
			gSCSI->finished(request, 1);
			return;
		}

		sreq->SetATAPICommand(request->data_length);
//		uint8* data = (uint8*) sreq->ccb()->cdb;
//		for (int i = 0; i < 16; i += 8) {
//			TRACE("  %02x %02x %02x %02x %02x %02x %02x %02x\n", data[i], data[i+1], data[i+2], data[i+3], data[i+4], data[i+5], data[i+6], data[i+7]);
//		}
		ExecuteSataRequest(sreq, isWrite);
		return;
	}

	if (request->cdb[0] == SCSI_OP_REQUEST_SENSE) {
		panic("ahci: SCSI_OP_REQUEST_SENSE not yet supported\n");
		return;
	}

	if (!fDevicePresent) {
		TRACE("no device present on port %d\n", fIndex);
		request->subsys_status = SCSI_DEV_NOT_THERE;
		gSCSI->finished(request, 1);
		return;
	}

	request->subsys_status = SCSI_REQ_CMP;

	switch (request->cdb[0]) {
		case SCSI_OP_TEST_UNIT_READY:
			ScsiTestUnitReady(request);
			break;
		case SCSI_OP_INQUIRY:
			ScsiInquiry(request);
			break;
		case SCSI_OP_READ_CAPACITY:
			ScsiReadCapacity(request);
			break;
		case SCSI_OP_SERVICE_ACTION_IN:
			if ((request->cdb[1] & 0x1f) == SCSI_SAI_READ_CAPACITY_16)
				ScsiReadCapacity16(request);
			else {
				request->subsys_status = SCSI_REQ_INVALID;
				gSCSI->finished(request, 1);
			}
			break;
		case SCSI_OP_SYNCHRONIZE_CACHE:
			ScsiSynchronizeCache(request);
			break;
		case SCSI_OP_READ_6:
		case SCSI_OP_WRITE_6:
		{
			const scsi_cmd_rw_6* cmd = (const scsi_cmd_rw_6*)request->cdb;
			uint32 position = ((uint32)cmd->high_lba << 16)
				| ((uint32)cmd->mid_lba << 8) | (uint32)cmd->low_lba;
			size_t length = cmd->length != 0 ? cmd->length : 256;
			bool isWrite = request->cdb[0] == SCSI_OP_WRITE_6;
			ScsiReadWrite(request, position, length, isWrite);
			break;
		}
		case SCSI_OP_READ_10:
		case SCSI_OP_WRITE_10:
		{
			const scsi_cmd_rw_10* cmd = (const scsi_cmd_rw_10*)request->cdb;
			uint32 position = B_BENDIAN_TO_HOST_INT32(cmd->lba);
			size_t length = B_BENDIAN_TO_HOST_INT16(cmd->length);
			bool isWrite = request->cdb[0] == SCSI_OP_WRITE_10;
			if (length) {
				ScsiReadWrite(request, position, length, isWrite);
			} else {
				TRACE("AHCIPort::ScsiExecuteRequest error: transfer without "
					"data!\n");
				request->subsys_status = SCSI_REQ_INVALID;
				gSCSI->finished(request, 1);
			}
			break;
		}
		case SCSI_OP_READ_12:
		case SCSI_OP_WRITE_12:
		{
			const scsi_cmd_rw_12* cmd = (const scsi_cmd_rw_12*)request->cdb;
			uint32 position = B_BENDIAN_TO_HOST_INT32(cmd->lba);
			size_t length = B_BENDIAN_TO_HOST_INT32(cmd->length);
			bool isWrite = request->cdb[0] == SCSI_OP_WRITE_12;
			if (length) {
				ScsiReadWrite(request, position, length, isWrite);
			} else {
				TRACE("AHCIPort::ScsiExecuteRequest error: transfer without "
					"data!\n");
				request->subsys_status = SCSI_REQ_INVALID;
				gSCSI->finished(request, 1);
			}
			break;
		}
		case SCSI_OP_READ_16:
		case SCSI_OP_WRITE_16:
		{
			const scsi_cmd_rw_16* cmd = (const scsi_cmd_rw_16*)request->cdb;
			uint64 position = B_BENDIAN_TO_HOST_INT64(cmd->lba);
			size_t length = B_BENDIAN_TO_HOST_INT32(cmd->length);
			bool isWrite = request->cdb[0] == SCSI_OP_WRITE_16;
			if (length) {
				ScsiReadWrite(request, position, length, isWrite);
			} else {
				TRACE("AHCIPort::ScsiExecuteRequest error: transfer without "
					"data!\n");
				request->subsys_status = SCSI_REQ_INVALID;
				gSCSI->finished(request, 1);
			}
			break;
		}
		case SCSI_OP_UNMAP:
		{
			const scsi_cmd_unmap* cmd = (const scsi_cmd_unmap*)request->cdb;

			if (!fTrimSupported) {
				TRACE("%s port %d: unsupported request opcode 0x%02x\n",
					__func__, fIndex, request->cdb[0]);
				request->subsys_status = SCSI_REQ_ABORTED;
				gSCSI->finished(request, 1);
				break;
			}

			scsi_unmap_parameter_list* unmapBlocks
				= (scsi_unmap_parameter_list*)request->data;
			if (unmapBlocks == NULL
				|| B_BENDIAN_TO_HOST_INT16(cmd->length) != request->data_length
				|| B_BENDIAN_TO_HOST_INT16(unmapBlocks->data_length)
					!= request->data_length - 1) {
				TRACE("%s port %d: invalid unmap parameter data length\n",
					__func__, fIndex);
				request->subsys_status = SCSI_REQ_ABORTED;
				gSCSI->finished(request, 1);
			} else {
				ScsiUnmap(request, unmapBlocks);
			}
			break;
		}
		default:
			TRACE("AHCIPort::ScsiExecuteRequest port %d unsupported request "
				"opcode 0x%02x\n", fIndex, request->cdb[0]);
			request->subsys_status = SCSI_REQ_ABORTED;
			gSCSI->finished(request, 1);
	}
}
Beispiel #16
0
status_t
read_cdtext(int fd, struct cdtext &cdtext)
{
	uint8 *buffer = (uint8 *)malloc(kBufferSize);
	if (buffer == NULL)
		return B_NO_MEMORY;

	// do it twice, just in case...
	// (at least my CD-ROM sometimes returned broken data on first try)
	read_table_of_contents(fd, 1, SCSI_TOC_FORMAT_CD_TEXT, buffer,
		kBufferSize);
	if (read_table_of_contents(fd, 1, SCSI_TOC_FORMAT_CD_TEXT, buffer,
			kBufferSize) != B_OK) {
		free(buffer);
		return B_ERROR;
	}

	scsi_toc_general *header = (scsi_toc_general *)buffer;

	size_t packLength = B_BENDIAN_TO_HOST_INT16(header->data_length) - 2;
	cdtext_pack_data *pack = (cdtext_pack_data *)(header + 1);
	cdtext_pack_data *lastPack = NULL;
	uint8 state = 0;
	char text[256];

	while (true) {
		size_t length = sizeof(text);
		uint8 id = 0, track = 0;

		if (!parse_pack_data(pack, packLength, lastPack, id, track,
				state, text, length))
			break;

		switch (id) {
			case kTrackID:
				if (track == 0) {
					if (cdtext.album == NULL)
						cdtext.album = copy_string(text);
				} else if (track <= kMaxTracks) {
					if (cdtext.titles[track - 1] == NULL)
						cdtext.titles[track - 1] = copy_string(text);
					if (track > cdtext.track_count)
						cdtext.track_count = track;
				}
				break;

			case kArtistID:
				if (track == 0) {
					if (cdtext.artist == NULL)
						cdtext.artist = copy_string(text);
				} else if (track <= kMaxTracks) {
					if (cdtext.artists[track - 1] == NULL)
						cdtext.artists[track - 1] = copy_string(text);
				}
				break;

			default:
				if (is_string_id(id))
					dprintf("UNKNOWN %u: \"%s\"\n", id, text);
				break;
		}
	}

	free(buffer);

	if (cdtext.artist == NULL || cdtext.album == NULL)
		return B_ERROR;

	for (int i = 0; i < cdtext.track_count; i++) {
		if (cdtext.titles[i] == NULL)
			return B_ERROR;
	}

	sanitize_string(cdtext.artist);
	sanitize_album(cdtext);
	sanitize_titles(cdtext);
	correct_case(cdtext);

	dump_cdtext(cdtext);
	return B_OK;
}
Beispiel #17
0
	uint16 RequiredProgramVersion() const
		{ return B_BENDIAN_TO_HOST_INT16(required_program_version); }
Beispiel #18
0
	uint16 Version() const
		{ return B_BENDIAN_TO_HOST_INT16(version); }
Beispiel #19
0
status_t
PackageReader::Init(int fd, bool keepFD)
{
    fFD = fd;
    fOwnsFD = keepFD;

    // stat it
    struct stat st;
    if (fstat(fFD, &st) < 0) {
        fErrorOutput->PrintError("Error: Failed to access package file: %s\n",
                                 strerror(errno));
        return errno;
    }

    // read the header
    hpkg_header header;
    status_t error = _ReadBuffer(0, &header, sizeof(header));
    if (error != B_OK)
        return error;

    // check the header

    // magic
    if (B_BENDIAN_TO_HOST_INT32(header.magic) != B_HPKG_MAGIC) {
        fErrorOutput->PrintError("Error: Invalid package file: Invalid "
                                 "magic\n");
        return B_BAD_DATA;
    }

    // header size
    fHeapOffset = B_BENDIAN_TO_HOST_INT16(header.header_size);
    if ((size_t)fHeapOffset < sizeof(hpkg_header)) {
        fErrorOutput->PrintError("Error: Invalid package file: Invalid header "
                                 "size (%llu)\n", fHeapOffset);
        return B_BAD_DATA;
    }

    // version
    if (B_BENDIAN_TO_HOST_INT16(header.version) != B_HPKG_VERSION) {
        fErrorOutput->PrintError("Error: Invalid/unsupported package file "
                                 "version (%d)\n", B_BENDIAN_TO_HOST_INT16(header.version));
        return B_BAD_DATA;
    }

    // total size
    fTotalSize = B_BENDIAN_TO_HOST_INT64(header.total_size);
    if (fTotalSize != (uint64)st.st_size) {
        fErrorOutput->PrintError("Error: Invalid package file: Total size in "
                                 "header (%llu) doesn't agree with total file size (%lld)\n",
                                 fTotalSize, st.st_size);
        return B_BAD_DATA;
    }

    // package attributes length and compression
    fPackageAttributesCompression
        = B_BENDIAN_TO_HOST_INT32(header.attributes_compression);
    fPackageAttributesCompressedLength
        = B_BENDIAN_TO_HOST_INT32(header.attributes_length_compressed);
    fPackageAttributesUncompressedLength
        = B_BENDIAN_TO_HOST_INT32(header.attributes_length_uncompressed);

    if (const char* errorString = _CheckCompression(
                                      fPackageAttributesCompression, fPackageAttributesCompressedLength,
                                      fPackageAttributesUncompressedLength)) {
        fErrorOutput->PrintError("Error: Invalid package file: package "
                                 "attributes section: %s\n", errorString);
        return B_BAD_DATA;
    }

    // TOC length and compression
    fTOCCompression = B_BENDIAN_TO_HOST_INT32(header.toc_compression);
    fTOCCompressedLength
        = B_BENDIAN_TO_HOST_INT64(header.toc_length_compressed);
    fTOCUncompressedLength
        = B_BENDIAN_TO_HOST_INT64(header.toc_length_uncompressed);

    if (const char* errorString = _CheckCompression(fTOCCompression,
                                  fTOCCompressedLength, fTOCUncompressedLength)) {
        fErrorOutput->PrintError("Error: Invalid package file: TOC section: "
                                 "%s\n", errorString);
        return B_BAD_DATA;
    }

    // TOC subsections
    fTOCAttributeTypesLength
        = B_BENDIAN_TO_HOST_INT64(header.toc_attribute_types_length);
    fTOCAttributeTypesCount
        = B_BENDIAN_TO_HOST_INT64(header.toc_attribute_types_count);
    fTOCStringsLength = B_BENDIAN_TO_HOST_INT64(header.toc_strings_length);
    fTOCStringsCount = B_BENDIAN_TO_HOST_INT64(header.toc_strings_count);

    if (fTOCAttributeTypesLength > fTOCUncompressedLength
            || fTOCStringsLength > fTOCUncompressedLength - fTOCAttributeTypesLength
            || fTOCAttributeTypesCount > fTOCAttributeTypesLength
            || fTOCStringsCount > fTOCStringsLength) {
        fErrorOutput->PrintError("Error: Invalid package file: Invalid TOC "
                                 "subsections description\n");
        return B_BAD_DATA;
    }

    // check whether the sections fit together
    if (fPackageAttributesCompressedLength > fTotalSize
            || fTOCCompressedLength
            > fTotalSize - fPackageAttributesCompressedLength
            || fHeapOffset
            > fTotalSize - fPackageAttributesCompressedLength
            - fTOCCompressedLength) {
        fErrorOutput->PrintError("Error: Invalid package file: The sum of the "
                                 "sections sizes is greater than the package size\n");
        return B_BAD_DATA;
    }

    fPackageAttributesOffset = fTotalSize - fPackageAttributesCompressedLength;
    fTOCSectionOffset = fPackageAttributesOffset - fTOCCompressedLength;
    fHeapSize = fTOCSectionOffset - fHeapOffset;

    // TOC size sanity check
    if (fTOCUncompressedLength > kMaxTOCSize) {
        fErrorOutput->PrintError("Error: Package file TOC section size "
                                 "is %llu bytes. This is beyond the reader's sanity limit\n",
                                 fTOCUncompressedLength);
        return B_UNSUPPORTED;
    }

    // allocate a scratch buffer
    fScratchBuffer = new(std::nothrow) uint8[kScratchBufferSize];
    if (fScratchBuffer == NULL) {
        fErrorOutput->PrintError("Error: Out of memory!\n");
        return B_NO_MEMORY;
    }
    fScratchBufferSize = kScratchBufferSize;

    // read in the complete TOC
    fTOCSection = new(std::nothrow) uint8[fTOCUncompressedLength];
    if (fTOCSection == NULL) {
        fErrorOutput->PrintError("Error: Out of memory!\n");
        return B_NO_MEMORY;
    }

    error = _ReadCompressedBuffer(fTOCSectionOffset, fTOCSection,
                                  fTOCCompressedLength, fTOCUncompressedLength, fTOCCompression);
    if (error != B_OK)
        return error;

    // start parsing the TOC
    fCurrentTOCOffset = 0;

    // attribute types
    error = _ParseTOCAttributeTypes();
    if (error != B_OK)
        return error;
    fCurrentTOCOffset += fTOCAttributeTypesLength;

    // strings
    error = _ParseTOCStrings();
    if (error != B_OK)
        return error;
    fCurrentTOCOffset += fTOCStringsLength;

    return B_OK;
}
Beispiel #20
0
status_t
PackageReader::_ReadAttributeValue(uint8 type, uint8 encoding,
                                   AttributeValue& _value)
{
    switch (type) {
    case B_HPKG_ATTRIBUTE_TYPE_INT:
    case B_HPKG_ATTRIBUTE_TYPE_UINT:
    {
        uint64 intValue;
        status_t error;

        switch (encoding) {
        case B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT:
        {
            uint8 value;
            error = _Read(value);
            intValue = value;
            break;
        }
        case B_HPKG_ATTRIBUTE_ENCODING_INT_16_BIT:
        {
            uint16 value;
            error = _Read(value);
            intValue = B_BENDIAN_TO_HOST_INT16(value);
            break;
        }
        case B_HPKG_ATTRIBUTE_ENCODING_INT_32_BIT:
        {
            uint32 value;
            error = _Read(value);
            intValue = B_BENDIAN_TO_HOST_INT32(value);
            break;
        }
        case B_HPKG_ATTRIBUTE_ENCODING_INT_64_BIT:
        {
            uint64 value;
            error = _Read(value);
            intValue = B_BENDIAN_TO_HOST_INT64(value);
            break;
        }
        default:
        {
            fErrorOutput->PrintError("Error: Invalid TOC section: "
                                     "invalid encoding %d for int value type %d\n", encoding,
                                     type);
            return B_BAD_VALUE;
        }
        }

        if (error != B_OK)
            return error;

        if (type == B_HPKG_ATTRIBUTE_TYPE_INT)
            _value.SetTo((int64)intValue);
        else
            _value.SetTo(intValue);

        return B_OK;
    }

    case B_HPKG_ATTRIBUTE_TYPE_STRING:
    {
        if (encoding == B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE) {
            uint64 index;
            status_t error = _ReadUnsignedLEB128(index);
            if (error != B_OK)
                return error;

            if (index > fTOCStringsCount) {
                fErrorOutput->PrintError("Error: Invalid TOC section: "
                                         "string reference out of bounds\n");
                return B_BAD_DATA;
            }

            _value.SetTo(fStrings[index]);
        } else if (encoding == B_HPKG_ATTRIBUTE_ENCODING_STRING_INLINE) {
            const char* string;
            status_t error = _ReadString(string);
            if (error != B_OK)
                return error;

            _value.SetTo(string);
        } else {
            fErrorOutput->PrintError("Error: Invalid TOC section: invalid "
                                     "string encoding (%u)\n", encoding);
            return B_BAD_DATA;
        }

        return B_OK;
    }

    case B_HPKG_ATTRIBUTE_TYPE_RAW:
    {
        uint64 size;
        status_t error = _ReadUnsignedLEB128(size);
        if (error != B_OK)
            return error;

        if (encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP) {
            uint64 offset;
            error = _ReadUnsignedLEB128(offset);
            if (error != B_OK)
                return error;

            if (offset > fHeapSize || size > fHeapSize - offset) {
                fErrorOutput->PrintError("Error: Invalid TOC section: "
                                         "invalid data reference\n");
                return B_BAD_DATA;
            }

            _value.SetToData(size, fHeapOffset + offset);
        } else if (encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE) {
            if (size > B_HPKG_MAX_INLINE_DATA_SIZE) {
                fErrorOutput->PrintError("Error: Invalid TOC section: "
                                         "inline data too long\n");
                return B_BAD_DATA;
            }

            const void* buffer;
            error = _GetTOCBuffer(size, buffer);
            if (error != B_OK)
                return error;
            _value.SetToData(size, buffer);
        } else {
            fErrorOutput->PrintError("Error: Invalid TOC section: invalid "
                                     "raw encoding (%u)\n", encoding);
            return B_BAD_DATA;
        }

        return B_OK;
    }

    default:
        fErrorOutput->PrintError("Error: Invalid TOC section: invalid "
                                 "value type: %d\n", type);
        return B_BAD_DATA;
    }
}