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