Start of a CDDA file system (replacement for cdda as part of BeOS):
* doesn't do anything useful yet, but it compiles (and should be mountable). * CDDA test app "cdda_text" will dump the table of contents as well as eventually existing CD-Text - if I had known only so few titles in my collection would come with those, I probably wouldn't have gone through it (if the samples I used are representable, it's about 1/10th of the CDs I have) :-) * cdda_text compiles on BeOS, so if you have known CD-Text CDs, please test it and report eventual problems - the test app already tries to beautify the names as much as possible. Usage is "cdda_text <path-to-raw-device>". * The test app will be removed later, so better test now ;) git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21109 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -2,6 +2,7 @@ SubDir HAIKU_TOP src add-ons kernel file_systems ;
|
|||||||
|
|
||||||
SubInclude HAIKU_TOP src add-ons kernel file_systems dos ;
|
SubInclude HAIKU_TOP src add-ons kernel file_systems dos ;
|
||||||
SubInclude HAIKU_TOP src add-ons kernel file_systems bfs ;
|
SubInclude HAIKU_TOP src add-ons kernel file_systems bfs ;
|
||||||
|
SubInclude HAIKU_TOP src add-ons kernel file_systems cdda ;
|
||||||
SubInclude HAIKU_TOP src add-ons kernel file_systems googlefs ;
|
SubInclude HAIKU_TOP src add-ons kernel file_systems googlefs ;
|
||||||
SubInclude HAIKU_TOP src add-ons kernel file_systems iso9660 ;
|
SubInclude HAIKU_TOP src add-ons kernel file_systems iso9660 ;
|
||||||
SubInclude HAIKU_TOP src add-ons kernel file_systems nfs ;
|
SubInclude HAIKU_TOP src add-ons kernel file_systems nfs ;
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
SubDir HAIKU_TOP src add-ons kernel file_systems cdda ;
|
||||||
|
|
||||||
|
UsePrivateHeaders [ FDirName kernel ] ;
|
||||||
|
UsePrivateHeaders [ FDirName kernel disk_device_manager ] ;
|
||||||
|
UsePrivateHeaders [ FDirName storage ] ;
|
||||||
|
|
||||||
|
KernelAddon cdda :
|
||||||
|
kernel_interface.cpp
|
||||||
|
;
|
||||||
|
|
||||||
|
SimpleTest cdda_text :
|
||||||
|
cdda.cpp
|
||||||
|
: be ;
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2001-2007, Axel Dörfler, [email protected].
|
||||||
|
* This file may be used under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef LOCK_H
|
||||||
|
#define LOCK_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <OS.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define USE_BENAPHORE
|
||||||
|
// if defined, benaphores are used for the Semaphore class
|
||||||
|
|
||||||
|
class Semaphore {
|
||||||
|
public:
|
||||||
|
Semaphore(const char *name)
|
||||||
|
:
|
||||||
|
#ifdef USE_BENAPHORE
|
||||||
|
fSemaphore(create_sem(0, name)),
|
||||||
|
fCount(1)
|
||||||
|
#else
|
||||||
|
fSemaphore(create_sem(1, name))
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~Semaphore()
|
||||||
|
{
|
||||||
|
delete_sem(fSemaphore);
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t InitCheck()
|
||||||
|
{
|
||||||
|
if (fSemaphore < B_OK)
|
||||||
|
return fSemaphore;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t Lock()
|
||||||
|
{
|
||||||
|
#ifdef USE_BENAPHORE
|
||||||
|
if (atomic_add(&fCount, -1) <= 0)
|
||||||
|
#endif
|
||||||
|
return acquire_sem(fSemaphore);
|
||||||
|
#ifdef USE_BENAPHORE
|
||||||
|
return B_OK;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t Unlock()
|
||||||
|
{
|
||||||
|
#ifdef USE_BENAPHORE
|
||||||
|
if (atomic_add(&fCount, 1) < 0)
|
||||||
|
#endif
|
||||||
|
return release_sem(fSemaphore);
|
||||||
|
#ifdef USE_BENAPHORE
|
||||||
|
return B_OK;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
sem_id fSemaphore;
|
||||||
|
#ifdef USE_BENAPHORE
|
||||||
|
vint32 fCount;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
// a convenience class to lock a Semaphore object
|
||||||
|
|
||||||
|
class Locker {
|
||||||
|
public:
|
||||||
|
Locker(Semaphore &lock)
|
||||||
|
: fLock(lock)
|
||||||
|
{
|
||||||
|
fStatus = lock.Lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
~Locker()
|
||||||
|
{
|
||||||
|
if (fStatus == B_OK)
|
||||||
|
fLock.Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t Status() const
|
||||||
|
{
|
||||||
|
return fStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Semaphore &fLock;
|
||||||
|
status_t fStatus;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* LOCK_H */
|
||||||
@@ -0,0 +1,532 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007, Axel Dörfler, [email protected].
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <bus/scsi/scsi_cmds.h>
|
||||||
|
#include <device/scsi.h>
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define kFramesPerSecond 75
|
||||||
|
#define kFramesPerMinute (kFramesPerSecond * 60)
|
||||||
|
|
||||||
|
struct cdtext_pack_data {
|
||||||
|
uint8 id;
|
||||||
|
uint8 track;
|
||||||
|
uint8 number;
|
||||||
|
uint8 character_position : 4;
|
||||||
|
uint8 block_number : 3;
|
||||||
|
uint8 double_byte : 1;
|
||||||
|
char text[12];
|
||||||
|
uint8 crc[2];
|
||||||
|
} _PACKED;
|
||||||
|
|
||||||
|
|
||||||
|
static const uint8 kMaxTracks = 0x63;
|
||||||
|
enum {
|
||||||
|
kTrackID = 0x80,
|
||||||
|
kArtistID = 0x81,
|
||||||
|
kMessageID = 0x85,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cdtext {
|
||||||
|
char *artist;
|
||||||
|
char *album;
|
||||||
|
char *titles[kMaxTracks];
|
||||||
|
char *artists[kMaxTracks];
|
||||||
|
uint8 track_count;
|
||||||
|
char *genre;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - string functions
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
is_garbage(char c)
|
||||||
|
{
|
||||||
|
return isspace(c) || c == '-' || c == '/' || c == '\\';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
sanitize_string(char *string)
|
||||||
|
{
|
||||||
|
if (string == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// strip garbage at the start
|
||||||
|
|
||||||
|
uint32 length = strlen(string);
|
||||||
|
uint32 garbage = 0;
|
||||||
|
while (is_garbage(string[garbage])) {
|
||||||
|
garbage++;
|
||||||
|
}
|
||||||
|
|
||||||
|
length -= garbage;
|
||||||
|
if (garbage)
|
||||||
|
memmove(string, string + garbage, length + 1);
|
||||||
|
|
||||||
|
// strip garbage from the end
|
||||||
|
|
||||||
|
while (length > 1 && isspace(string[length - 1])) {
|
||||||
|
string[--length] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! Finds the first occurrence of \a find in \a string, ignores case.
|
||||||
|
static char*
|
||||||
|
find_string(const char *string, const char *find)
|
||||||
|
{
|
||||||
|
if (string == NULL || find == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
char first = tolower(find[0]);
|
||||||
|
if (first == '\0')
|
||||||
|
return (char *)string;
|
||||||
|
|
||||||
|
int32 findLength = strlen(find) - 1;
|
||||||
|
find++;
|
||||||
|
|
||||||
|
for (; string[0]; string++) {
|
||||||
|
if (tolower(string[0]) != first)
|
||||||
|
continue;
|
||||||
|
if (strncasecmp(string + 1, find, findLength) == 0)
|
||||||
|
return (char *)string;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
cut_string(char *string, char *cut)
|
||||||
|
{
|
||||||
|
if (string == NULL || cut == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
char *found = find_string(string, cut);
|
||||||
|
if (found != NULL) {
|
||||||
|
uint32 foundLength = strlen(found);
|
||||||
|
uint32 cutLength = strlen(cut);
|
||||||
|
memmove(found, found + cutLength, foundLength + 1 - cutLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
sanitize_album(cdtext &text)
|
||||||
|
{
|
||||||
|
cut_string(text.album, text.artist);
|
||||||
|
sanitize_string(text.album);
|
||||||
|
|
||||||
|
if ((text.artist == NULL || !text.artist[0]) && text.album != NULL) {
|
||||||
|
// try to extract artist from album
|
||||||
|
char *space = strstr(text.album, " ");
|
||||||
|
if (space != NULL) {
|
||||||
|
space[0] = '\0';
|
||||||
|
text.artist = text.album;
|
||||||
|
text.album = strdup(space + 2);
|
||||||
|
|
||||||
|
sanitize_string(text.artist);
|
||||||
|
sanitize_string(text.album);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
sanitize_titles(cdtext &text)
|
||||||
|
{
|
||||||
|
for (uint8 i = 0; i < text.track_count; i++) {
|
||||||
|
cut_string(text.titles[i], "(Album Version)");
|
||||||
|
sanitize_string(text.titles[i]);
|
||||||
|
sanitize_string(text.artists[i]);
|
||||||
|
if (!strcasecmp(text.artists[i], text.artist)) {
|
||||||
|
// if the title artist is the same as the main artist, remove it
|
||||||
|
free(text.artists[i]);
|
||||||
|
text.artists[i] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.titles[i] != NULL && text.titles[i][0] == '\t' && i > 0)
|
||||||
|
text.titles[i] = strdup(text.titles[i - 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
single_case(const char *string, bool &upper, bool &first)
|
||||||
|
{
|
||||||
|
if (string == NULL)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
while (string[0]) {
|
||||||
|
while (!isalpha(string[0])) {
|
||||||
|
string++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (first) {
|
||||||
|
upper = isupper(string[0]) != 0;
|
||||||
|
first = false;
|
||||||
|
} else if ((isupper(string[0]) != 0) ^ upper)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
string++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
capitalize_string(char *string)
|
||||||
|
{
|
||||||
|
if (string == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool newWord = isalpha(string[0]) || isspace(string[0]);
|
||||||
|
while (string[0]) {
|
||||||
|
if (isalpha(string[0])) {
|
||||||
|
if (newWord) {
|
||||||
|
string[0] = toupper(string[0]);
|
||||||
|
newWord = false;
|
||||||
|
} else
|
||||||
|
string[0] = tolower(string[0]);
|
||||||
|
} else if (string[0] != '\'')
|
||||||
|
newWord = true;
|
||||||
|
|
||||||
|
string++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
correct_case(cdtext &text)
|
||||||
|
{
|
||||||
|
// check if all titles share a single case
|
||||||
|
bool first = true;
|
||||||
|
bool upper;
|
||||||
|
if (!single_case(text.album, upper, first)
|
||||||
|
|| !single_case(text.artist, upper, first))
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int32 i = 0; i < text.track_count; i++) {
|
||||||
|
if (!single_case(text.titles[i], upper, first)
|
||||||
|
|| !single_case(text.artists[i], upper, first))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we get here, everything has a single case; we fix that
|
||||||
|
// and capitalize each word
|
||||||
|
|
||||||
|
capitalize_string(text.album);
|
||||||
|
capitalize_string(text.artist);
|
||||||
|
for (int32 i = 0; i < text.track_count; i++) {
|
||||||
|
capitalize_string(text.titles[i]);
|
||||||
|
capitalize_string(text.artists[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - CD-Text
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
is_string_id(uint8 id)
|
||||||
|
{
|
||||||
|
return id >= kTrackID && id <= kMessageID;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
parse_pack_data(cdtext_pack_data *&pack, uint32 &packLeft,
|
||||||
|
cdtext_pack_data *&lastPack, uint8 &id, uint8 &track, uint8 &state,
|
||||||
|
char *buffer, size_t &length)
|
||||||
|
{
|
||||||
|
if (packLeft < sizeof(cdtext_pack_data))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint8 number = pack->number;
|
||||||
|
size_t size = length;
|
||||||
|
|
||||||
|
if (state != 0) {
|
||||||
|
// we had a terminated string and a missing track
|
||||||
|
track++;
|
||||||
|
memcpy(buffer, lastPack->text + state, 12 - state);
|
||||||
|
if (pack->track - track == 1)
|
||||||
|
state = 0;
|
||||||
|
else
|
||||||
|
state += strnlen(buffer, 12 - state);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
id = pack->id;
|
||||||
|
track = pack->track;
|
||||||
|
buffer[0] = '\0';
|
||||||
|
length = 0;
|
||||||
|
|
||||||
|
size_t position = pack->character_position;
|
||||||
|
if (position > 0 && lastPack != NULL) {
|
||||||
|
memcpy(buffer, &lastPack->text[12 - position], position);
|
||||||
|
length = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (id == pack->id && track == pack->track) {
|
||||||
|
#if 1
|
||||||
|
printf("%u.%u.%u, %u.%u.%u, ", pack->id, pack->track, pack->number,
|
||||||
|
pack->double_byte, pack->block_number, pack->character_position);
|
||||||
|
for (int32 i = 0; i < 12; i++) {
|
||||||
|
if (isprint(pack->text[i]))
|
||||||
|
putchar(pack->text[i]);
|
||||||
|
}
|
||||||
|
putchar('\n');
|
||||||
|
#endif
|
||||||
|
if (is_string_id(id)) {
|
||||||
|
// TODO: support double byte characters
|
||||||
|
if (length + 12 < size) {
|
||||||
|
memcpy(buffer + length, pack->text, 12);
|
||||||
|
length += 12;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
packLeft -= sizeof(cdtext_pack_data);
|
||||||
|
if (packLeft < sizeof(cdtext_pack_data))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
lastPack = pack;
|
||||||
|
number++;
|
||||||
|
pack++;
|
||||||
|
|
||||||
|
if (pack->number != number)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id == pack->id) {
|
||||||
|
length -= pack->character_position;
|
||||||
|
if (length >= size)
|
||||||
|
length = size - 1;
|
||||||
|
buffer[length] = '\0';
|
||||||
|
|
||||||
|
if (pack->track > lastPack->track + 1) {
|
||||||
|
// there is a missing track
|
||||||
|
for (int32 i = 0; i < 12; i++) {
|
||||||
|
if (lastPack->text[i] == '\0') {
|
||||||
|
state = i + (lastPack->double_byte ? 2 : 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: convert text to UTF-8
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
dump_cdtext(cdtext_pack_data *pack, size_t packLength)
|
||||||
|
{
|
||||||
|
cdtext_pack_data *lastPack = NULL;
|
||||||
|
char buffer[256];
|
||||||
|
uint8 state = 0;
|
||||||
|
cdtext text;
|
||||||
|
|
||||||
|
memset(&text, 0, sizeof(cdtext));
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
size_t length = sizeof(buffer);
|
||||||
|
uint8 id, track;
|
||||||
|
|
||||||
|
if (!parse_pack_data(pack, packLength, lastPack, id, track,
|
||||||
|
state, buffer, length))
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (id) {
|
||||||
|
case kTrackID:
|
||||||
|
if (track == 0) {
|
||||||
|
if (text.album == NULL)
|
||||||
|
text.album = strdup(buffer);
|
||||||
|
} else if (track <= kMaxTracks) {
|
||||||
|
if (text.titles[track - 1] == NULL)
|
||||||
|
text.titles[track - 1] = strdup(buffer);
|
||||||
|
if (track > text.track_count)
|
||||||
|
text.track_count = track;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kArtistID:
|
||||||
|
if (track == 0) {
|
||||||
|
if (text.artist == NULL)
|
||||||
|
text.artist = strdup(buffer);
|
||||||
|
} else if (track <= kMaxTracks) {
|
||||||
|
if (text.artists[track - 1] == NULL)
|
||||||
|
text.artists[track - 1] = strdup(buffer);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (is_string_id(id))
|
||||||
|
printf("UNKNOWN %u: \"%s\"\n", id, buffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sanitize_string(text.artist);
|
||||||
|
sanitize_album(text);
|
||||||
|
sanitize_titles(text);
|
||||||
|
correct_case(text);
|
||||||
|
|
||||||
|
if (text.album)
|
||||||
|
printf("Album: \"%s\"\n", text.album);
|
||||||
|
if (text.artist)
|
||||||
|
printf("Artist: \"%s\"\n", text.artist);
|
||||||
|
for (uint8 i = 0; i < text.track_count; i++) {
|
||||||
|
printf("Track %02u: \"%s\"%s%s%s\n", i + 1, text.titles[i],
|
||||||
|
text.artists[i] ? " (" : "", text.artists[i] ? text.artists[i] : "",
|
||||||
|
text.artists[i] ? ")" : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
dump_toc(scsi_toc_toc *toc)
|
||||||
|
{
|
||||||
|
int32 numTracks = toc->last_track + 1 - toc->first_track;
|
||||||
|
|
||||||
|
for (int32 i = 0; i < numTracks; i++) {
|
||||||
|
scsi_toc_track& track = toc->tracks[i];
|
||||||
|
scsi_cd_msf& next = toc->tracks[i + 1].start.time;
|
||||||
|
// the last track is always lead-out
|
||||||
|
scsi_cd_msf& start = toc->tracks[i].start.time;
|
||||||
|
scsi_cd_msf length;
|
||||||
|
|
||||||
|
uint64 diff = next.minute * kFramesPerMinute
|
||||||
|
+ next.second * kFramesPerSecond + next.frame
|
||||||
|
- start.minute * kFramesPerMinute
|
||||||
|
- start.second * kFramesPerSecond - start.frame;
|
||||||
|
length.minute = diff / kFramesPerMinute;
|
||||||
|
length.second = (diff % kFramesPerMinute) / kFramesPerSecond;
|
||||||
|
length.frame = diff % kFramesPerSecond;
|
||||||
|
|
||||||
|
printf("%02u. %02u:%02u.%02u (length %02u:%02u.%02u)\n",
|
||||||
|
track.track_number, start.minute, start.second, start.frame,
|
||||||
|
length.minute, length.second, length.frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
read_table_of_contents(int fd, uint32 track, uint8 format, uint8 *buffer,
|
||||||
|
size_t bufferSize)
|
||||||
|
{
|
||||||
|
raw_device_command raw;
|
||||||
|
uint8 senseData[1024];
|
||||||
|
|
||||||
|
memset(&raw, 0, sizeof(raw_device_command));
|
||||||
|
memset(senseData, 0, sizeof(senseData));
|
||||||
|
memset(buffer, 0, bufferSize);
|
||||||
|
|
||||||
|
scsi_cmd_read_toc &toc = *(scsi_cmd_read_toc*)&raw.command;
|
||||||
|
toc.opcode = SCSI_OP_READ_TOC;
|
||||||
|
toc.time = 1;
|
||||||
|
toc.format = format;
|
||||||
|
toc.track = track;
|
||||||
|
toc.allocation_length = B_HOST_TO_BENDIAN_INT16(bufferSize);
|
||||||
|
|
||||||
|
raw.command_length = 10;
|
||||||
|
raw.flags = B_RAW_DEVICE_DATA_IN | B_RAW_DEVICE_REPORT_RESIDUAL
|
||||||
|
| B_RAW_DEVICE_SHORT_READ_VALID;
|
||||||
|
raw.scsi_status = 0;
|
||||||
|
raw.cam_status = 0;
|
||||||
|
raw.data = buffer;
|
||||||
|
raw.data_length = bufferSize;
|
||||||
|
raw.timeout = 10000000LL; // 10 secs
|
||||||
|
raw.sense_data = senseData;
|
||||||
|
raw.sense_data_length = sizeof(senseData);
|
||||||
|
|
||||||
|
if (ioctl(fd, B_RAW_DEVICE_COMMAND, &raw) == 0) {
|
||||||
|
if (raw.scsi_status == 0 && raw.cam_status == 1) {
|
||||||
|
puts("success!\n");
|
||||||
|
} else {
|
||||||
|
puts("failure!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
dump_block(const uint8 *buffer, int size, const char *prefix)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < size;)
|
||||||
|
{
|
||||||
|
int start = i;
|
||||||
|
|
||||||
|
printf(prefix);
|
||||||
|
for (; i < start+16; i++)
|
||||||
|
{
|
||||||
|
if (!(i % 4))
|
||||||
|
printf(" ");
|
||||||
|
|
||||||
|
if (i >= size)
|
||||||
|
printf(" ");
|
||||||
|
else
|
||||||
|
printf("%02x", *(unsigned char *)(buffer + i));
|
||||||
|
}
|
||||||
|
printf(" ");
|
||||||
|
|
||||||
|
for (i = start; i < start + 16; i++)
|
||||||
|
{
|
||||||
|
if (i < size)
|
||||||
|
{
|
||||||
|
char c = buffer[i];
|
||||||
|
|
||||||
|
if (c < 30)
|
||||||
|
printf(".");
|
||||||
|
else
|
||||||
|
printf("%c", c);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
int fd = open(argv[1], O_RDONLY);
|
||||||
|
if (fd < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
uint8 buffer[1024];
|
||||||
|
scsi_toc_general *header = (scsi_toc_general *)buffer;
|
||||||
|
|
||||||
|
read_table_of_contents(fd, 1, SCSI_TOC_FORMAT_TOC, buffer, sizeof(buffer));
|
||||||
|
header->data_length = B_BENDIAN_TO_HOST_INT16(header->data_length);
|
||||||
|
printf("TOC header %u, %d, %d\n", header->data_length, header->first, header->last);
|
||||||
|
//dump_block(buffer, header->data_length + 2, "TOC");
|
||||||
|
dump_toc((scsi_toc_toc *)buffer);
|
||||||
|
|
||||||
|
read_table_of_contents(fd, 1, SCSI_TOC_FORMAT_CD_TEXT, buffer, sizeof(buffer));
|
||||||
|
read_table_of_contents(fd, 1, SCSI_TOC_FORMAT_CD_TEXT, buffer, sizeof(buffer));
|
||||||
|
header->data_length = B_BENDIAN_TO_HOST_INT16(header->data_length);
|
||||||
|
printf("TEXT header %u, %d, %d\n", header->data_length, header->first, header->last);
|
||||||
|
//dump_block(buffer, header->data_length + 2, "TEXT");
|
||||||
|
|
||||||
|
dump_cdtext((cdtext_pack_data *)(buffer + 4), header->data_length - 2);
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
@@ -0,0 +1,826 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007, Axel Dörfler, [email protected].
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "Lock.h"
|
||||||
|
|
||||||
|
#include <fs_info.h>
|
||||||
|
#include <fs_interface.h>
|
||||||
|
#include <KernelExport.h>
|
||||||
|
|
||||||
|
#include <util/kernel_cpp.h>
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
|
||||||
|
//#define TRACE_CDDA
|
||||||
|
#ifdef TRACE_CDDA
|
||||||
|
# define TRACE(x) dprintf x
|
||||||
|
#else
|
||||||
|
# define TRACE(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
class Volume;
|
||||||
|
class Inode;
|
||||||
|
struct dir_cookie;
|
||||||
|
|
||||||
|
class Volume {
|
||||||
|
public:
|
||||||
|
Volume(mount_id id);
|
||||||
|
~Volume();
|
||||||
|
|
||||||
|
status_t InitCheck();
|
||||||
|
mount_id ID() const { return fID; }
|
||||||
|
Inode &RootNode() const { return *fRootNode; }
|
||||||
|
|
||||||
|
status_t Mount(const char* device);
|
||||||
|
int Device() const { return fDevice; }
|
||||||
|
vnode_id GetNextNodeID() { return fNextID++; }
|
||||||
|
|
||||||
|
const char *Name() const { return fName; }
|
||||||
|
status_t SetName(const char *name);
|
||||||
|
|
||||||
|
Semaphore &Lock();
|
||||||
|
|
||||||
|
Inode *Find(vnode_id id);
|
||||||
|
Inode *Find(const char *name);
|
||||||
|
|
||||||
|
Inode *FirstEntry() const { return fFirstEntry; }
|
||||||
|
off_t NumBlocks() const { return fNumBlocks; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Inode *_CreateNode(Inode *parent, const char *name, int32 type);
|
||||||
|
|
||||||
|
Semaphore fLock;
|
||||||
|
int fDevice;
|
||||||
|
mount_id fID;
|
||||||
|
Inode *fRootNode;
|
||||||
|
vnode_id fNextID;
|
||||||
|
char *fName;
|
||||||
|
off_t fNumBlocks;
|
||||||
|
|
||||||
|
// root directory contents - we don't support other directories
|
||||||
|
Inode *fFirstEntry;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Inode {
|
||||||
|
public:
|
||||||
|
Inode(Volume *volume, Inode *parent, const char *name, int32 type);
|
||||||
|
~Inode();
|
||||||
|
|
||||||
|
status_t InitCheck();
|
||||||
|
vnode_id ID() const { return fID; }
|
||||||
|
|
||||||
|
const char *Name() const { return fName; }
|
||||||
|
status_t SetName(const char* name);
|
||||||
|
|
||||||
|
int32 Type() const
|
||||||
|
{ return fType; }
|
||||||
|
gid_t GroupID() const
|
||||||
|
{ return fGroupID; }
|
||||||
|
uid_t UserID() const
|
||||||
|
{ return fUserID; }
|
||||||
|
time_t CreationTime() const
|
||||||
|
{ return fCreationTime; }
|
||||||
|
time_t ModificationTime() const
|
||||||
|
{ return fModificationTime; }
|
||||||
|
off_t Size() const
|
||||||
|
{ return fSize; }
|
||||||
|
|
||||||
|
Inode *Next() const { return fNext; }
|
||||||
|
void SetNext(Inode *inode) { fNext = inode; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Inode *fNext;
|
||||||
|
vnode_id fID;
|
||||||
|
int32 fType;
|
||||||
|
const char *fName;
|
||||||
|
gid_t fGroupID;
|
||||||
|
uid_t fUserID;
|
||||||
|
time_t fCreationTime;
|
||||||
|
time_t fModificationTime;
|
||||||
|
off_t fSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct dir_cookie {
|
||||||
|
Inode *current;
|
||||||
|
int state; // iteration state
|
||||||
|
};
|
||||||
|
|
||||||
|
// directory iteration states
|
||||||
|
enum {
|
||||||
|
ITERATION_STATE_DOT = 0,
|
||||||
|
ITERATION_STATE_DOT_DOT = 1,
|
||||||
|
ITERATION_STATE_OTHERS = 2,
|
||||||
|
ITERATION_STATE_BEGIN = ITERATION_STATE_DOT,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct file_cookie {
|
||||||
|
int open_mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Volume::Volume(mount_id id)
|
||||||
|
:
|
||||||
|
fLock("cdda"),
|
||||||
|
fDevice(-1),
|
||||||
|
fID(id),
|
||||||
|
fRootNode(NULL),
|
||||||
|
fNextID(1),
|
||||||
|
fName(NULL),
|
||||||
|
fNumBlocks(0),
|
||||||
|
fFirstEntry(NULL)
|
||||||
|
{
|
||||||
|
// create the root vnode
|
||||||
|
fRootNode = _CreateNode(NULL, "", S_IFDIR | 0777);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Volume::~Volume()
|
||||||
|
{
|
||||||
|
close(fDevice);
|
||||||
|
|
||||||
|
// put_vnode on the root to release the ref to it
|
||||||
|
if (fRootNode)
|
||||||
|
put_vnode(ID(), fRootNode->ID());
|
||||||
|
|
||||||
|
Inode *inode, *next;
|
||||||
|
|
||||||
|
for (inode = fFirstEntry; inode != NULL; inode = next) {
|
||||||
|
next = inode->Next();
|
||||||
|
delete inode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
Volume::InitCheck()
|
||||||
|
{
|
||||||
|
if (fLock.InitCheck() < B_OK
|
||||||
|
|| fRootNode == NULL)
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
Volume::Mount(const char* device)
|
||||||
|
{
|
||||||
|
fDevice = open(device, O_RDONLY);
|
||||||
|
if (fDevice < 0)
|
||||||
|
return errno;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Semaphore&
|
||||||
|
Volume::Lock()
|
||||||
|
{
|
||||||
|
return fLock;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Inode *
|
||||||
|
Volume::_CreateNode(Inode *parent, const char *name, int32 type)
|
||||||
|
{
|
||||||
|
Inode *inode = new Inode(this, parent, name, type);
|
||||||
|
if (inode == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (inode->InitCheck() != B_OK) {
|
||||||
|
delete inode;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (S_ISREG(type)) {
|
||||||
|
inode->SetNext(fFirstEntry);
|
||||||
|
fFirstEntry = inode;
|
||||||
|
}
|
||||||
|
|
||||||
|
publish_vnode(ID(), inode->ID(), inode);
|
||||||
|
return inode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Inode *
|
||||||
|
Volume::Find(vnode_id id)
|
||||||
|
{
|
||||||
|
for (Inode *inode = fFirstEntry; inode != NULL; inode = inode->Next()) {
|
||||||
|
if (inode->ID() == id)
|
||||||
|
return inode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Inode *
|
||||||
|
Volume::Find(const char *name)
|
||||||
|
{
|
||||||
|
if (!strcmp(name, ".")
|
||||||
|
|| !strcmp(name, ".."))
|
||||||
|
return fRootNode;
|
||||||
|
|
||||||
|
for (Inode *inode = fFirstEntry; inode != NULL; inode = inode->Next()) {
|
||||||
|
if (!strcmp(inode->Name(), name))
|
||||||
|
return inode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
Volume::SetName(const char *name)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
Inode::Inode(Volume *volume, Inode *parent, const char *name, int32 type)
|
||||||
|
:
|
||||||
|
fNext(NULL)
|
||||||
|
{
|
||||||
|
fName = strdup(name);
|
||||||
|
if (fName == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fID = volume->GetNextNodeID();
|
||||||
|
fType = type;
|
||||||
|
|
||||||
|
fUserID = geteuid();
|
||||||
|
fGroupID = parent ? parent->GroupID() : getegid();
|
||||||
|
|
||||||
|
fCreationTime = fModificationTime = time(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Inode::~Inode()
|
||||||
|
{
|
||||||
|
free(const_cast<char *>(fName));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
Inode::InitCheck()
|
||||||
|
{
|
||||||
|
if (fName == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
Inode::SetName(const char* name)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - module API
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
cdda_mount(mount_id id, const char *device, uint32 flags, const char *args,
|
||||||
|
fs_volume *_volume, vnode_id *_rootVnodeID)
|
||||||
|
{
|
||||||
|
TRACE(("cdda_mount: entry\n"));
|
||||||
|
|
||||||
|
Volume *volume = new Volume(id);
|
||||||
|
if (volume == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
status_t status = volume->InitCheck();
|
||||||
|
if (status == B_OK)
|
||||||
|
status = volume->Mount(device);
|
||||||
|
|
||||||
|
if (status < B_OK) {
|
||||||
|
delete volume;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
*_rootVnodeID = volume->RootNode().ID();
|
||||||
|
*_volume = volume;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
cdda_unmount(fs_volume _volume)
|
||||||
|
{
|
||||||
|
struct Volume *volume = (struct Volume *)_volume;
|
||||||
|
|
||||||
|
TRACE(("cdda_unmount: entry fs = %p\n", _volume));
|
||||||
|
delete volume;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
cdda_read_fs_stat(fs_volume _volume, struct fs_info *info)
|
||||||
|
{
|
||||||
|
Volume *volume = (Volume *)_volume;
|
||||||
|
Locker locker(volume->Lock());
|
||||||
|
|
||||||
|
// File system flags.
|
||||||
|
info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR | B_FS_HAS_MIME
|
||||||
|
| B_FS_IS_REMOVABLE;
|
||||||
|
info->io_size = 65536;
|
||||||
|
|
||||||
|
info->block_size = 2048;
|
||||||
|
info->total_blocks = volume->NumBlocks();
|
||||||
|
info->free_blocks = 0;
|
||||||
|
|
||||||
|
// Volume name
|
||||||
|
strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name));
|
||||||
|
|
||||||
|
// File system name
|
||||||
|
strlcpy(info->fsh_name, "cdda", sizeof(info->fsh_name));
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
cdda_write_fs_stat(fs_volume _volume, const struct fs_info *info, uint32 mask)
|
||||||
|
{
|
||||||
|
Volume *volume = (Volume *)_volume;
|
||||||
|
Locker locker(volume->Lock());
|
||||||
|
|
||||||
|
status_t status = B_BAD_VALUE;
|
||||||
|
|
||||||
|
if (mask & FS_WRITE_FSINFO_NAME)
|
||||||
|
status = volume->SetName(info->volume_name);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
cdda_sync(fs_volume fs)
|
||||||
|
{
|
||||||
|
TRACE(("cdda_sync: entry\n"));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
cdda_lookup(fs_volume _volume, fs_vnode _dir, const char *name, vnode_id *_id, int *_type)
|
||||||
|
{
|
||||||
|
Volume *volume = (Volume *)_volume;
|
||||||
|
status_t status;
|
||||||
|
|
||||||
|
TRACE(("cdda_lookup: entry dir %p, name '%s'\n", _dir, name));
|
||||||
|
|
||||||
|
Inode *directory = (Inode *)_dir;
|
||||||
|
if (!S_ISDIR(directory->Type()))
|
||||||
|
return B_NOT_A_DIRECTORY;
|
||||||
|
|
||||||
|
Locker _(volume->Lock());
|
||||||
|
|
||||||
|
Inode *inode = volume->Find(name);
|
||||||
|
if (inode == NULL)
|
||||||
|
return B_ENTRY_NOT_FOUND;
|
||||||
|
|
||||||
|
Inode *dummy;
|
||||||
|
status = get_vnode(volume->ID(), inode->ID(), (fs_vnode *)&dummy);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
*_id = inode->ID();
|
||||||
|
*_type = inode->Type();
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
cdda_get_vnode_name(fs_volume _volume, fs_vnode _node, char *buffer, size_t bufferSize)
|
||||||
|
{
|
||||||
|
Volume *volume = (Volume *)_volume;
|
||||||
|
Inode *inode = (Inode *)_node;
|
||||||
|
|
||||||
|
TRACE(("cdda_get_vnode_name(): inode = %p\n", inode));
|
||||||
|
|
||||||
|
Locker _(volume->Lock());
|
||||||
|
strlcpy(buffer, inode->Name(), bufferSize);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
cdda_get_vnode(fs_volume _volume, vnode_id id, fs_vnode *_inode, bool reenter)
|
||||||
|
{
|
||||||
|
Volume *volume = (Volume *)_volume;
|
||||||
|
Inode *inode;
|
||||||
|
|
||||||
|
TRACE(("cdda_getvnode: asking for vnode 0x%Lx, r %d\n", id, reenter));
|
||||||
|
|
||||||
|
inode = volume->Find(id);
|
||||||
|
if (inode == NULL)
|
||||||
|
return B_ENTRY_NOT_FOUND;
|
||||||
|
|
||||||
|
*_inode = inode;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
cdda_put_vnode(fs_volume _volume, fs_vnode _node, bool reenter)
|
||||||
|
{
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
cdda_open(fs_volume _volume, fs_vnode _node, int openMode, fs_cookie *_cookie)
|
||||||
|
{
|
||||||
|
TRACE(("cdda_open(): node = %p, openMode = %d\n", _node, openMode));
|
||||||
|
|
||||||
|
file_cookie *cookie = (file_cookie *)malloc(sizeof(file_cookie));
|
||||||
|
if (cookie == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
TRACE((" open cookie = %p\n", cookie));
|
||||||
|
cookie->open_mode = openMode;
|
||||||
|
|
||||||
|
*_cookie = (void *)cookie;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
cdda_close(fs_volume _volume, fs_vnode _node, fs_cookie _cookie)
|
||||||
|
{
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
cdda_free_cookie(fs_volume _volume, fs_vnode _node, fs_cookie _cookie)
|
||||||
|
{
|
||||||
|
file_cookie *cookie = (file_cookie *)_cookie;
|
||||||
|
|
||||||
|
TRACE(("cdda_freecookie: entry vnode %p, cookie %p\n", _node, _cookie));
|
||||||
|
|
||||||
|
free(cookie);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
cdda_fsync(fs_volume _volume, fs_vnode _v)
|
||||||
|
{
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
cdda_read(fs_volume _volume, fs_vnode _node, fs_cookie _cookie, off_t offset,
|
||||||
|
void *buffer, size_t *_length)
|
||||||
|
{
|
||||||
|
file_cookie *cookie = (file_cookie *)_cookie;
|
||||||
|
Inode *inode = (Inode *)_node;
|
||||||
|
|
||||||
|
TRACE(("cdda_read(vnode = %p, offset %Ld, length = %lu, mode = %d)\n",
|
||||||
|
_node, offset, *_length, cookie->open_mode));
|
||||||
|
|
||||||
|
if (S_ISDIR(inode->Type()))
|
||||||
|
return B_IS_A_DIRECTORY;
|
||||||
|
if ((cookie->open_mode & O_RWMASK) != O_RDONLY)
|
||||||
|
return B_NOT_ALLOWED;
|
||||||
|
|
||||||
|
// TODO: read!
|
||||||
|
*_length = 0;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool
|
||||||
|
cdda_can_page(fs_volume _volume, fs_vnode _v, fs_cookie cookie)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
cdda_read_pages(fs_volume _volume, fs_vnode _v, fs_cookie cookie, off_t pos,
|
||||||
|
const iovec *vecs, size_t count, size_t *_numBytes, bool reenter)
|
||||||
|
{
|
||||||
|
return EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
cdda_write_pages(fs_volume _volume, fs_vnode _v, fs_cookie cookie, off_t pos,
|
||||||
|
const iovec *vecs, size_t count, size_t *_numBytes, bool reenter)
|
||||||
|
{
|
||||||
|
return EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
cdda_read_stat(fs_volume _volume, fs_vnode _node, struct stat *stat)
|
||||||
|
{
|
||||||
|
Volume *volume = (Volume *)_volume;
|
||||||
|
Inode *inode = (Inode *)_node;
|
||||||
|
|
||||||
|
TRACE(("cdda_read_stat: vnode %p (0x%Lx), stat %p\n", inode, inode->ID(), stat));
|
||||||
|
|
||||||
|
stat->st_dev = volume->ID();
|
||||||
|
stat->st_ino = inode->ID();
|
||||||
|
|
||||||
|
stat->st_size = inode->Size();
|
||||||
|
stat->st_mode = inode->Type();
|
||||||
|
|
||||||
|
stat->st_nlink = 1;
|
||||||
|
stat->st_blksize = 2048;
|
||||||
|
|
||||||
|
stat->st_uid = inode->UserID();
|
||||||
|
stat->st_gid = inode->GroupID();
|
||||||
|
|
||||||
|
stat->st_atime = time(NULL);
|
||||||
|
stat->st_mtime = stat->st_ctime = inode->ModificationTime();
|
||||||
|
stat->st_crtime = inode->CreationTime();
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
cdda_rename(fs_volume _volume, void *_oldDir, const char *oldName, void *_newDir,
|
||||||
|
const char *newName)
|
||||||
|
{
|
||||||
|
if (_volume == NULL || _oldDir == NULL || _newDir == NULL
|
||||||
|
|| oldName == NULL || *oldName == '\0'
|
||||||
|
|| newName == NULL || *newName == '\0'
|
||||||
|
|| !strcmp(oldName, ".") || !strcmp(oldName, "..")
|
||||||
|
|| !strcmp(newName, ".") || !strcmp(newName, "..")
|
||||||
|
|| strchr(newName, '/') != NULL)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - directory functions
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
cdda_open_dir(fs_volume _volume, fs_vnode _node, fs_cookie *_cookie)
|
||||||
|
{
|
||||||
|
Volume *volume = (Volume *)_volume;
|
||||||
|
|
||||||
|
TRACE(("cdda_open_dir(): vnode = %p\n", _node));
|
||||||
|
|
||||||
|
Inode *inode = (Inode *)_node;
|
||||||
|
if (!S_ISDIR(inode->Type()))
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
if (inode != &volume->RootNode())
|
||||||
|
panic("pipefs: found directory that's not the root!");
|
||||||
|
|
||||||
|
dir_cookie *cookie = (dir_cookie *)malloc(sizeof(dir_cookie));
|
||||||
|
if (cookie == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
cookie->current = volume->FirstEntry();
|
||||||
|
cookie->state = ITERATION_STATE_BEGIN;
|
||||||
|
|
||||||
|
*_cookie = (void *)cookie;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
cdda_read_dir(fs_volume _volume, fs_vnode _node, fs_cookie _cookie,
|
||||||
|
struct dirent *dirent, size_t bufferSize, uint32 *_num)
|
||||||
|
{
|
||||||
|
Volume *volume = (Volume *)_volume;
|
||||||
|
Inode *inode = (Inode *)_node;
|
||||||
|
status_t status = 0;
|
||||||
|
|
||||||
|
TRACE(("cdda_read_dir: vnode %p, cookie %p, buffer = %p, bufferSize = %ld, num = %p\n", _node, _cookie, dirent, bufferSize,_num));
|
||||||
|
|
||||||
|
if (_node != &volume->RootNode())
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
Locker _(volume->Lock());
|
||||||
|
|
||||||
|
dir_cookie *cookie = (dir_cookie *)_cookie;
|
||||||
|
Inode *childNode = NULL;
|
||||||
|
const char *name = NULL;
|
||||||
|
Inode *nextChildNode = NULL;
|
||||||
|
int nextState = cookie->state;
|
||||||
|
|
||||||
|
switch (cookie->state) {
|
||||||
|
case ITERATION_STATE_DOT:
|
||||||
|
childNode = inode;
|
||||||
|
name = ".";
|
||||||
|
nextChildNode = volume->FirstEntry();
|
||||||
|
nextState = cookie->state + 1;
|
||||||
|
break;
|
||||||
|
case ITERATION_STATE_DOT_DOT:
|
||||||
|
childNode = inode; // parent of the root node is the root node
|
||||||
|
name = "..";
|
||||||
|
nextChildNode = volume->FirstEntry();
|
||||||
|
nextState = cookie->state + 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
childNode = cookie->current;
|
||||||
|
if (childNode) {
|
||||||
|
name = childNode->Name();
|
||||||
|
nextChildNode = childNode->Next();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!childNode) {
|
||||||
|
// we're at the end of the directory
|
||||||
|
*_num = 0;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
dirent->d_dev = volume->ID();
|
||||||
|
dirent->d_ino = inode->ID();
|
||||||
|
dirent->d_reclen = strlen(name) + sizeof(struct dirent);
|
||||||
|
|
||||||
|
if (dirent->d_reclen > bufferSize)
|
||||||
|
return ENOBUFS;
|
||||||
|
|
||||||
|
status = user_strlcpy(dirent->d_name, name, bufferSize);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
cookie->current = nextChildNode;
|
||||||
|
cookie->state = nextState;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
cdda_rewind_dir(fs_volume _volume, fs_vnode _vnode, fs_cookie _cookie)
|
||||||
|
{
|
||||||
|
Volume *volume = (Volume *)_volume;
|
||||||
|
|
||||||
|
dir_cookie *cookie = (dir_cookie *)_cookie;
|
||||||
|
cookie->current = volume->FirstEntry();
|
||||||
|
cookie->state = ITERATION_STATE_BEGIN;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
cdda_close_dir(fs_volume _volume, fs_vnode _node, fs_cookie _cookie)
|
||||||
|
{
|
||||||
|
TRACE(("cdda_close: entry vnode %p, cookie %p\n", _node, _cookie));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
cdda_free_dir_cookie(fs_volume _volume, fs_vnode _vnode, fs_cookie _cookie)
|
||||||
|
{
|
||||||
|
dir_cookie *cookie = (dir_cookie *)_cookie;
|
||||||
|
|
||||||
|
TRACE(("cdda_freecookie: entry vnode %p, cookie %p\n", _vnode, cookie));
|
||||||
|
|
||||||
|
free(cookie);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
cdda_std_ops(int32 op, ...)
|
||||||
|
{
|
||||||
|
switch (op) {
|
||||||
|
case B_MODULE_INIT:
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
case B_MODULE_UNINIT:
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static file_system_module_info sCDDAFileSystem = {
|
||||||
|
{
|
||||||
|
"file_systems/cdda" B_CURRENT_FS_API_VERSION,
|
||||||
|
0,
|
||||||
|
cdda_std_ops,
|
||||||
|
},
|
||||||
|
|
||||||
|
"CDDA File System",
|
||||||
|
|
||||||
|
NULL, // identify_partition()
|
||||||
|
NULL, // scan_partition()
|
||||||
|
NULL, // free_identify_partition_cookie()
|
||||||
|
NULL, // free_partition_content_cookie()
|
||||||
|
|
||||||
|
&cdda_mount,
|
||||||
|
&cdda_unmount,
|
||||||
|
&cdda_read_fs_stat,
|
||||||
|
&cdda_write_fs_stat,
|
||||||
|
&cdda_sync,
|
||||||
|
|
||||||
|
&cdda_lookup,
|
||||||
|
&cdda_get_vnode_name,
|
||||||
|
|
||||||
|
&cdda_get_vnode,
|
||||||
|
&cdda_put_vnode,
|
||||||
|
NULL, // fs_remove_vnode()
|
||||||
|
|
||||||
|
&cdda_can_page,
|
||||||
|
&cdda_read_pages,
|
||||||
|
&cdda_write_pages,
|
||||||
|
|
||||||
|
NULL, // get_file_map()
|
||||||
|
|
||||||
|
// common
|
||||||
|
NULL, // fs_ioctl()
|
||||||
|
NULL, // fs_set_flags()
|
||||||
|
NULL, // fs_select()
|
||||||
|
NULL, // fs_deselect()
|
||||||
|
&cdda_fsync,
|
||||||
|
|
||||||
|
NULL, // fs_read_link()
|
||||||
|
NULL, // fs_symlink()
|
||||||
|
NULL, // fs_link()
|
||||||
|
NULL, // fs_unlink()
|
||||||
|
NULL, // fs_rename()
|
||||||
|
|
||||||
|
NULL, // fs_access()
|
||||||
|
&cdda_read_stat,
|
||||||
|
NULL, // fs_write_stat()
|
||||||
|
|
||||||
|
// file
|
||||||
|
NULL, // fs_create()
|
||||||
|
&cdda_open,
|
||||||
|
&cdda_close,
|
||||||
|
&cdda_free_cookie,
|
||||||
|
&cdda_read,
|
||||||
|
NULL, // fs_write()
|
||||||
|
|
||||||
|
// directory
|
||||||
|
NULL, // fs_create_dir()
|
||||||
|
NULL, // fs_remove_dir()
|
||||||
|
&cdda_open_dir,
|
||||||
|
&cdda_close_dir,
|
||||||
|
&cdda_free_dir_cookie,
|
||||||
|
&cdda_read_dir,
|
||||||
|
&cdda_rewind_dir,
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// attribute directory operations
|
||||||
|
&cdda_open_attr_dir,
|
||||||
|
&cdda_close_attr_dir,
|
||||||
|
&cdda_free_attr_dir_cookie,
|
||||||
|
&cdda_read_attr_dir,
|
||||||
|
&cdda_rewind_attr_dir,
|
||||||
|
|
||||||
|
// attribute operations
|
||||||
|
&cdda_create_attr,
|
||||||
|
&cdda_open_attr,
|
||||||
|
&cdda_close_attr,
|
||||||
|
&cdda_free_attr_cookie,
|
||||||
|
&cdda_read_attr,
|
||||||
|
&cdda_write_attr,
|
||||||
|
|
||||||
|
&cdda_read_attr_stat,
|
||||||
|
&cdda_write_attr_stat,
|
||||||
|
&cdda_rename_attr,
|
||||||
|
&cdda_remove_attr,
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// the other operations are not yet supported (indices, queries)
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_info *modules[] = {
|
||||||
|
(module_info *)&sCDDAFileSystem,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user