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