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