Example #1
0
static float
identify_partition(int fd, partition_data *partition, void **cookie)
{
	DEBUG_INIT_ETC(NULL, ("fd: %d, id: %ld, offset: %Ld, "
		"size: %Ld, block_size: %ld, flags: 0x%lx", fd,
		partition->id, partition->offset, partition->size,
		partition->block_size, partition->flags));

	device_geometry geometry;
	float result = -1;
	if ((partition->flags & B_PARTITION_IS_DEVICE) != 0
		&& partition->block_size == 2048
		&& ioctl(fd, B_GET_GEOMETRY, &geometry) == 0
		&& geometry.device_type == B_CD) {
		Disc *disc = new(std::nothrow) Disc(fd);
		if (disc != NULL && disc->InitCheck() == B_OK) {
			// If we have only a single session then we can let the file system
			// drivers play directly with the device.
			Session *session = disc->GetSession(1);
			if (session != NULL) {
				result = 0.9f;
				delete session;
			} else
				result = 0.1f;

			*cookie = static_cast<void*>(disc);
		} else
			delete disc;
	}
	PRINT(("returning %g\n", result));
	return result;
}
Example #2
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;
}
Example #3
0
/*! \brief Returns the number of blocks needed to accomodate the
	given number of bytes.
*/
uint32
Allocator::BlocksFor(off_t bytes)
{
    if (BlockSize() == 0) {
        DEBUG_INIT_ETC("Allocator", ("bytes: %ld\n", bytes));
        PRINT(("WARNING: Allocator::BlockSize() == 0!\n"));
        return 0;
    } else {
        off_t blocks = bytes >> BlockShift();
        if (bytes % BlockSize() != 0)
            blocks++;
        uint64 mask = 0xffffffff;
        mask <<= 32;
        if (blocks & mask) {
            // ToDo: Convert this to actually signal an error
            DEBUG_INIT_ETC("Allocator", ("bytes: %ld\n", bytes));
            PRINT(("WARNING: bytes argument too large for corresponding number "
                   "of blocks to be specified with a uint32! (bytes: %Ld, blocks: %Ld, "
                   "maxblocks: %ld).\n", bytes, blocks, ULONG_MAX));
            blocks = 0;
        }
        return blocks;
    }
}
static status_t
udf_put_vnode(fs_volume *volume, fs_vnode *node, bool reenter)
{
	TRACE(("udf_put_vnode: volume = %p, node = %p\n", volume, node));
// No debug-to-file in release_vnode; can cause a deadlock in
// rare circumstances.
#if !DEBUG_TO_FILE
	DEBUG_INIT_ETC(NULL, ("node: %p", node));
#endif
	Icb *icb = (Icb *)node->private_node;
	delete icb;
#if !DEBUG_TO_FILE
	RETURN(B_OK);
#else
	return B_OK;
#endif
}
Example #5
0
static status_t
scan_partition(int fd, partition_data *partition, void *cookie)
{
	DEBUG_INIT_ETC(NULL, ("fd: %d, id: %ld, offset: %Ld, size: %Ld, "
		"block_size: %ld, cookie: %p", fd, partition->id, partition->offset,
		partition->size, partition->block_size, cookie));

	Disc *disc = static_cast<Disc*>(cookie);
	partition->status = B_PARTITION_VALID;
	partition->flags |= B_PARTITION_PARTITIONING_SYSTEM
		| B_PARTITION_READ_ONLY;
	partition->content_size = partition->size;

	Session *session = NULL;
	status_t error = B_OK;
	for (int i = 0; (session = disc->GetSession(i)); i++) {
		partition_data *child = create_child_partition(partition->id,
			i, partition->offset + session->Offset(), session->Size(), -1);
		if (!child) {
			PRINT(("Unable to create child at index %d.\n", i));
			// something went wrong
			error = B_ERROR;
			break;
		}
		child->block_size = session->BlockSize();
		child->flags |= session->Flags();
		child->type = strdup(session->Type());
		delete session;
		if (!child->type) {
			error = B_NO_MEMORY;
			break;
		}
		child->parameters = NULL;
	}
	PRINT(("error: 0x%lx, `%s'\n", error, strerror(error)));
	RETURN(error);
}
Example #6
0
/*! \brief Reads through the given table of contents data and creates an
	unsorted, unverified (i.e. non-error-checked) list of sessions and tracks.
*/
status_t
Disc::_ParseTableOfContents(cdrom_full_table_of_contents_entry entries[],
	uint32 count)
{
	DEBUG_INIT_ETC("Disc", ("entries: %p, count: %ld", entries, count));

	for (uint32 i = 0; i < count; i++) {
		// Find or create the appropriate session
		uint8 sessionIndex = entries[i].session;
		session* session = (struct session*)fSessionList->Find(sessionIndex);
		if (session == NULL) {
			session = new struct session(sessionIndex);
			if (session == NULL)
				return B_NO_MEMORY;

			fSessionList->Add(session);
		}

		uint8 point = entries[i].point;

		switch (point) {
			// first track hint
			case 0xA0:
				if (!session->first_track_hint_is_set()) {
					int8 firstTrackHint = entries[i].pminutes;
					if (1 <= firstTrackHint && firstTrackHint <= 99) {
						session->first_track_hint = firstTrackHint;
					} else {
						WARN(("%s: warning: illegal first track hint %d found "
							"for session %d\n", kModuleDebugName,
							firstTrackHint, sessionIndex));
					}
				} else {
					WARN(("%s: warning: duplicated first track hint values "
						"found for session %d; using first value "
						"encountered: %d", kModuleDebugName, sessionIndex,
						session->first_track_hint));
				}
				break;

			// last track hint
			case 0xA1:
				if (!session->last_track_hint_is_set()) {
					int8 lastTrackHint = entries[i].pminutes;
					if (1 <= lastTrackHint && lastTrackHint <= 99) {
						session->last_track_hint = lastTrackHint;
					} else {
						WARN(("%s: warning: illegal last track hint %d found "
							"for session %d\n", kModuleDebugName,
							lastTrackHint, sessionIndex));
					}
				} else {
					WARN(("%s: warning: duplicate last track hint values found "
						"for session %d; using first value encountered: %d",
						kModuleDebugName, sessionIndex,
						session->last_track_hint));
				}
				break;

			// end of session address
			case 0xA2:
				if (!session->end_lba_is_set()) {
					off_t endLBA = msf_to_lba(make_msf_address(
						entries[i].pminutes, entries[i].pseconds,
						entries[i].pframes));
					if (endLBA > 0) {
						session->end_lba = endLBA;
						// We also grab the session's control and adr values
						// from this entry
						session->control = entries[i].control;
						session->adr = entries[i].adr;
					} else {
						WARN(("%s: warning: illegal end lba %lld found for "
							"session %d\n", kModuleDebugName, endLBA,
							sessionIndex));
					}
				} else {
					WARN(("%s: warning: duplicate end lba values found for "
						"session %d; using first value encountered: %lld",
						kModuleDebugName, sessionIndex, session->end_lba));
				}
				break;

			// Valid, but uninteresting, points
			case 0xB0:
			case 0xB1:
			case 0xB2:
			case 0xB3:
			case 0xB4:
			case 0xC0:
			case 0xC1:
				break;

			default:
				// Anything else had better be a valid track number,
				// or it's an invalid point
				if (1 <= point && point <= 99) {
					// Create and add the track. We'll weed out any duplicates
					// later.
					uint8 trackIndex = point;
					off_t startLBA = msf_to_lba(make_msf_address(
						entries[i].pminutes, entries[i].pseconds,
						entries[i].pframes));
					// The control and adr values grabbed here are only used
					// later on to signal a warning if they don't match the
					// corresponding values of the parent session.
					track* track = new(std::nothrow) struct track(trackIndex,
						startLBA, entries[i].control, entries[i].adr);
					if (track == NULL)
						return B_NO_MEMORY;

					session->track_list.Add(track);
				} else {
					WARN(("%s: warning: illegal point 0x%2x found in table of "
						"contents\n", kModuleDebugName, entries[i].point));
				}
				break;
		}
	}
	return B_OK;
}
Example #7
0
/*! \brief Stores the info for the given session (using 0 based indicies) in the
	struct pointed to by \a sessionInfo.

	Returns \c B_ENTRY_NOT_FOUND if no such session exists.
*/
Session*
Disc::GetSession(int32 index)
{
	DEBUG_INIT_ETC("Disc", ("index: %ld", index));
	int32 counter = -1;
	for (session* session = (struct session*)fSessionList->First(); session;
			session = (struct session*)session->next) {
		if (session->is_audio()) {
			counter++;
				// only one session per audio session
			if (counter == index) {
				// Found an audio session. Take the start of the first
				// track with the end of session.
				track* track = (struct track*)session->track_list.First();
				if (track != NULL) {
					PRINT(("found session #%ld info (audio session)\n", index));

					off_t startLBA = track->start_lba;
					off_t endLBA = session->end_lba;

					off_t offset = startLBA * kBlockSize;
					off_t size = (endLBA - startLBA) * kBlockSize;

					Session* result = new Session(offset, size, kBlockSize,
						index, B_PARTITION_READ_ONLY,
						kPartitionTypeAudioSession);
					if (result == NULL) {
						PRINT(("Error allocating new Session object; out of "
							"memory!\n"));
					}
					return result;
				} else {
					PRINT(("Error: session #%ld is an audio session with no "
						"tracks!\n", index));
					return NULL;
				}
			}
		} else {
			for (track* track = (struct track*)session->track_list.First();
					track; track = (struct track*)track->next) {
				counter++;
				if (counter == index) {
					PRINT(("found session #%ld info (data session)\n", index));

					off_t startLBA = track->start_lba;
					if (startLBA < 0) {
						WARN(("%s: warning: invalid negative start LBA of %lld"
							" for data track assuming 0\n", kModuleDebugName,
							startLBA));
						startLBA = 0;
					}

					off_t endLBA = track->next
						? ((struct track*)track->next)->start_lba
						: session->end_lba;

					off_t offset = startLBA * kBlockSize;
					off_t size = (endLBA - startLBA) * kBlockSize;

					Session* result = new Session(offset, size, kBlockSize,
						index, B_PARTITION_READ_ONLY,
						kPartitionTypeDataSession);
					if (result == NULL) {
						PRINT(("Error allocating new Session object; out of "
							"memory!\n"));
					}
					return result;
				}
			}
		}
	}

	PRINT(("no session #%ld found!\n", index));
	return NULL;
}
Example #8
0
static status_t
read_table_of_contents(int deviceFD, uint32 first_session, uchar* buffer,
	uint16 buffer_length, bool msf)
{
	scsi_table_of_contents_command scsi_command;
	raw_device_command raw_command;
	const uint32 sense_data_length = 1024;
	uchar sense_data[sense_data_length];
	status_t error = buffer ? B_OK : B_BAD_VALUE;

	DEBUG_INIT_ETC(NULL, ("fd: %d, buffer: %p, buffer_length: %d",
		deviceFD, buffer, buffer_length));

	if (error)
		return error;

	// Init the scsi command and copy it into the "raw scsi command"
	// ioctl struct
	memset(raw_command.command, 0, 16);
	scsi_command.command = 0x43;
	scsi_command.msf = 1;
	scsi_command.format = kFullTableOfContentsFormat;
	scsi_command.number = first_session;
	scsi_command.length = B_HOST_TO_BENDIAN_INT16(buffer_length);
	scsi_command.control = 0;
	scsi_command.reserved0 = scsi_command.reserved1 = scsi_command.reserved2
		= scsi_command.reserved3 = scsi_command.reserved4
		= scsi_command.reserved5 = scsi_command.reserved6 = 0;
	memcpy(raw_command.command, &scsi_command, sizeof(scsi_command));

	// Init the rest of the raw command
	raw_command.command_length = 10;
	raw_command.flags = kScsiFlags;
	raw_command.scsi_status = 0;
	raw_command.cam_status = 0;
	raw_command.data = buffer;
	raw_command.data_length = buffer_length;
	memset(raw_command.data, 0, raw_command.data_length);
	raw_command.sense_data = sense_data;
	raw_command.sense_data_length = sense_data_length;
	memset(raw_command.sense_data, 0, raw_command.sense_data_length);
	raw_command.timeout = kScsiTimeout;

	if (ioctl(deviceFD, B_RAW_DEVICE_COMMAND, &raw_command) == 0) {
		if (raw_command.scsi_status == 0 && raw_command.cam_status == 1) {
			// SUCCESS!!!
			DBG(dump_full_table_of_contents(buffer, buffer_length));
		} else {
			error = B_FILE_ERROR;
			TRACE(("%s: scsi ioctl succeeded, but scsi command failed\n",
				kModuleDebugName));
		}
	} else {
		error = errno;
		TRACE(("%s: scsi command failed with error 0x%lx\n", kModuleDebugName,
			error));
	}

	return error;

}
Example #9
0
static void
free_identify_partition_cookie(partition_data */*partition*/, void *cookie)
{
	DEBUG_INIT_ETC(NULL, ("cookie: %p", cookie));
	delete static_cast<Disc*>(cookie);
}
Example #10
0
/*! \brief Allocates the next available extent of given length.

	\param length The desired length (in bytes) of the extent.
	\param contiguous If false, signals that an extent of shorter length will
	                  be accepted. This allows for small chunks of
	                  unallocated space to be consumed, provided a
	                  contiguous chunk is not needed.
	\param extent Output parameter into which the extent as allocated
	              is stored. Note that the length field of the extent
	              may be shorter than the length parameter passed
	              to this function is \a contiguous is false.
	\param minimumStartingBlock The minimum acceptable starting block
	                            for the extent (used by the physical
	                            partition allocator).

	\return
	- B_OK: Success.
	- error code: Failure.
*/
status_t
Allocator::GetNextExtent(uint32 _length, bool contiguous,
                         Udf::extent_address &extent,
                         uint32 minimumStartingBlock)
{
    DEBUG_INIT_ETC("Allocator", ("length: %lld, contiguous: %d", _length, contiguous));
    uint32 length = BlocksFor(_length);
    bool isPartial = false;
    status_t error = InitCheck();
    PRINT(("allocation length: %lu\n", Length()));
    if (!error) {
        for (list<Udf::extent_address>::iterator i = fChunkList.begin();
                i != fChunkList.end();
                i++)
        {
            uint32 chunkOffset = i->location();
            uint32 chunkLength = BlocksFor(i->length());
            if (chunkOffset < minimumStartingBlock)
            {
                if (minimumStartingBlock < chunkOffset+chunkLength) {
                    // Start of chunk is below min starting block. See if
                    // any part of the chunk would make for an acceptable
                    // allocation
                    uint32 difference = minimumStartingBlock - chunkOffset;
                    uint32 newOffset = minimumStartingBlock;
                    uint32 newLength = chunkLength-difference;
                    if (length <= newLength) {
                        // new chunk is still long enough
                        Udf::extent_address newExtent(newOffset, _length);
                        if (GetExtent(newExtent) == B_OK) {
                            extent = newExtent;
                            return B_OK;
                        }
                    } else if (!contiguous) {
                        // new chunk is too short, but we're allowed to
                        // allocate a shorter extent, so we'll do it.
                        Udf::extent_address newExtent(newOffset, newLength<<BlockShift());
                        if (GetExtent(newExtent) == B_OK) {
                            extent = newExtent;
                            return B_OK;
                        }
                    }
                }
            } else if (length <= chunkLength) {
                // Chunk is larger than necessary. Allocate first
                // length blocks, and resize the chunk appropriately.
                extent.set_location(chunkOffset);
                extent.set_length(_length);
                if (length != chunkLength) {
                    i->set_location(chunkOffset+length);
                    i->set_length((chunkLength-length)<<BlockShift());
                } else {
                    fChunkList.erase(i);
                }
                return B_OK;
            } else if (!contiguous) {
                extent.set_location(chunkOffset);
                extent.set_length(chunkLength<<BlockShift());
                fChunkList.erase(i);
                return B_OK;
            }
        }
        // No sufficient chunk found, so try to allocate from the tail
        PRINT(("ULONG_MAX: %lu\n", ULONG_MAX));
        uint32 maxLength = ULONG_MAX-Length();
        PRINT(("maxLength: %lu\n", maxLength));
        error = maxLength > 0 ? B_OK : B_DEVICE_FULL;
        if (!error) {
            if (minimumStartingBlock > Tail())
                maxLength -= minimumStartingBlock - Tail();
            uint32 tail = minimumStartingBlock > Tail() ? minimumStartingBlock : Tail();
            if (length > maxLength) {
                if (contiguous)
                    error = B_DEVICE_FULL;
                else {
                    isPartial = true;
                    length = maxLength;
                }
            }
            if (!error) {
                Udf::extent_address newExtent(tail, isPartial ? length<<BlockShift() : _length);
                if (GetExtent(newExtent) == B_OK) {
                    extent = newExtent;
                    return B_OK;
                }
            }
        }
    }
    return error;
}