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; }
/*! \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; }
/*! \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 }
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); }
/*! \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; }
/*! \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; }
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; }
static void free_identify_partition_cookie(partition_data */*partition*/, void *cookie) { DEBUG_INIT_ETC(NULL, ("cookie: %p", cookie)); delete static_cast<Disc*>(cookie); }
/*! \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; }