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:
Axel Dörfler 2007-05-11 12:25:02 +00:00
parent 6f709e745c
commit ff2a7d86f8
5 changed files with 1468 additions and 0 deletions

View File

@ -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 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 iso9660 ;
SubInclude HAIKU_TOP src add-ons kernel file_systems nfs ;

View File

@ -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 ;

View File

@ -0,0 +1,96 @@
/*
* Copyright 2001-2007, Axel Dörfler, axeld@pinc-software.de.
* 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 */

View File

@ -0,0 +1,532 @@
/*
* Copyright 2007, Axel Dörfler, axeld@pinc-software.de.
* 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);
}

View File

@ -0,0 +1,826 @@
/*
* Copyright 2007, Axel Dörfler, axeld@pinc-software.de.
* 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,
};