xref: /haiku/src/add-ons/kernel/file_systems/cdda/kernel_interface.cpp (revision 1b8f7f13a3dc70e0e903cb94248220b40b732204)
1 /*
2  * Copyright 2007, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "cdda.h"
8 #include "cddb.h"
9 #include "Lock.h"
10 
11 #include <FindDirectory.h>
12 #include <fs_info.h>
13 #include <fs_interface.h>
14 #include <KernelExport.h>
15 #include <Mime.h>
16 #include <TypeConstants.h>
17 
18 #include <util/kernel_cpp.h>
19 #include <util/DoublyLinkedList.h>
20 
21 #include <dirent.h>
22 #include <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/stat.h>
27 
28 
29 //#define TRACE_CDDA
30 #ifdef TRACE_CDDA
31 #	define TRACE(x) dprintf x
32 #else
33 #	define TRACE(x)
34 #endif
35 
36 
37 class Attribute;
38 class Inode;
39 struct attr_cookie;
40 struct dir_cookie;
41 
42 typedef DoublyLinkedList<Attribute> AttributeList;
43 typedef DoublyLinkedList<attr_cookie> AttrCookieList;
44 
45 struct riff_header {
46 	uint32	magic;
47 	uint32	length;
48 	uint32	id;
49 } _PACKED;
50 
51 struct riff_chunk {
52 	uint32	fourcc;
53 	uint32	length;
54 } _PACEKD;
55 
56 struct wav_format_chunk : riff_chunk {
57 	uint16		format_tag;
58 	uint16		channels;
59 	uint32		samples_per_second;
60 	uint32		average_bytes_per_second;
61 	uint16		block_align;
62 	uint16		bits_per_sample;
63 } _PACKED;
64 
65 struct wav_header {
66 	riff_header			header;
67 	wav_format_chunk	format;
68 	riff_chunk			data;
69 } _PACKED;
70 
71 enum attr_mode {
72 	kDiscIDAttributes,
73 	kSharedAttributes,
74 	kDeviceAttributes
75 };
76 
77 class Volume {
78 	public:
79 		Volume(dev_t id);
80 		~Volume();
81 
82 		status_t	InitCheck();
83 		dev_t		ID() const { return fID; }
84 		uint32		DiscID() const { return fDiscID; }
85 		Inode		&RootNode() const { return *fRootNode; }
86 
87 		status_t	Mount(const char* device);
88 		int			Device() const { return fDevice; }
89 		ino_t		GetNextNodeID() { return fNextID++; }
90 
91 		const char	*Name() const { return fName; }
92 		status_t	SetName(const char *name);
93 
94 		Semaphore	&Lock();
95 
96 		Inode		*Find(ino_t id);
97 		Inode		*Find(const char *name);
98 
99 		Inode		*FirstEntry() const { return fFirstEntry; }
100 
101 		off_t		NumBlocks() const { return fNumBlocks; }
102 		size_t		BufferSize() const { return 32 * kFrameSize; }
103 			// TODO: for now
104 
105 		static void	DetermineName(cdtext &text, char *name, size_t length);
106 
107 	private:
108 		Inode		*_CreateNode(Inode *parent, const char *name,
109 						off_t start, off_t frames, int32 type);
110 		int			_OpenAttributes(int mode,
111 						enum attr_mode attrMode = kDiscIDAttributes);
112 		void		_RestoreAttributes();
113 		void		_StoreAttributes();
114 		void		_RestoreSharedAttributes();
115 		void		_StoreSharedAttributes();
116 
117 		Semaphore	fLock;
118 		int			fDevice;
119 		dev_t		fID;
120 		uint32		fDiscID;
121 		Inode 		*fRootNode;
122 		ino_t		fNextID;
123 		char		*fName;
124 		off_t		fNumBlocks;
125 
126 		// root directory contents - we don't support other directories
127 		Inode		*fFirstEntry;
128 };
129 
130 class Attribute : public DoublyLinkedListLinkImpl<Attribute> {
131 	public:
132 		Attribute(const char *name, type_code type);
133 		~Attribute();
134 
135 		status_t InitCheck() const { return fName != NULL ? B_OK : B_NO_MEMORY; }
136 		status_t SetTo(const char *name, type_code type);
137 		void SetType(type_code type) { fType = type; }
138 
139 		status_t ReadAt(off_t offset, uint8 *buffer, size_t *_length);
140 		status_t WriteAt(off_t offset, const uint8 *buffer, size_t *_length);
141 		void Truncate();
142 		status_t SetSize(off_t size);
143 
144 		const char *Name() const { return fName; }
145 		size_t Size() const { return fSize; }
146 		type_code Type() const { return fType; }
147 		uint8 *Data() const { return fData; }
148 
149 	private:
150 		char		*fName;
151 		type_code	fType;
152 		uint8		*fData;
153 		size_t		fSize;
154 };
155 
156 class Inode {
157 	public:
158 		Inode(Volume *volume, Inode *parent, const char *name, off_t start,
159 			off_t frames, int32 type);
160 		~Inode();
161 
162 		status_t	InitCheck();
163 		ino_t		ID() const { return fID; }
164 
165 		const char	*Name() const { return fName; }
166 		status_t	SetName(const char* name);
167 
168 		int32		Type() const
169 						{ return fType; }
170 		gid_t		GroupID() const
171 						{ return fGroupID; }
172 		uid_t		UserID() const
173 						{ return fUserID; }
174 		time_t		CreationTime() const
175 						{ return fCreationTime; }
176 		time_t		ModificationTime() const
177 						{ return fModificationTime; }
178 		off_t		StartFrame() const
179 						{ return fStartFrame; }
180 		off_t		FrameCount() const
181 						{ return fFrameCount; }
182 		off_t		Size() const
183 						{ return fFrameCount * kFrameSize /* + WAV header */; }
184 
185 		Attribute	*FindAttribute(const char *name) const;
186 		status_t	AddAttribute(Attribute *attribute, bool overwrite);
187 		status_t	AddAttribute(const char *name, type_code type,
188 						bool overwrite, const uint8 *data = 0,
189 						size_t length = 0);
190 		status_t	AddAttribute(const char *name, type_code type,
191 						const char *string);
192 		status_t	AddAttribute(const char *name, int32 value);
193 		status_t	RemoveAttribute(const char *name);
194 
195 		void		AddAttrCookie(attr_cookie *cookie);
196 		void		RemoveAttrCookie(attr_cookie *cookie);
197 		void		RewindAttrCookie(attr_cookie *cookie);
198 
199 		AttributeList::ConstIterator Attributes() const
200 						{ return fAttributes.GetIterator(); }
201 
202 		const wav_header *WAVHeader() const { return &fWAVHeader; }
203 
204 		Inode		*Next() const { return fNext; }
205 		void		SetNext(Inode *inode) { fNext = inode; }
206 
207 	private:
208 		Inode		*fNext;
209 		ino_t		fID;
210 		int32		fType;
211 		char		*fName;
212 		gid_t		fGroupID;
213 		uid_t		fUserID;
214 		time_t		fCreationTime;
215 		time_t		fModificationTime;
216 		off_t		fStartFrame;
217 		off_t		fFrameCount;
218 		AttributeList fAttributes;
219 		AttrCookieList fAttrCookies;
220 		wav_header	fWAVHeader;
221 };
222 
223 struct dir_cookie {
224 	Inode		*current;
225 	int			state;	// iteration state
226 };
227 
228 // directory iteration states
229 enum {
230 	ITERATION_STATE_DOT		= 0,
231 	ITERATION_STATE_DOT_DOT	= 1,
232 	ITERATION_STATE_OTHERS	= 2,
233 	ITERATION_STATE_BEGIN	= ITERATION_STATE_DOT,
234 };
235 
236 struct attr_cookie : DoublyLinkedListLinkImpl<attr_cookie> {
237 	Attribute	*current;
238 };
239 
240 struct file_cookie {
241 	int			open_mode;
242 	off_t		buffer_offset;
243 	void		*buffer;
244 };
245 
246 static const uint32 kMaxAttributeSize = 65536;
247 static const uint32 kMaxAttributes = 64;
248 
249 
250 //	#pragma mark helper functions
251 
252 
253 /*!
254 	Determines if the attribute is shared among all devices or among
255 	all CDs in a specific device.
256 	We use this to share certain Tracker attributes.
257 */
258 static bool
259 is_special_attribute(const char *name, attr_mode attrMode)
260 {
261 	if (attrMode == kDeviceAttributes) {
262 		static const char *kAttributes[] = {
263 			"_trk/windframe",
264 			"_trk/pinfo",
265 			"_trk/pinfo_le",
266 			NULL,
267 		};
268 
269 		for (int32 i = 0; kAttributes[i]; i++) {
270 			if (!strcmp(name, kAttributes[i]))
271 				return true;
272 		}
273 	} else if (attrMode == kSharedAttributes) {
274 		static const char *kAttributes[] = {
275 			"_trk/columns",
276 			"_trk/columns_le",
277 			"_trk/viewstate",
278 			"_trk/viewstate_le",
279 			NULL,
280 		};
281 
282 		for (int32 i = 0; kAttributes[i]; i++) {
283 			if (!strcmp(name, kAttributes[i]))
284 				return true;
285 		}
286 	}
287 
288 	return false;
289 }
290 
291 
292 static void
293 write_line(int fd, const char *line)
294 {
295 	if (line == NULL)
296 		line = "";
297 
298 	size_t length = strlen(line);
299 	write(fd, line, length);
300 	write(fd, "\n", 1);
301 }
302 
303 
304 static void
305 write_attributes(int fd, Inode *inode, attr_mode attrMode = kDiscIDAttributes)
306 {
307 	// count attributes
308 
309 	AttributeList::ConstIterator iterator = inode->Attributes();
310 	uint32 count = 0;
311 	while (iterator.HasNext()) {
312 		Attribute *attribute = iterator.Next();
313 		if (attrMode == kDiscIDAttributes
314 			|| is_special_attribute(attribute->Name(), attrMode))
315 			count++;
316 	}
317 
318 	// we're artificially limiting the attribute count per inode
319 	if (count > kMaxAttributes)
320 		count = kMaxAttributes;
321 
322 	count = B_HOST_TO_BENDIAN_INT32(count);
323 	write(fd, &count, sizeof(uint32));
324 
325 	// write attributes
326 
327 	iterator.Rewind();
328 
329 	while (iterator.HasNext()) {
330 		Attribute *attribute = iterator.Next();
331 		if (attrMode != kDiscIDAttributes
332 			&& !is_special_attribute(attribute->Name(), attrMode))
333 			continue;
334 
335 		uint32 type = B_HOST_TO_BENDIAN_INT32(attribute->Type());
336 		write(fd, &type, sizeof(uint32));
337 
338 		uint8 length = strlen(attribute->Name());
339 		write(fd, &length, 1);
340 		write(fd, attribute->Name(), length);
341 
342 		uint32 size = B_HOST_TO_BENDIAN_INT32(attribute->Size());
343 		write(fd, &size, sizeof(uint32));
344 		if (size != 0)
345 			write(fd, attribute->Data(), attribute->Size());
346 
347 		if (--count == 0)
348 			break;
349 	}
350 }
351 
352 
353 static bool
354 read_line(int fd, char *line, size_t length)
355 {
356 	bool first = true;
357 	size_t pos = 0;
358 	char c;
359 
360 	while (read(fd, &c, 1) == 1) {
361 		first = false;
362 
363 		if (c == '\n')
364 			break;
365 		if (pos < length)
366 			line[pos] = c;
367 
368 		pos++;
369 	}
370 
371 	if (pos >= length - 1)
372 		pos = length - 1;
373 	line[pos] = '\0';
374 
375 	return !first;
376 }
377 
378 
379 static bool
380 read_attributes(int fd, Inode *inode)
381 {
382 	uint32 count;
383 	if (read(fd, &count, sizeof(uint32)) != (ssize_t)sizeof(uint32))
384 		return false;
385 
386 	count = B_BENDIAN_TO_HOST_INT32(count);
387 dprintf("inode %s read %lu attrs\n", inode->Name(), count);
388 	if (count > kMaxAttributes)
389 		return false;
390 
391 	for (uint32 i = 0; i < count; i++) {
392 		char name[B_ATTR_NAME_LENGTH + 1];
393 		uint32 type, size;
394 		uint8 length;
395 		if (read(fd, &type, sizeof(uint32)) != (ssize_t)sizeof(uint32)
396 			|| read(fd, &length, 1) != 1
397 			|| read(fd, name, length) != length
398 			|| read(fd, &size, sizeof(uint32)) != (ssize_t)sizeof(uint32))
399 			return false;
400 
401 		type = B_BENDIAN_TO_HOST_INT32(type);
402 		size = B_BENDIAN_TO_HOST_INT32(size);
403 		name[length] = '\0';
404 dprintf("  type %08lx, size %lu, name %s\n", type, size, name);
405 
406 		Attribute *attribute = new Attribute(name, type);
407 		if (attribute->SetSize(size) != B_OK
408 			|| inode->AddAttribute(attribute, true) != B_OK) {
409 			delete attribute;
410 		} else
411 			read(fd, attribute->Data(), size);
412 	}
413 
414 	return true;
415 }
416 
417 
418 static void
419 fill_stat_buffer(Volume *volume, Inode *inode, Attribute *attribute,
420 	struct stat &stat)
421 {
422 	stat.st_dev = volume->ID();
423 	stat.st_ino = inode->ID();
424 
425 	if (attribute != NULL) {
426 		stat.st_size = attribute->Size();
427 		stat.st_mode = S_ATTR | 0666;
428 		stat.st_type = attribute->Type();
429 	} else {
430 		stat.st_size = inode->Size();
431 		stat.st_mode = inode->Type();
432 		stat.st_type = 0;
433 	}
434 
435 	stat.st_nlink = 1;
436 	stat.st_blksize = 2048;
437 
438 	stat.st_uid = inode->UserID();
439 	stat.st_gid = inode->GroupID();
440 
441 	stat.st_atime = time(NULL);
442 	stat.st_mtime = stat.st_ctime = inode->ModificationTime();
443 	stat.st_crtime = inode->CreationTime();
444 }
445 
446 
447 bool
448 is_data_track(const scsi_toc_track& track)
449 {
450 	return (track.control & 4) != 0;
451 }
452 
453 
454 uint32
455 count_audio_tracks(scsi_toc_toc* toc)
456 {
457 	uint32 lastTrack = toc->last_track + 1 - toc->first_track;
458 	uint32 count = 0;
459 	for (uint32 i = 0; i < lastTrack; i++) {
460 		if (!is_data_track(toc->tracks[i]))
461 			count++;
462 	}
463 
464 	return count;
465 }
466 
467 
468 //	#pragma mark - Volume class
469 
470 
471 Volume::Volume(dev_t id)
472 	:
473 	fLock("cdda"),
474 	fDevice(-1),
475 	fID(id),
476 	fRootNode(NULL),
477 	fNextID(1),
478 	fName(NULL),
479 	fNumBlocks(0),
480 	fFirstEntry(NULL)
481 {
482 }
483 
484 
485 Volume::~Volume()
486 {
487 	_StoreAttributes();
488 	_StoreSharedAttributes();
489 
490 	close(fDevice);
491 
492 	// put_vnode on the root to release the ref to it
493 	if (fRootNode)
494 		put_vnode(ID(), fRootNode->ID());
495 
496 	delete fRootNode;
497 
498 	Inode *inode, *next;
499 
500 	for (inode = fFirstEntry; inode != NULL; inode = next) {
501 		next = inode->Next();
502 		delete inode;
503 	}
504 
505 	free(fName);
506 }
507 
508 
509 status_t
510 Volume::InitCheck()
511 {
512 	if (fLock.InitCheck() < B_OK)
513 		return B_ERROR;
514 
515 	return B_OK;
516 }
517 
518 
519 /*static*/ void
520 Volume::DetermineName(cdtext &text, char *name, size_t length)
521 {
522 	if (text.artist != NULL && text.album != NULL)
523 		snprintf(name, length, "%s - %s", text.artist, text.album);
524 	else if (text.artist != NULL || text.album != NULL) {
525 		snprintf(name, length, "%s", text.artist != NULL
526 			? text.artist : text.album);
527 	} else
528 		strlcpy(name, "Audio CD", length);
529 }
530 
531 
532 status_t
533 Volume::Mount(const char* device)
534 {
535 	fDevice = open(device, O_RDONLY);
536 	if (fDevice < 0)
537 		return errno;
538 
539 	scsi_toc_toc *toc = (scsi_toc_toc *)malloc(1024);
540 	if (toc == NULL)
541 		return B_NO_MEMORY;
542 
543 	status_t status = read_table_of_contents(fDevice, toc, 1024);
544 
545 	// there has to be at least one audio track
546 	if (status == B_OK && count_audio_tracks(toc) == 0)
547 		status = B_BAD_TYPE;
548 
549 	if (status < B_OK) {
550 		free(toc);
551 		return status;
552 	}
553 
554 	fDiscID = compute_cddb_disc_id(*toc);
555 
556 	// create the root vnode
557 	fRootNode = _CreateNode(NULL, "", 0, 0, S_IFDIR | 0777);
558 	if (fRootNode == NULL)
559 		status = B_NO_MEMORY;
560 	if (status >= B_OK)
561 		status = publish_vnode(ID(), fRootNode->ID(), fRootNode);
562 	if (status < B_OK) {
563 		free(toc);
564 		return status;
565 	}
566 
567 	cdtext text;
568 	if (read_cdtext(fDevice, text) < B_OK)
569 		dprintf("CDDA: no CD-Text found.\n");
570 
571 	int32 trackCount = toc->last_track + 1 - toc->first_track;
572 	off_t totalFrames = 0;
573 	char title[256];
574 
575 	for (int32 i = 0; i < trackCount; i++) {
576 		scsi_cd_msf& next = toc->tracks[i + 1].start.time;
577 			// the last track is always lead-out
578 		scsi_cd_msf& start = toc->tracks[i].start.time;
579 		int32 track = i + 1;
580 
581 		off_t startFrame = start.minute * kFramesPerMinute
582 			+ start.second * kFramesPerSecond + start.frame;
583 		off_t frames = next.minute * kFramesPerMinute
584 			+ next.second * kFramesPerSecond + next.frame
585 			- startFrame;
586 
587 		totalFrames += frames;
588 
589 		if (is_data_track(toc->tracks[i]))
590 			continue;
591 
592 		if (text.titles[i] != NULL) {
593 			if (text.artists[i] != NULL) {
594 				snprintf(title, sizeof(title), "%02ld. %s - %s.wav", track,
595 					text.artists[i], text.titles[i]);
596 			} else {
597 				snprintf(title, sizeof(title), "%02ld. %s.wav", track,
598 					text.titles[i]);
599 			}
600 		} else
601 			snprintf(title, sizeof(title), "%02ld.wav", track);
602 
603 		// remove '/' and '\n' from title
604 		for (int32 j = 0; title[j]; j++) {
605 			if (title[j] == '/')
606 				title[j] = '-';
607 			else if (title[j] == '\n')
608 				title[j] = ' ';
609 		}
610 
611 		Inode *inode = _CreateNode(fRootNode, title, startFrame, frames,
612 			S_IFREG | 0444);
613 		if (inode == NULL)
614 			continue;
615 
616 		// add attributes
617 
618 		inode->AddAttribute("Audio:Artist", B_STRING_TYPE,
619 			text.artists[i] != NULL ? text.artists[i] : text.artist);
620 		inode->AddAttribute("Audio:Title", B_STRING_TYPE, text.titles[i]);
621 		inode->AddAttribute("Audio:Genre", B_STRING_TYPE, text.genre);
622 		inode->AddAttribute("Audio:Track", track);
623 
624 		snprintf(title, sizeof(title), "%02lu:%02lu",
625 			uint32(inode->FrameCount() / kFramesPerMinute),
626 			uint32((inode->FrameCount() % kFramesPerMinute) / kFramesPerSecond));
627 		inode->AddAttribute("Audio:Length", B_STRING_TYPE, title);
628 		inode->AddAttribute("BEOS:TYPE", B_MIME_STRING_TYPE, "audio/x-wav");
629 	}
630 
631 	_RestoreSharedAttributes();
632 	_RestoreAttributes();
633 
634 	free(toc);
635 
636 	// determine volume title
637 	DetermineName(text, title, sizeof(title));
638 
639 	fName = strdup(title);
640 	if (fName == NULL)
641 		return B_NO_MEMORY;
642 
643 	fNumBlocks = totalFrames;
644 	return B_OK;
645 }
646 
647 
648 Semaphore&
649 Volume::Lock()
650 {
651 	return fLock;
652 }
653 
654 
655 Inode *
656 Volume::_CreateNode(Inode *parent, const char *name, off_t start, off_t frames,
657 	int32 type)
658 {
659 	Inode *inode = new Inode(this, parent, name, start, frames, type);
660 	if (inode == NULL)
661 		return NULL;
662 
663 	if (inode->InitCheck() != B_OK) {
664 		delete inode;
665 		return NULL;
666 	}
667 
668 	if (S_ISREG(type)) {
669 		// we need to order it by track for compatibility with BeOS' cdda
670 		Inode *last = NULL, *current = fFirstEntry;
671 		while (current != NULL) {
672 			last = current;
673 			current = current->Next();
674 		}
675 
676 		if (last)
677 			last->SetNext(inode);
678 		else
679 			fFirstEntry = inode;
680 	}
681 
682 	return inode;
683 }
684 
685 
686 Inode *
687 Volume::Find(ino_t id)
688 {
689 	for (Inode *inode = fFirstEntry; inode != NULL; inode = inode->Next()) {
690 		if (inode->ID() == id)
691 			return inode;
692 	}
693 
694 	return NULL;
695 }
696 
697 
698 Inode *
699 Volume::Find(const char *name)
700 {
701 	if (!strcmp(name, ".")
702 		|| !strcmp(name, ".."))
703 		return fRootNode;
704 
705 	for (Inode *inode = fFirstEntry; inode != NULL; inode = inode->Next()) {
706 		if (!strcmp(inode->Name(), name))
707 			return inode;
708 	}
709 
710 	return NULL;
711 }
712 
713 
714 status_t
715 Volume::SetName(const char *name)
716 {
717 	if (name == NULL || !name[0])
718 		return B_BAD_VALUE;
719 
720 	name = strdup(name);
721 	if (name == NULL)
722 		return B_NO_MEMORY;
723 
724 	free(fName);
725 	fName = (char *)name;
726 	return B_OK;
727 }
728 
729 
730 /*!
731 	Opens the file that contains the volume and inode titles as well as all
732 	of their attributes.
733 	The attributes are stored in files below B_USER_SETTINGS_DIRECTORY/cdda.
734 */
735 int
736 Volume::_OpenAttributes(int mode, enum attr_mode attrMode)
737 {
738 	char* path = (char*)malloc(B_PATH_NAME_LENGTH);
739 	if (path == NULL)
740 		return -1;
741 
742 	bool create = (mode & O_WRONLY) != 0;
743 
744 	if (find_directory(B_USER_SETTINGS_DIRECTORY, -1, create, path,
745 			B_PATH_NAME_LENGTH) != B_OK) {
746 		free(path);
747 		return -1;
748 	}
749 
750 	strlcat(path, "/cdda", B_PATH_NAME_LENGTH);
751 	if (create)
752 		mkdir(path, 0755);
753 
754 	if (attrMode == kDiscIDAttributes) {
755 		char id[64];
756 		snprintf(id, sizeof(id), "/%08lx", fDiscID);
757 		strlcat(path, id, B_PATH_NAME_LENGTH);
758 	} else if (attrMode == kDeviceAttributes) {
759 		uint32 length = strlen(path);
760 		char *device = path + length;
761 		if (ioctl(fDevice, B_GET_PATH_FOR_DEVICE, device,
762 				B_PATH_NAME_LENGTH - length) < B_OK) {
763 			free(path);
764 			return B_ERROR;
765 		}
766 
767 		device++;
768 
769 		// replace slashes in the device path
770 		while (device[0]) {
771 			if (device[0] == '/')
772 				device[0] = '_';
773 
774 			device++;
775 		}
776 	} else
777 		strlcat(path, "/shared", B_PATH_NAME_LENGTH);
778 
779 dprintf("PATH: %s\n", path);
780 	int fd = open(path, mode | (create ? O_CREAT | O_TRUNC : 0), 0644);
781 
782 	free(path);
783 	return fd;
784 }
785 
786 
787 /*!
788 	Reads the attributes, if any, that belong to the CD currently being
789 	mounted.
790 */
791 void
792 Volume::_RestoreAttributes()
793 {
794 	int fd = _OpenAttributes(O_RDONLY);
795 	if (fd < 0)
796 		return;
797 
798 	char line[B_FILE_NAME_LENGTH];
799 	if (!read_line(fd, line, B_FILE_NAME_LENGTH)) {
800 		close(fd);
801 		return;
802 	}
803 
804 dprintf("VOLUME %s\n", line);
805 	SetName(line);
806 
807 	for (Inode *inode = fFirstEntry; inode != NULL; inode = inode->Next()) {
808 		if (!read_line(fd, line, B_FILE_NAME_LENGTH))
809 			break;
810 
811 		inode->SetName(line);
812 dprintf("INODE %s\n", line);
813 	}
814 
815 	if (read_attributes(fd, fRootNode)) {
816 		for (Inode *inode = fFirstEntry; inode != NULL; inode = inode->Next()) {
817 			if (!read_attributes(fd, inode))
818 				break;
819 		}
820 	}
821 
822 	close(fd);
823 }
824 
825 
826 void
827 Volume::_StoreAttributes()
828 {
829 	int fd = _OpenAttributes(O_WRONLY);
830 	if (fd < 0)
831 		return;
832 
833 	write_line(fd, Name());
834 
835 	for (Inode *inode = fFirstEntry; inode != NULL; inode = inode->Next()) {
836 		write_line(fd, inode->Name());
837 	}
838 
839 	write_attributes(fd, fRootNode);
840 
841 	for (Inode *inode = fFirstEntry; inode != NULL; inode = inode->Next()) {
842 		write_attributes(fd, inode);
843 	}
844 
845 	close(fd);
846 }
847 
848 
849 /*!
850 	Restores the attributes, if any, that are shared between CDs; some are
851 	stored per device, others are stored for all CDs no matter which device.
852 */
853 void
854 Volume::_RestoreSharedAttributes()
855 {
856 	// device attributes overwrite shared attributes
857 
858 	int fd = _OpenAttributes(O_RDONLY, kSharedAttributes);
859 	if (fd >= 0) {
860 		read_attributes(fd, fRootNode);
861 		close(fd);
862 	}
863 
864 	fd = _OpenAttributes(O_RDONLY, kDeviceAttributes);
865 	if (fd >= 0) {
866 		read_attributes(fd, fRootNode);
867 		close(fd);
868 	}
869 }
870 
871 
872 void
873 Volume::_StoreSharedAttributes()
874 {
875 	// write shared and device specific settings
876 
877 	int fd = _OpenAttributes(O_WRONLY, kSharedAttributes);
878 	if (fd >= 0) {
879 		write_attributes(fd, fRootNode, kSharedAttributes);
880 		close(fd);
881 	}
882 
883 	fd = _OpenAttributes(O_WRONLY, kDeviceAttributes);
884 	if (fd >= 0) {
885 		write_attributes(fd, fRootNode, kDeviceAttributes);
886 		close(fd);
887 	}
888 }
889 
890 
891 //	#pragma mark - Attribute class
892 
893 
894 Attribute::Attribute(const char *name, type_code type)
895 	:
896 	fName(NULL),
897 	fType(0),
898 	fData(NULL),
899 	fSize(0)
900 {
901 	SetTo(name, type);
902 }
903 
904 
905 Attribute::~Attribute()
906 {
907 	free(fName);
908 	free(fData);
909 }
910 
911 
912 status_t
913 Attribute::SetTo(const char *name, type_code type)
914 {
915 	if (name == NULL || !name[0])
916 		return B_BAD_VALUE;
917 
918 	name = strdup(name);
919 	if (name == NULL)
920 		return B_NO_MEMORY;
921 
922 	free(fName);
923 
924 	fName = (char *)name;
925 	fType = type;
926 	return B_OK;
927 }
928 
929 
930 status_t
931 Attribute::ReadAt(off_t offset, uint8 *buffer, size_t *_length)
932 {
933 	size_t length = *_length;
934 
935 	if (offset < 0)
936 		return B_BAD_VALUE;
937 	if (offset >= fSize) {
938 		*_length = 0;
939 		return B_OK;
940 	}
941 	if (offset + length > fSize)
942 		length = fSize - offset;
943 
944 	if (user_memcpy(buffer, fData + offset, length) < B_OK)
945 		return B_BAD_ADDRESS;
946 
947 	*_length = length;
948 	return B_OK;
949 }
950 
951 
952 /*!
953 	Writes to the attribute and enlarges it as needed.
954 	An attribute has a maximum size of 65536 bytes for now.
955 */
956 status_t
957 Attribute::WriteAt(off_t offset, const uint8 *buffer, size_t *_length)
958 {
959 	size_t length = *_length;
960 
961 	if (offset < 0)
962 		return B_BAD_VALUE;
963 
964 	// we limit the attribute size to something reasonable
965 	off_t end = offset + length;
966 	if (end > kMaxAttributeSize) {
967 		end = kMaxAttributeSize;
968 		length = end - offset;
969 	}
970 	if (offset > end) {
971 		*_length = 0;
972 		return E2BIG;
973 	}
974 
975 	if (end > fSize) {
976 		// make room in the data stream
977 		uint8 *data = (uint8 *)realloc(fData, end);
978 		if (data == NULL)
979 			return B_NO_MEMORY;
980 
981 		if (fSize < offset)
982 			memset(data + fSize, 0, offset - fSize);
983 
984 		fData = data;
985 		fSize = end;
986 	}
987 
988 	if (user_memcpy(fData + offset, buffer, length) < B_OK)
989 		return B_BAD_ADDRESS;
990 
991 	*_length = length;
992 	return B_OK;
993 }
994 
995 
996 //!	Removes all data from the attribute.
997 void
998 Attribute::Truncate()
999 {
1000 	free(fData);
1001 	fData = NULL;
1002 	fSize = 0;
1003 }
1004 
1005 
1006 /*!
1007 	Resizes the data part of an attribute to the requested amount \a size.
1008 	An attribute has a maximum size of 65536 bytes for now.
1009 */
1010 status_t
1011 Attribute::SetSize(off_t size)
1012 {
1013 	if (size > kMaxAttributeSize)
1014 		return E2BIG;
1015 
1016 	uint8 *data = (uint8 *)realloc(fData, size);
1017 	if (data == NULL)
1018 		return B_NO_MEMORY;
1019 
1020 	if (fSize < size)
1021 		memset(data + fSize, 0, size - fSize);
1022 
1023 	fData = data;
1024 	fSize = size;
1025 	return B_OK;
1026 }
1027 
1028 
1029 //	#pragma mark - Inode class
1030 
1031 
1032 Inode::Inode(Volume *volume, Inode *parent, const char *name, off_t start,
1033 		off_t frames, int32 type)
1034 	:
1035 	fNext(NULL)
1036 {
1037 	fName = strdup(name);
1038 	if (fName == NULL)
1039 		return;
1040 
1041 	fID = volume->GetNextNodeID();
1042 	fType = type;
1043 	fStartFrame = start;
1044 	fFrameCount = frames;
1045 
1046 	fUserID = geteuid();
1047 	fGroupID = parent ? parent->GroupID() : getegid();
1048 
1049 	fCreationTime = fModificationTime = time(NULL);
1050 
1051 	if (frames) {
1052 		// initialize WAV header
1053 
1054 		// RIFF header
1055 		fWAVHeader.header.magic = B_HOST_TO_BENDIAN_INT32('RIFF');
1056 		fWAVHeader.header.length = B_HOST_TO_LENDIAN_INT32(Size()
1057 			+ sizeof(wav_header) - sizeof(riff_chunk));
1058 		fWAVHeader.header.id = B_HOST_TO_BENDIAN_INT32('WAVE');
1059 
1060 		// 'fmt ' format chunk
1061 		fWAVHeader.format.fourcc = B_HOST_TO_BENDIAN_INT32('fmt ');
1062 		fWAVHeader.format.length = B_HOST_TO_LENDIAN_INT32(
1063 			sizeof(wav_format_chunk) - sizeof(riff_chunk));
1064 		fWAVHeader.format.format_tag = B_HOST_TO_LENDIAN_INT16(1);
1065 		fWAVHeader.format.channels = B_HOST_TO_LENDIAN_INT16(2);
1066 		fWAVHeader.format.samples_per_second = B_HOST_TO_LENDIAN_INT32(44100);
1067 		fWAVHeader.format.average_bytes_per_second = B_HOST_TO_LENDIAN_INT32(
1068 			44100 * sizeof(uint16) * 2);
1069 		fWAVHeader.format.block_align = B_HOST_TO_LENDIAN_INT16(4);
1070 		fWAVHeader.format.bits_per_sample = B_HOST_TO_LENDIAN_INT16(16);
1071 
1072 		// 'data' chunk
1073 		fWAVHeader.data.fourcc = B_HOST_TO_BENDIAN_INT32('data');
1074 		fWAVHeader.data.length = B_HOST_TO_LENDIAN_INT32(Size());
1075 	}
1076 }
1077 
1078 
1079 Inode::~Inode()
1080 {
1081 	free(const_cast<char *>(fName));
1082 }
1083 
1084 
1085 status_t
1086 Inode::InitCheck()
1087 {
1088 	if (fName == NULL)
1089 		return B_NO_MEMORY;
1090 
1091 	return B_OK;
1092 }
1093 
1094 
1095 status_t
1096 Inode::SetName(const char* name)
1097 {
1098 	if (name == NULL || !name[0]
1099 		|| strchr(name, '/') != NULL
1100 		|| strchr(name, '\n') != NULL)
1101 		return B_BAD_VALUE;
1102 
1103 	name = strdup(name);
1104 	if (name == NULL)
1105 		return B_NO_MEMORY;
1106 
1107 	free(fName);
1108 	fName = (char *)name;
1109 	return B_OK;
1110 }
1111 
1112 
1113 Attribute *
1114 Inode::FindAttribute(const char *name) const
1115 {
1116 	if (name == NULL || !name[0])
1117 		return NULL;
1118 
1119 	AttributeList::ConstIterator iterator = fAttributes.GetIterator();
1120 
1121 	while (iterator.HasNext()) {
1122 		Attribute *attribute = iterator.Next();
1123 		if (!strcmp(attribute->Name(), name))
1124 			return attribute;
1125 	}
1126 
1127 	return NULL;
1128 }
1129 
1130 
1131 status_t
1132 Inode::AddAttribute(Attribute *attribute, bool overwrite)
1133 {
1134 	Attribute *oldAttribute = FindAttribute(attribute->Name());
1135 	if (oldAttribute != NULL) {
1136 		if (!overwrite)
1137 			return B_NAME_IN_USE;
1138 
1139 		fAttributes.Remove(oldAttribute);
1140 		delete oldAttribute;
1141 	}
1142 
1143 	fAttributes.Add(attribute);
1144 	return B_OK;
1145 }
1146 
1147 
1148 status_t
1149 Inode::AddAttribute(const char *name, type_code type,
1150 	bool overwrite, const uint8 *data, size_t length)
1151 {
1152 	Attribute *attribute = new Attribute(name, type);
1153 	status_t status = attribute != NULL ? B_OK : B_NO_MEMORY;
1154 	if (status == B_OK)
1155 		status = attribute->InitCheck();
1156 	if (status == B_OK && data != NULL && length != 0)
1157 		status = attribute->WriteAt(0, data, &length);
1158 	if (status == B_OK)
1159 		status = AddAttribute(attribute, overwrite);
1160 	if (status < B_OK) {
1161 		delete attribute;
1162 		return status;
1163 	}
1164 
1165 	return B_OK;
1166 }
1167 
1168 
1169 status_t
1170 Inode::AddAttribute(const char *name, type_code type, const char *string)
1171 {
1172 	if (string == NULL)
1173 		return NULL;
1174 
1175 	return AddAttribute(name, type, true, (const uint8 *)string,
1176 		strlen(string));
1177 }
1178 
1179 
1180 status_t
1181 Inode::AddAttribute(const char *name, int32 value)
1182 {
1183 	return AddAttribute(name, B_INT32_TYPE, true,
1184 		(const uint8 *)&value, sizeof(int32));
1185 }
1186 
1187 
1188 status_t
1189 Inode::RemoveAttribute(const char *name)
1190 {
1191 	if (name == NULL || !name[0])
1192 		return B_ENTRY_NOT_FOUND;
1193 
1194 	AttributeList::Iterator iterator = fAttributes.GetIterator();
1195 
1196 	while (iterator.HasNext()) {
1197 		Attribute *attribute = iterator.Next();
1198 		if (!strcmp(attribute->Name(), name)) {
1199 			// look for attribute in cookies
1200 			AttrCookieList::Iterator i = fAttrCookies.GetIterator();
1201 			while (i.HasNext()) {
1202 				attr_cookie *cookie = i.Next();
1203 				if (cookie->current == attribute)
1204 					cookie->current = attribute->GetDoublyLinkedListLink()->next;
1205 			}
1206 
1207 			iterator.Remove();
1208 			delete attribute;
1209 			return B_OK;
1210 		}
1211 	}
1212 
1213 	return B_ENTRY_NOT_FOUND;
1214 }
1215 
1216 
1217 void
1218 Inode::AddAttrCookie(attr_cookie *cookie)
1219 {
1220 	fAttrCookies.Add(cookie);
1221 	RewindAttrCookie(cookie);
1222 }
1223 
1224 
1225 void
1226 Inode::RemoveAttrCookie(attr_cookie *cookie)
1227 {
1228 	fAttrCookies.Remove(cookie);
1229 }
1230 
1231 
1232 void
1233 Inode::RewindAttrCookie(attr_cookie *cookie)
1234 {
1235 	cookie->current = fAttributes.First();
1236 }
1237 
1238 
1239 //	#pragma mark - Module API
1240 
1241 
1242 static float
1243 cdda_identify_partition(int fd, partition_data *partition, void **_cookie)
1244 {
1245 	scsi_toc_toc *toc = (scsi_toc_toc *)malloc(1024);
1246 	if (toc == NULL)
1247 		return B_NO_MEMORY;
1248 
1249 	status_t status = read_table_of_contents(fd, toc, 1024);
1250 
1251 	// there has to be at least a single audio track
1252 	if (status == B_OK && count_audio_tracks(toc) == 0)
1253 		status = B_BAD_TYPE;
1254 
1255 	if (status < B_OK) {
1256 		free(toc);
1257 		return status;
1258 	}
1259 
1260 	*_cookie = toc;
1261 	return 0.8f;
1262 }
1263 
1264 
1265 static status_t
1266 cdda_scan_partition(int fd, partition_data *partition, void *_cookie)
1267 {
1268 	scsi_toc_toc *toc = (scsi_toc_toc *)_cookie;
1269 
1270 	partition->status = B_PARTITION_VALID;
1271 	partition->flags |= B_PARTITION_FILE_SYSTEM;
1272 
1273 	// compute length
1274 
1275 	uint32 lastTrack = toc->last_track + 1 - toc->first_track;
1276 	scsi_cd_msf& end = toc->tracks[lastTrack].start.time;
1277 
1278 	partition->content_size = off_t(end.minute * kFramesPerMinute
1279 		+ end.second * kFramesPerSecond + end.frame) * kFrameSize;
1280 	partition->block_size = kFrameSize;
1281 
1282 	// determine volume title
1283 
1284 	cdtext text;
1285 	read_cdtext(fd, text);
1286 
1287 	char name[256];
1288 	Volume::DetermineName(text, name, sizeof(name));
1289 	partition->content_name = strdup(name);
1290 	if (partition->content_name == NULL)
1291 		return B_NO_MEMORY;
1292 
1293 	return B_OK;
1294 }
1295 
1296 
1297 static void
1298 cdda_free_identify_partition_cookie(partition_data *partition, void *_cookie)
1299 {
1300 	free(_cookie);
1301 }
1302 
1303 
1304 static status_t
1305 cdda_mount(dev_t id, const char *device, uint32 flags, const char *args,
1306 	fs_volume *_volume, ino_t *_rootVnodeID)
1307 {
1308 	TRACE(("cdda_mount: entry\n"));
1309 
1310 	Volume *volume = new Volume(id);
1311 	if (volume == NULL)
1312 		return B_NO_MEMORY;
1313 
1314 	status_t status = volume->InitCheck();
1315 	if (status == B_OK)
1316 		status = volume->Mount(device);
1317 
1318 	if (status < B_OK) {
1319 		delete volume;
1320 		return status;
1321 	}
1322 
1323 	*_rootVnodeID = volume->RootNode().ID();
1324 	*_volume = volume;
1325 
1326 	return B_OK;
1327 }
1328 
1329 
1330 static status_t
1331 cdda_unmount(fs_volume _volume)
1332 {
1333 	struct Volume *volume = (struct Volume *)_volume;
1334 
1335 	TRACE(("cdda_unmount: entry fs = %p\n", _volume));
1336 	delete volume;
1337 
1338 	return 0;
1339 }
1340 
1341 
1342 static status_t
1343 cdda_read_fs_stat(fs_volume _volume, struct fs_info *info)
1344 {
1345 	Volume *volume = (Volume *)_volume;
1346 	Locker locker(volume->Lock());
1347 
1348 	// File system flags.
1349 	info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR | B_FS_HAS_MIME
1350 		| B_FS_IS_REMOVABLE;
1351 	info->io_size = 65536;
1352 
1353 	info->block_size = 2048;
1354 	info->total_blocks = volume->NumBlocks();
1355 	info->free_blocks = 0;
1356 
1357 	// Volume name
1358 	strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name));
1359 
1360 	// File system name
1361 	strlcpy(info->fsh_name, "cdda", sizeof(info->fsh_name));
1362 
1363 	return B_OK;
1364 }
1365 
1366 
1367 static status_t
1368 cdda_write_fs_stat(fs_volume _volume, const struct fs_info *info, uint32 mask)
1369 {
1370 	Volume *volume = (Volume *)_volume;
1371 	Locker locker(volume->Lock());
1372 
1373 	status_t status = B_BAD_VALUE;
1374 
1375 	if (mask & FS_WRITE_FSINFO_NAME)
1376 		status = volume->SetName(info->volume_name);
1377 
1378 	return status;
1379 }
1380 
1381 
1382 static status_t
1383 cdda_sync(fs_volume fs)
1384 {
1385 	TRACE(("cdda_sync: entry\n"));
1386 
1387 	return 0;
1388 }
1389 
1390 
1391 static status_t
1392 cdda_lookup(fs_volume _volume, fs_vnode _dir, const char *name, ino_t *_id, int *_type)
1393 {
1394 	Volume *volume = (Volume *)_volume;
1395 	status_t status;
1396 
1397 	TRACE(("cdda_lookup: entry dir %p, name '%s'\n", _dir, name));
1398 
1399 	Inode *directory = (Inode *)_dir;
1400 	if (!S_ISDIR(directory->Type()))
1401 		return B_NOT_A_DIRECTORY;
1402 
1403 	Locker _(volume->Lock());
1404 
1405 	Inode *inode = volume->Find(name);
1406 	if (inode == NULL)
1407 		return B_ENTRY_NOT_FOUND;
1408 
1409 	Inode *dummy;
1410 	status = get_vnode(volume->ID(), inode->ID(), (fs_vnode *)&dummy);
1411 	if (status < B_OK)
1412 		return status;
1413 
1414 	*_id = inode->ID();
1415 	*_type = inode->Type();
1416 	return B_OK;
1417 }
1418 
1419 
1420 static status_t
1421 cdda_get_vnode_name(fs_volume _volume, fs_vnode _node, char *buffer, size_t bufferSize)
1422 {
1423 	Volume *volume = (Volume *)_volume;
1424 	Inode *inode = (Inode *)_node;
1425 
1426 	TRACE(("cdda_get_vnode_name(): inode = %p\n", inode));
1427 
1428 	Locker _(volume->Lock());
1429 	strlcpy(buffer, inode->Name(), bufferSize);
1430 	return B_OK;
1431 }
1432 
1433 
1434 static status_t
1435 cdda_get_vnode(fs_volume _volume, ino_t id, fs_vnode *_inode, bool reenter)
1436 {
1437 	Volume *volume = (Volume *)_volume;
1438 	Inode *inode;
1439 
1440 	TRACE(("cdda_getvnode: asking for vnode 0x%Lx, r %d\n", id, reenter));
1441 
1442 	inode = volume->Find(id);
1443 	if (inode == NULL)
1444 		return B_ENTRY_NOT_FOUND;
1445 
1446 	*_inode = inode;
1447 	return B_OK;
1448 }
1449 
1450 
1451 static status_t
1452 cdda_put_vnode(fs_volume _volume, fs_vnode _node, bool reenter)
1453 {
1454 	return B_OK;
1455 }
1456 
1457 
1458 static status_t
1459 cdda_open(fs_volume _volume, fs_vnode _node, int openMode, fs_cookie *_cookie)
1460 {
1461 	TRACE(("cdda_open(): node = %p, openMode = %d\n", _node, openMode));
1462 
1463 	file_cookie *cookie = (file_cookie *)malloc(sizeof(file_cookie));
1464 	if (cookie == NULL)
1465 		return B_NO_MEMORY;
1466 
1467 	TRACE(("  open cookie = %p\n", cookie));
1468 	cookie->open_mode = openMode;
1469 	cookie->buffer = NULL;
1470 
1471 	*_cookie = (void *)cookie;
1472 
1473 	return B_OK;
1474 }
1475 
1476 
1477 static status_t
1478 cdda_close(fs_volume _volume, fs_vnode _node, fs_cookie _cookie)
1479 {
1480 	return B_OK;
1481 }
1482 
1483 
1484 static status_t
1485 cdda_free_cookie(fs_volume _volume, fs_vnode _node, fs_cookie _cookie)
1486 {
1487 	file_cookie *cookie = (file_cookie *)_cookie;
1488 
1489 	TRACE(("cdda_freecookie: entry vnode %p, cookie %p\n", _node, _cookie));
1490 
1491 	free(cookie);
1492 	return B_OK;
1493 }
1494 
1495 
1496 static status_t
1497 cdda_fsync(fs_volume _volume, fs_vnode _v)
1498 {
1499 	return B_OK;
1500 }
1501 
1502 
1503 static status_t
1504 cdda_read(fs_volume _volume, fs_vnode _node, fs_cookie _cookie, off_t offset,
1505 	void *buffer, size_t *_length)
1506 {
1507 	file_cookie *cookie = (file_cookie *)_cookie;
1508 	Volume *volume = (Volume *)_volume;
1509 	Inode *inode = (Inode *)_node;
1510 
1511 	TRACE(("cdda_read(vnode = %p, offset %Ld, length = %lu, mode = %d)\n",
1512 		_node, offset, *_length, cookie->open_mode));
1513 
1514 	if (S_ISDIR(inode->Type()))
1515 		return B_IS_A_DIRECTORY;
1516 	if (offset < 0)
1517 		return B_BAD_VALUE;
1518 
1519 	off_t maxSize = inode->Size() + sizeof(wav_header);
1520 	if (offset >= maxSize) {
1521 		*_length = 0;
1522 		return B_OK;
1523 	}
1524 
1525 	if (cookie->buffer == NULL) {
1526 		// TODO: move that to open() to make sure reading can't fail for this reason?
1527 		cookie->buffer = malloc(volume->BufferSize());
1528 		if (cookie->buffer == NULL)
1529 			return B_NO_MEMORY;
1530 
1531 		cookie->buffer_offset = -1;
1532 	}
1533 
1534 	size_t length = *_length;
1535 	if (offset + length > maxSize)
1536 		length = maxSize - offset;
1537 
1538 	status_t status = B_OK;
1539 
1540 	if (offset < sizeof(wav_header)) {
1541 		// read fake WAV header
1542 		size_t size = sizeof(wav_header) - offset;
1543 		size = min_c(size, length);
1544 
1545 		if (user_memcpy(buffer, (uint8 *)inode->WAVHeader() + offset, size) < B_OK)
1546 			return B_BAD_ADDRESS;
1547 
1548 		buffer = (void *)((uint8 *)buffer + size);
1549 		length -= size;
1550 		offset = 0;
1551 	} else
1552 		offset -= sizeof(wav_header);
1553 
1554 	if (length > 0) {
1555 		// read actual CD data
1556 		offset += inode->StartFrame() * kFrameSize;
1557 
1558 		status = read_cdda_data(volume->Device(), offset, buffer, length,
1559 			cookie->buffer_offset, cookie->buffer, volume->BufferSize());
1560 	}
1561 	if (status == B_OK)
1562 		*_length = length;
1563 
1564 	return status;
1565 }
1566 
1567 
1568 static bool
1569 cdda_can_page(fs_volume _volume, fs_vnode _v, fs_cookie cookie)
1570 {
1571 	return false;
1572 }
1573 
1574 
1575 static status_t
1576 cdda_read_pages(fs_volume _volume, fs_vnode _v, fs_cookie cookie, off_t pos,
1577 	const iovec *vecs, size_t count, size_t *_numBytes, bool mayBlock,
1578 	bool reenter)
1579 {
1580 	return B_NOT_ALLOWED;
1581 }
1582 
1583 
1584 static status_t
1585 cdda_write_pages(fs_volume _volume, fs_vnode _v, fs_cookie cookie, off_t pos,
1586 	const iovec *vecs, size_t count, size_t *_numBytes, bool mayBlock,
1587 	bool reenter)
1588 {
1589 	return B_NOT_ALLOWED;
1590 }
1591 
1592 
1593 static status_t
1594 cdda_read_stat(fs_volume _volume, fs_vnode _node, struct stat *stat)
1595 {
1596 	Volume *volume = (Volume *)_volume;
1597 	Inode *inode = (Inode *)_node;
1598 
1599 	TRACE(("cdda_read_stat: vnode %p (0x%Lx), stat %p\n", inode, inode->ID(), stat));
1600 
1601 	fill_stat_buffer(volume, inode, NULL, *stat);
1602 
1603 	return B_OK;
1604 }
1605 
1606 
1607 status_t
1608 cdda_rename(fs_volume _volume, void *_oldDir, const char *oldName, void *_newDir,
1609 	const char *newName)
1610 {
1611 	if (_volume == NULL || _oldDir == NULL || _newDir == NULL
1612 		|| oldName == NULL || *oldName == '\0'
1613 		|| newName == NULL || *newName == '\0'
1614 		|| !strcmp(oldName, ".") || !strcmp(oldName, "..")
1615 		|| !strcmp(newName, ".") || !strcmp(newName, "..")
1616 		|| strchr(newName, '/') != NULL)
1617 		return B_BAD_VALUE;
1618 
1619 	// we only have a single directory which simplifies things a bit :-)
1620 
1621 	Volume *volume = (Volume *)_volume;
1622 	Locker _(volume->Lock());
1623 
1624 	Inode *inode = volume->Find(oldName);
1625 	if (inode == NULL)
1626 		return B_ENTRY_NOT_FOUND;
1627 
1628 	if (volume->Find(newName) != NULL)
1629 		return B_NAME_IN_USE;
1630 
1631 	return inode->SetName(newName);
1632 }
1633 
1634 
1635 //	#pragma mark - directory functions
1636 
1637 
1638 static status_t
1639 cdda_open_dir(fs_volume _volume, fs_vnode _node, fs_cookie *_cookie)
1640 {
1641 	Volume *volume = (Volume *)_volume;
1642 
1643 	TRACE(("cdda_open_dir(): vnode = %p\n", _node));
1644 
1645 	Inode *inode = (Inode *)_node;
1646 	if (!S_ISDIR(inode->Type()))
1647 		return B_BAD_VALUE;
1648 
1649 	if (inode != &volume->RootNode())
1650 		panic("pipefs: found directory that's not the root!");
1651 
1652 	dir_cookie *cookie = (dir_cookie *)malloc(sizeof(dir_cookie));
1653 	if (cookie == NULL)
1654 		return B_NO_MEMORY;
1655 
1656 	cookie->current = volume->FirstEntry();
1657 	cookie->state = ITERATION_STATE_BEGIN;
1658 
1659 	*_cookie = (void *)cookie;
1660 	return B_OK;
1661 }
1662 
1663 
1664 static status_t
1665 cdda_read_dir(fs_volume _volume, fs_vnode _node, fs_cookie _cookie,
1666 	struct dirent *dirent, size_t bufferSize, uint32 *_num)
1667 {
1668 	Volume *volume = (Volume *)_volume;
1669 	Inode *inode = (Inode *)_node;
1670 	status_t status = 0;
1671 
1672 	TRACE(("cdda_read_dir: vnode %p, cookie %p, buffer = %p, bufferSize = %ld, num = %p\n", _node, _cookie, dirent, bufferSize,_num));
1673 
1674 	if (_node != &volume->RootNode())
1675 		return B_BAD_VALUE;
1676 
1677 	Locker _(volume->Lock());
1678 
1679 	dir_cookie *cookie = (dir_cookie *)_cookie;
1680 	Inode *childNode = NULL;
1681 	const char *name = NULL;
1682 	Inode *nextChildNode = NULL;
1683 	int nextState = cookie->state;
1684 
1685 	switch (cookie->state) {
1686 		case ITERATION_STATE_DOT:
1687 			childNode = inode;
1688 			name = ".";
1689 			nextChildNode = volume->FirstEntry();
1690 			nextState = cookie->state + 1;
1691 			break;
1692 		case ITERATION_STATE_DOT_DOT:
1693 			childNode = inode; // parent of the root node is the root node
1694 			name = "..";
1695 			nextChildNode = volume->FirstEntry();
1696 			nextState = cookie->state + 1;
1697 			break;
1698 		default:
1699 			childNode = cookie->current;
1700 			if (childNode) {
1701 				name = childNode->Name();
1702 				nextChildNode = childNode->Next();
1703 			}
1704 			break;
1705 	}
1706 
1707 	if (!childNode) {
1708 		// we're at the end of the directory
1709 		*_num = 0;
1710 		return B_OK;
1711 	}
1712 
1713 	dirent->d_dev = volume->ID();
1714 	dirent->d_ino = inode->ID();
1715 	dirent->d_reclen = strlen(name) + sizeof(struct dirent);
1716 
1717 	if (dirent->d_reclen > bufferSize)
1718 		return ENOBUFS;
1719 
1720 	status = user_strlcpy(dirent->d_name, name, bufferSize);
1721 	if (status < B_OK)
1722 		return status;
1723 
1724 	cookie->current = nextChildNode;
1725 	cookie->state = nextState;
1726 	return B_OK;
1727 }
1728 
1729 
1730 static status_t
1731 cdda_rewind_dir(fs_volume _volume, fs_vnode _vnode, fs_cookie _cookie)
1732 {
1733 	Volume *volume = (Volume *)_volume;
1734 
1735 	dir_cookie *cookie = (dir_cookie *)_cookie;
1736 	cookie->current = volume->FirstEntry();
1737 	cookie->state = ITERATION_STATE_BEGIN;
1738 
1739 	return B_OK;
1740 }
1741 
1742 
1743 static status_t
1744 cdda_close_dir(fs_volume _volume, fs_vnode _node, fs_cookie _cookie)
1745 {
1746 	TRACE(("cdda_close: entry vnode %p, cookie %p\n", _node, _cookie));
1747 
1748 	return 0;
1749 }
1750 
1751 
1752 static status_t
1753 cdda_free_dir_cookie(fs_volume _volume, fs_vnode _vnode, fs_cookie _cookie)
1754 {
1755 	dir_cookie *cookie = (dir_cookie *)_cookie;
1756 
1757 	TRACE(("cdda_freecookie: entry vnode %p, cookie %p\n", _vnode, cookie));
1758 
1759 	free(cookie);
1760 	return 0;
1761 }
1762 
1763 
1764 //	#pragma mark - attribute functions
1765 
1766 
1767 static status_t
1768 cdda_open_attr_dir(fs_volume _volume, fs_vnode _vnode, fs_cookie *_cookie)
1769 {
1770 	Volume *volume = (Volume *)_volume;
1771 	Inode *inode = (Inode *)_vnode;
1772 
1773 	attr_cookie *cookie = new attr_cookie;
1774 	if (cookie == NULL)
1775 		return B_NO_MEMORY;
1776 
1777 	Locker _(volume->Lock());
1778 
1779 	inode->AddAttrCookie(cookie);
1780 	*_cookie = cookie;
1781 	return B_OK;
1782 }
1783 
1784 
1785 static status_t
1786 cdda_close_attr_dir(fs_volume _volume, fs_vnode _vnode, fs_cookie _cookie)
1787 {
1788 	return B_OK;
1789 }
1790 
1791 
1792 static status_t
1793 cdda_free_attr_dir_cookie(fs_volume _volume, fs_vnode _vnode, fs_cookie _cookie)
1794 {
1795 	Volume *volume = (Volume *)_volume;
1796 	Inode *inode = (Inode *)_vnode;
1797 	attr_cookie *cookie = (attr_cookie *)_cookie;
1798 
1799 	Locker _(volume->Lock());
1800 
1801 	inode->RemoveAttrCookie(cookie);
1802 	delete cookie;
1803 	return B_OK;
1804 }
1805 
1806 
1807 static status_t
1808 cdda_rewind_attr_dir(fs_volume _volume, fs_vnode _vnode, fs_cookie _cookie)
1809 {
1810 	Volume *volume = (Volume *)_volume;
1811 	Inode *inode = (Inode *)_vnode;
1812 	attr_cookie *cookie = (attr_cookie *)_cookie;
1813 
1814 	Locker _(volume->Lock());
1815 
1816 	inode->RewindAttrCookie(cookie);
1817 	return B_OK;
1818 }
1819 
1820 
1821 static status_t
1822 cdda_read_attr_dir(fs_volume _volume, fs_vnode _vnode, fs_cookie _cookie,
1823 	struct dirent *dirent, size_t bufferSize, uint32 *_num)
1824 {
1825 	Volume *volume = (Volume *)_volume;
1826 	Inode *inode = (Inode *)_vnode;
1827 	attr_cookie *cookie = (attr_cookie *)_cookie;
1828 
1829 	Locker _(volume->Lock());
1830 	Attribute *attribute = cookie->current;
1831 
1832 	if (attribute == NULL) {
1833 		*_num = 0;
1834 		return B_OK;
1835 	}
1836 
1837 	size_t length = strlcpy(dirent->d_name, attribute->Name(), bufferSize);
1838 	dirent->d_dev = volume->ID();
1839 	dirent->d_ino = inode->ID();
1840 	dirent->d_reclen = sizeof(struct dirent) + length;
1841 
1842 	cookie->current = attribute->GetDoublyLinkedListLink()->next;
1843 	*_num = 1;
1844 	return B_OK;
1845 }
1846 
1847 
1848 static status_t
1849 cdda_create_attr(fs_volume _volume, fs_vnode _node, const char *name,
1850 	uint32 type, int openMode, fs_cookie *_cookie)
1851 {
1852 	Volume *volume = (Volume *)_volume;
1853 	Inode *inode = (Inode *)_node;
1854 
1855 	Locker _(volume->Lock());
1856 
1857 	Attribute *attribute = inode->FindAttribute(name);
1858 	if (attribute == NULL) {
1859 		status_t status = inode->AddAttribute(name, type);
1860 		if (status < B_OK)
1861 			return status;
1862 	} else if ((openMode & O_EXCL) == 0) {
1863 		attribute->SetType(type);
1864 		if ((openMode & O_TRUNC) != 0)
1865 			attribute->Truncate();
1866 	} else
1867 		return B_FILE_EXISTS;
1868 
1869 	*_cookie = strdup(name);
1870 	if (*_cookie == NULL)
1871 		return B_NO_MEMORY;
1872 
1873 	return B_OK;
1874 }
1875 
1876 
1877 static status_t
1878 cdda_open_attr(fs_volume _volume, fs_vnode _node, const char *name,
1879 	int openMode, fs_cookie *_cookie)
1880 {
1881 	Volume *volume = (Volume *)_volume;
1882 	Inode *inode = (Inode *)_node;
1883 
1884 	Locker _(volume->Lock());
1885 
1886 	Attribute *attribute = inode->FindAttribute(name);
1887 	if (attribute == NULL)
1888 		return B_ENTRY_NOT_FOUND;
1889 
1890 	*_cookie = strdup(name);
1891 	if (*_cookie == NULL)
1892 		return B_NO_MEMORY;
1893 
1894 	return B_OK;
1895 }
1896 
1897 
1898 static status_t
1899 cdda_close_attr(fs_volume _fs, fs_vnode _file, fs_cookie cookie)
1900 {
1901 	return B_OK;
1902 }
1903 
1904 
1905 static status_t
1906 cdda_free_attr_cookie(fs_volume _fs, fs_vnode _file, fs_cookie cookie)
1907 {
1908 	free(cookie);
1909 	return B_OK;
1910 }
1911 
1912 
1913 static status_t
1914 cdda_read_attr(fs_volume _volume, fs_vnode _node, fs_cookie _cookie,
1915 	off_t offset, void *buffer, size_t *_length)
1916 {
1917 	Volume *volume = (Volume *)_volume;
1918 	Inode *inode = (Inode *)_node;
1919 
1920 	Locker _(volume->Lock());
1921 
1922 	Attribute *attribute = inode->FindAttribute((const char *)_cookie);
1923 	if (attribute == NULL)
1924 		return B_ENTRY_NOT_FOUND;
1925 
1926 	return attribute->ReadAt(offset, (uint8 *)buffer, _length);
1927 }
1928 
1929 
1930 static status_t
1931 cdda_write_attr(fs_volume _volume, fs_vnode _file, fs_cookie _cookie,
1932 	off_t offset, const void *buffer, size_t *_length)
1933 {
1934 	Volume *volume = (Volume *)_volume;
1935 	Inode *inode = (Inode *)_file;
1936 
1937 	Locker _(volume->Lock());
1938 
1939 	Attribute *attribute = inode->FindAttribute((const char *)_cookie);
1940 	if (attribute == NULL)
1941 		return B_ENTRY_NOT_FOUND;
1942 
1943 	return attribute->WriteAt(offset, (uint8 *)buffer, _length);
1944 }
1945 
1946 
1947 static status_t
1948 cdda_read_attr_stat(fs_volume _volume, fs_vnode _file, fs_cookie _cookie,
1949 	struct stat *stat)
1950 {
1951 	Volume *volume = (Volume *)_volume;
1952 	Inode *inode = (Inode *)_file;
1953 
1954 	Locker _(volume->Lock());
1955 
1956 	Attribute *attribute = inode->FindAttribute((const char *)_cookie);
1957 	if (attribute == NULL)
1958 		return B_ENTRY_NOT_FOUND;
1959 
1960 	fill_stat_buffer(volume, inode, attribute, *stat);
1961 	return B_OK;
1962 }
1963 
1964 
1965 static status_t
1966 cdda_write_attr_stat(fs_volume _volume, fs_vnode file, fs_cookie cookie,
1967 	const struct stat *stat, int statMask)
1968 {
1969 	return EOPNOTSUPP;
1970 }
1971 
1972 
1973 static status_t
1974 cdda_remove_attr(fs_volume _volume, fs_vnode _node, const char *name)
1975 {
1976 	if (name == NULL)
1977 		return B_BAD_VALUE;
1978 
1979 	Volume *volume = (Volume *)_volume;
1980 	Inode *inode = (Inode *)_node;
1981 
1982 	Locker _(volume->Lock());
1983 
1984 	return inode->RemoveAttribute(name);
1985 }
1986 
1987 
1988 static status_t
1989 cdda_std_ops(int32 op, ...)
1990 {
1991 	switch (op) {
1992 		case B_MODULE_INIT:
1993 			return B_OK;
1994 
1995 		case B_MODULE_UNINIT:
1996 			return B_OK;
1997 
1998 		default:
1999 			return B_ERROR;
2000 	}
2001 }
2002 
2003 
2004 static file_system_module_info sCDDAFileSystem = {
2005 	{
2006 		"file_systems/cdda" B_CURRENT_FS_API_VERSION,
2007 		0,
2008 		cdda_std_ops,
2009 	},
2010 
2011 	"CDDA File System",
2012 	0,	// DDM flags
2013 
2014 	cdda_identify_partition,
2015 	cdda_scan_partition,
2016 	cdda_free_identify_partition_cookie,
2017 	NULL,	// free_partition_content_cookie()
2018 
2019 	cdda_mount,
2020 	cdda_unmount,
2021 	cdda_read_fs_stat,
2022 	cdda_write_fs_stat,
2023 	cdda_sync,
2024 
2025 	cdda_lookup,
2026 	cdda_get_vnode_name,
2027 
2028 	cdda_get_vnode,
2029 	cdda_put_vnode,
2030 	NULL,	// fs_remove_vnode()
2031 
2032 	cdda_can_page,
2033 	cdda_read_pages,
2034 	cdda_write_pages,
2035 
2036 	NULL,	// get_file_map()
2037 
2038 	// common
2039 	NULL,	// fs_ioctl()
2040 	NULL,	// fs_set_flags()
2041 	NULL,	// fs_select()
2042 	NULL,	// fs_deselect()
2043 	cdda_fsync,
2044 
2045 	NULL,	// fs_read_link()
2046 	NULL,	// fs_symlink()
2047 	NULL,	// fs_link()
2048 	NULL,	// fs_unlink()
2049 	cdda_rename,
2050 
2051 	NULL,	// fs_access()
2052 	cdda_read_stat,
2053 	NULL,	// fs_write_stat()
2054 
2055 	// file
2056 	NULL,	// fs_create()
2057 	cdda_open,
2058 	cdda_close,
2059 	cdda_free_cookie,
2060 	cdda_read,
2061 	NULL,	// fs_write()
2062 
2063 	// directory
2064 	NULL,	// fs_create_dir()
2065 	NULL,	// fs_remove_dir()
2066 	cdda_open_dir,
2067 	cdda_close_dir,
2068 	cdda_free_dir_cookie,
2069 	cdda_read_dir,
2070 	cdda_rewind_dir,
2071 
2072 	// attribute directory operations
2073 	cdda_open_attr_dir,
2074 	cdda_close_attr_dir,
2075 	cdda_free_attr_dir_cookie,
2076 	cdda_read_attr_dir,
2077 	cdda_rewind_attr_dir,
2078 
2079 	// attribute operations
2080 	cdda_create_attr,
2081 	cdda_open_attr,
2082 	cdda_close_attr,
2083 	cdda_free_attr_cookie,
2084 	cdda_read_attr,
2085 	cdda_write_attr,
2086 
2087 	cdda_read_attr_stat,
2088 	cdda_write_attr_stat,
2089 	NULL,	// fs_rename_attr()
2090 	cdda_remove_attr,
2091 
2092 	// the other operations are not yet supported (indices, queries)
2093 	NULL,
2094 };
2095 
2096 module_info *modules[] = {
2097 	(module_info *)&sCDDAFileSystem,
2098 	NULL,
2099 };
2100