xref: /haiku/src/add-ons/kernel/file_systems/cdda/kernel_interface.cpp (revision 16c83730262f1e4f0fc69d80744bb36dcfbbe3af)
1 /*
2  * Copyright 2007-2011, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "cdda.h"
8 #include "cddb.h"
9 #include "Lock.h"
10 
11 #include <FindDirectory.h>
12 #include <fs_info.h>
13 #include <fs_interface.h>
14 #include <KernelExport.h>
15 #include <Mime.h>
16 #include <NodeMonitor.h>
17 #include <TypeConstants.h>
18 
19 #include <util/kernel_cpp.h>
20 #include <util/DoublyLinkedList.h>
21 
22 #include <dirent.h>
23 #include <errno.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/stat.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 Attribute(name, type);
436 		if (attribute->IsProtectedNamespace()) {
437 			// Attributes in the protected namespace are handled internally
438 			// so we do not load them even if they are present in the
439 			// attributes file.
440 			delete attribute;
441 			continue;
442 		}
443 		if (attribute->SetSize(size) != B_OK
444 			|| inode->AddAttribute(attribute, true) != B_OK) {
445 			delete attribute;
446 		} else
447 			read(fd, attribute->Data(), size);
448 	}
449 
450 	return true;
451 }
452 
453 
454 static int
455 open_attributes(uint32 cddbID, int deviceFD, int mode,
456 	enum attr_mode attrMode)
457 {
458 	char* path = (char*)malloc(B_PATH_NAME_LENGTH);
459 	if (path == NULL)
460 		return -1;
461 
462 	bool create = (mode & O_WRONLY) != 0;
463 
464 	if (find_directory(B_USER_SETTINGS_DIRECTORY, -1, create, path,
465 			B_PATH_NAME_LENGTH) != B_OK) {
466 		free(path);
467 		return -1;
468 	}
469 
470 	strlcat(path, "/cdda", B_PATH_NAME_LENGTH);
471 	if (create)
472 		mkdir(path, 0755);
473 
474 	if (attrMode == kDiscIDAttributes) {
475 		char id[64];
476 		snprintf(id, sizeof(id), "/%08" B_PRIx32, cddbID);
477 		strlcat(path, id, B_PATH_NAME_LENGTH);
478 	} else if (attrMode == kDeviceAttributes) {
479 		uint32 length = strlen(path);
480 		char* deviceName = path + length;
481 		if (ioctl(deviceFD, B_GET_PATH_FOR_DEVICE, deviceName,
482 				B_PATH_NAME_LENGTH - length) < B_OK) {
483 			free(path);
484 			return B_ERROR;
485 		}
486 
487 		deviceName++;
488 
489 		// replace slashes in the device path
490 		while (deviceName[0]) {
491 			if (deviceName[0] == '/')
492 				deviceName[0] = '_';
493 
494 			deviceName++;
495 		}
496 	} else
497 		strlcat(path, "/shared", B_PATH_NAME_LENGTH);
498 
499 	int fd = open(path, mode | (create ? O_CREAT | O_TRUNC : 0), 0644);
500 
501 	free(path);
502 	return fd;
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 	fFirstEntry(NULL)
574 {
575 }
576 
577 
578 Volume::~Volume()
579 {
580 	if (fRootNode) {
581 		_StoreAttributes();
582 		_StoreSharedAttributes();
583 	}
584 
585 	if (fDevice >= 0)
586 		close(fDevice);
587 
588 	// put_vnode on the root to release the ref to it
589 	if (fRootNode)
590 		put_vnode(FSVolume(), fRootNode->ID());
591 
592 	delete fRootNode;
593 
594 	Inode* inode;
595 	Inode* next;
596 
597 	for (inode = fFirstEntry; inode != NULL; inode = next) {
598 		next = inode->Next();
599 		delete inode;
600 	}
601 
602 	free(fName);
603 }
604 
605 
606 status_t
607 Volume::InitCheck()
608 {
609 	if (fLock.InitCheck() < B_OK)
610 		return B_ERROR;
611 
612 	return B_OK;
613 }
614 
615 
616 status_t
617 Volume::Mount(const char* device)
618 {
619 	fDevice = open(device, O_RDONLY);
620 	if (fDevice < 0)
621 		return errno;
622 
623 	scsi_toc_toc* toc = (scsi_toc_toc*)malloc(1024);
624 	if (toc == NULL)
625 		return B_NO_MEMORY;
626 
627 	status_t status = read_table_of_contents(fDevice, toc, 1024);
628 	// there has to be at least one audio track
629 	if (status == B_OK && count_audio_tracks(toc) == 0)
630 		status = B_BAD_TYPE;
631 
632 	if (status != B_OK) {
633 		free(toc);
634 		return status;
635 	}
636 
637 	fDiscID = compute_cddb_disc_id(*toc);
638 
639 	// create the root vnode
640 	fRootNode = _CreateNode(NULL, "", 0, 0, S_IFDIR | 0777);
641 	if (fRootNode == NULL)
642 		status = B_NO_MEMORY;
643 	if (status == B_OK) {
644 		status = publish_vnode(FSVolume(), fRootNode->ID(), fRootNode,
645 			&gCDDAVnodeOps, fRootNode->Type(), 0);
646 	}
647 	if (status != B_OK) {
648 		free(toc);
649 		return status;
650 	}
651 
652 	bool doLookup = true;
653 	cdtext text;
654 	int fd = _OpenAttributes(O_RDONLY);
655 	if (fd < 0) {
656 		// We do not seem to have an attribute file so this is probably the
657 		// first time this CD is inserted. In this case, try to read CD-Text
658 		// data.
659 		if (read_cdtext(fDevice, text) == B_OK)
660 			doLookup = false;
661 		else
662 			TRACE(("CDDA: no CD-Text found.\n"));
663 	} else {
664 		doLookup = false;
665 	}
666 
667 	int32 trackCount = toc->last_track + 1 - toc->first_track;
668 	off_t totalFrames = 0;
669 	char title[256];
670 
671 	for (int32 i = 0; i < trackCount; i++) {
672 		scsi_cd_msf& next = toc->tracks[i + 1].start.time;
673 			// the last track is always lead-out
674 		scsi_cd_msf& start = toc->tracks[i].start.time;
675 		int32 track = i + 1;
676 
677 		uint64 startFrame = start.minute * kFramesPerMinute
678 			+ start.second * kFramesPerSecond + start.frame;
679 		uint64 frames = next.minute * kFramesPerMinute
680 			+ next.second * kFramesPerSecond + next.frame
681 			- startFrame;
682 
683 		// Adjust length of the last audio track according to the Blue Book
684 		// specification in case of an Enhanced CD
685 		if (i + 1 < trackCount && is_data_track(toc->tracks[i + 1])
686 			&& !is_data_track(toc->tracks[i]))
687 			frames -= kDataTrackLeadGap;
688 
689 		totalFrames += frames;
690 
691 		if (is_data_track(toc->tracks[i]))
692 			continue;
693 
694 		if (text.titles[i] != NULL) {
695 			if (text.artists[i] != NULL) {
696 				snprintf(title, sizeof(title), "%02" B_PRId32 ". %s - %s.wav",
697 					track, text.artists[i], text.titles[i]);
698 			} else {
699 				snprintf(title, sizeof(title), "%02" B_PRId32 ". %s.wav",
700 					track, text.titles[i]);
701 			}
702 		} else
703 			snprintf(title, sizeof(title), "Track %02" B_PRId32 ".wav", track);
704 
705 		// remove '/' and '\n' from title
706 		for (int32 j = 0; title[j]; j++) {
707 			if (title[j] == '/')
708 				title[j] = '-';
709 			else if (title[j] == '\n')
710 				title[j] = ' ';
711 		}
712 
713 		Inode* inode = _CreateNode(fRootNode, title, startFrame, frames,
714 			S_IFREG | 0444);
715 		if (inode == NULL)
716 			continue;
717 
718 		// add attributes
719 
720 		inode->AddAttribute("Audio:Artist", B_STRING_TYPE,
721 			text.artists[i] != NULL ? text.artists[i] : text.artist);
722 		inode->AddAttribute("Audio:Album", B_STRING_TYPE, text.album);
723 		inode->AddAttribute("Audio:Title", B_STRING_TYPE, text.titles[i]);
724 		inode->AddAttribute("Audio:Genre", B_STRING_TYPE, text.genre);
725 		inode->AddAttribute("Audio:Track", B_INT32_TYPE, (uint32)track);
726 		inode->AddAttribute("Audio:Bitrate", B_STRING_TYPE, "1411 kbps");
727 		inode->AddAttribute("Media:Length", B_INT64_TYPE,
728 			inode->FrameCount() * 1000000L / kFramesPerSecond);
729 		inode->AddAttribute("BEOS:TYPE", B_MIME_STRING_TYPE, "audio/x-wav");
730 	}
731 
732 	// Add CD:cddbid attribute.
733 	fRootNode->AddAttribute(kCddbIdAttribute, B_UINT32_TYPE, fDiscID);
734 
735 	// Add CD:do_lookup attribute.
736 	fRootNode->AddAttribute(kDoLookupAttribute, B_BOOL_TYPE, true,
737 		(const uint8*)&doLookup, sizeof(bool));
738 
739 	// Add CD:toc attribute.
740 	fRootNode->AddAttribute(kTocAttribute, B_RAW_TYPE, true,
741 		(const uint8*)toc, B_BENDIAN_TO_HOST_INT16(toc->data_length) + 2);
742 
743 	_RestoreSharedAttributes();
744 	if (fd >= 0)
745 		_RestoreAttributes(fd);
746 
747 	free(toc);
748 
749 	// determine volume title
750 	DetermineName(fDiscID, fDevice, title, sizeof(title));
751 
752 	fName = strdup(title);
753 	if (fName == NULL)
754 		return B_NO_MEMORY;
755 
756 	fNumBlocks = totalFrames;
757 	return B_OK;
758 }
759 
760 
761 status_t
762 Volume::SetName(const char* name)
763 {
764 	if (name == NULL || !name[0])
765 		return B_BAD_VALUE;
766 
767 	name = strdup(name);
768 	if (name == NULL)
769 		return B_NO_MEMORY;
770 
771 	free(fName);
772 	fName = (char*)name;
773 	return B_OK;
774 }
775 
776 
777 Semaphore&
778 Volume::Lock()
779 {
780 	return fLock;
781 }
782 
783 
784 Inode*
785 Volume::Find(ino_t id)
786 {
787 	for (Inode* inode = fFirstEntry; inode != NULL; inode = inode->Next()) {
788 		if (inode->ID() == id)
789 			return inode;
790 	}
791 
792 	return NULL;
793 }
794 
795 
796 Inode*
797 Volume::Find(const char* name)
798 {
799 	if (!strcmp(name, ".")
800 		|| !strcmp(name, ".."))
801 		return fRootNode;
802 
803 	for (Inode* inode = fFirstEntry; inode != NULL; inode = inode->Next()) {
804 		if (!strcmp(inode->Name(), name))
805 			return inode;
806 	}
807 
808 	return NULL;
809 }
810 
811 
812 void
813 Volume::DisableCDDBLookUps()
814 {
815 	bool doLookup = false;
816 	RootNode().AddAttribute(kDoLookupAttribute, B_BOOL_TYPE, true,
817 		(const uint8*)&doLookup, sizeof(bool));
818 }
819 
820 
821 /*static*/ void
822 Volume::DetermineName(uint32 cddbID, int device, char* name, size_t length)
823 {
824 	name[0] = '\0';
825 
826 	int attrFD = open_attributes(cddbID, device, O_RDONLY,
827 		kDiscIDAttributes);
828 	if (attrFD < 0) {
829 		// We do not have attributes set. Read CD text.
830 		cdtext text;
831 		if (read_cdtext(device, text) == B_OK) {
832 			if (text.artist != NULL && text.album != NULL)
833 				snprintf(name, length, "%s - %s", text.artist, text.album);
834 			else if (text.artist != NULL || text.album != NULL) {
835 				snprintf(name, length, "%s", text.artist != NULL
836 					? text.artist : text.album);
837 			}
838 		}
839 	} else {
840 		// We have an attribute file. Read name from it.
841 		if (!read_line(attrFD, name, length))
842 			name[0] = '\0';
843 
844 		close(attrFD);
845 	}
846 
847 	if (!name[0])
848 		strlcpy(name, "Audio CD", length);
849 }
850 
851 
852 Inode*
853 Volume::_CreateNode(Inode* parent, const char* name, uint64 start,
854 	uint64 frames, int32 type)
855 {
856 	Inode* inode = new Inode(this, parent, name, start, frames, 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 	// device attributes overwrite shared attributes
966 
967 	int fd = _OpenAttributes(O_RDONLY, kSharedAttributes);
968 	if (fd >= 0) {
969 		read_attributes(fd, fRootNode);
970 		close(fd);
971 	}
972 
973 	fd = _OpenAttributes(O_RDONLY, kDeviceAttributes);
974 	if (fd >= 0) {
975 		read_attributes(fd, fRootNode);
976 		close(fd);
977 	}
978 }
979 
980 
981 void
982 Volume::_StoreSharedAttributes()
983 {
984 	// write shared and device specific settings
985 
986 	int fd = _OpenAttributes(O_WRONLY, kSharedAttributes);
987 	if (fd >= 0) {
988 		write_attributes(fd, fRootNode, kSharedAttributes);
989 		close(fd);
990 	}
991 
992 	fd = _OpenAttributes(O_WRONLY, kDeviceAttributes);
993 	if (fd >= 0) {
994 		write_attributes(fd, fRootNode, kDeviceAttributes);
995 		close(fd);
996 	}
997 }
998 
999 
1000 //	#pragma mark - Attribute class
1001 
1002 
1003 Attribute::Attribute(const char* name, type_code type)
1004 	:
1005 	fName(NULL),
1006 	fType(0),
1007 	fData(NULL),
1008 	fSize(0)
1009 {
1010 	SetTo(name, type);
1011 }
1012 
1013 
1014 Attribute::~Attribute()
1015 {
1016 	free(fName);
1017 	free(fData);
1018 }
1019 
1020 
1021 status_t
1022 Attribute::SetTo(const char* name, type_code type)
1023 {
1024 	if (name == NULL || !name[0])
1025 		return B_BAD_VALUE;
1026 
1027 	name = strdup(name);
1028 	if (name == NULL)
1029 		return B_NO_MEMORY;
1030 
1031 	free(fName);
1032 
1033 	fName = (char*)name;
1034 	fType = type;
1035 	return B_OK;
1036 }
1037 
1038 
1039 status_t
1040 Attribute::ReadAt(off_t offset, uint8* buffer, size_t* _length)
1041 {
1042 	size_t length = *_length;
1043 
1044 	if (offset < 0)
1045 		return B_BAD_VALUE;
1046 	if (offset >= fSize) {
1047 		*_length = 0;
1048 		return B_OK;
1049 	}
1050 	if (offset + (off_t)length > fSize)
1051 		length = fSize - offset;
1052 
1053 	if (user_memcpy(buffer, fData + offset, length) < B_OK)
1054 		return B_BAD_ADDRESS;
1055 
1056 	*_length = length;
1057 	return B_OK;
1058 }
1059 
1060 
1061 /*!	Writes to the attribute and enlarges it as needed.
1062 	An attribute has a maximum size of 65536 bytes for now.
1063 */
1064 status_t
1065 Attribute::WriteAt(off_t offset, const uint8* buffer, size_t* _length)
1066 {
1067 	size_t length = *_length;
1068 
1069 	if (offset < 0)
1070 		return B_BAD_VALUE;
1071 
1072 	// we limit the attribute size to something reasonable
1073 	off_t end = offset + length;
1074 	if (end > kMaxAttributeSize) {
1075 		end = kMaxAttributeSize;
1076 		length = end - offset;
1077 	}
1078 	if (offset > end) {
1079 		*_length = 0;
1080 		return E2BIG;
1081 	}
1082 
1083 	if (end > fSize) {
1084 		// make room in the data stream
1085 		uint8* data = (uint8*)realloc(fData, end);
1086 		if (data == NULL)
1087 			return B_NO_MEMORY;
1088 
1089 		if (fSize < offset)
1090 			memset(data + fSize, 0, offset - fSize);
1091 
1092 		fData = data;
1093 		fSize = end;
1094 	}
1095 
1096 	if (user_memcpy(fData + offset, buffer, length) < B_OK)
1097 		return B_BAD_ADDRESS;
1098 
1099 	*_length = length;
1100 	return B_OK;
1101 }
1102 
1103 
1104 //!	Removes all data from the attribute.
1105 void
1106 Attribute::Truncate()
1107 {
1108 	free(fData);
1109 	fData = NULL;
1110 	fSize = 0;
1111 }
1112 
1113 
1114 /*!	Resizes the data part of an attribute to the requested amount \a size.
1115 	An attribute has a maximum size of 65536 bytes for now.
1116 */
1117 status_t
1118 Attribute::SetSize(off_t size)
1119 {
1120 	if (size > kMaxAttributeSize)
1121 		return E2BIG;
1122 
1123 	uint8* data = (uint8*)realloc(fData, size);
1124 	if (data == NULL)
1125 		return B_NO_MEMORY;
1126 
1127 	if (fSize < size)
1128 		memset(data + fSize, 0, size - fSize);
1129 
1130 	fData = data;
1131 	fSize = size;
1132 	return B_OK;
1133 }
1134 
1135 
1136 bool
1137 Attribute::IsProtectedNamespace()
1138 {
1139 	// Check if the attribute is in the restricted namespace. Attributes in
1140 	// this namespace should not be edited by the user as they are handled
1141 	// internally by the add-on. Calls the static version.
1142 	return IsProtectedNamespace(fName);
1143 }
1144 
1145 
1146 bool
1147 Attribute::IsProtectedNamespace(const char* name)
1148 {
1149 	// Convenience static version of the above method. Usually called when we
1150 	// don't have a constructed Attribute object handy.
1151 	return strncmp(kProtectedAttrNamespace, name,
1152 		strlen(kProtectedAttrNamespace)) == 0;
1153 }
1154 
1155 
1156 //	#pragma mark - Inode class
1157 
1158 
1159 Inode::Inode(Volume* volume, Inode* parent, const char* name, uint64 start,
1160 		uint64 frames, int32 type)
1161 	:
1162 	fNext(NULL)
1163 {
1164 	fName = strdup(name);
1165 	if (fName == NULL)
1166 		return;
1167 
1168 	fID = volume->GetNextNodeID();
1169 	fType = type;
1170 	fStartFrame = start;
1171 	fFrameCount = frames;
1172 
1173 	fUserID = geteuid();
1174 	fGroupID = parent ? parent->GroupID() : getegid();
1175 
1176 	fCreationTime = fModificationTime = time(NULL);
1177 
1178 	if (frames) {
1179 		// initialize WAV header
1180 
1181 		// RIFF header
1182 		fWAVHeader.header.magic = B_HOST_TO_BENDIAN_INT32('RIFF');
1183 		fWAVHeader.header.length = B_HOST_TO_LENDIAN_INT32(Size()
1184 			+ sizeof(wav_header) - sizeof(riff_chunk));
1185 		fWAVHeader.header.id = B_HOST_TO_BENDIAN_INT32('WAVE');
1186 
1187 		// 'fmt ' format chunk
1188 		fWAVHeader.format.fourcc = B_HOST_TO_BENDIAN_INT32('fmt ');
1189 		fWAVHeader.format.length = B_HOST_TO_LENDIAN_INT32(
1190 			sizeof(wav_format_chunk) - sizeof(riff_chunk));
1191 		fWAVHeader.format.format_tag = B_HOST_TO_LENDIAN_INT16(1);
1192 		fWAVHeader.format.channels = B_HOST_TO_LENDIAN_INT16(2);
1193 		fWAVHeader.format.samples_per_second = B_HOST_TO_LENDIAN_INT32(44100);
1194 		fWAVHeader.format.average_bytes_per_second = B_HOST_TO_LENDIAN_INT32(
1195 			44100 * sizeof(uint16) * 2);
1196 		fWAVHeader.format.block_align = B_HOST_TO_LENDIAN_INT16(4);
1197 		fWAVHeader.format.bits_per_sample = B_HOST_TO_LENDIAN_INT16(16);
1198 
1199 		// 'data' chunk
1200 		fWAVHeader.data.fourcc = B_HOST_TO_BENDIAN_INT32('data');
1201 		fWAVHeader.data.length = B_HOST_TO_LENDIAN_INT32(Size());
1202 	}
1203 }
1204 
1205 
1206 Inode::~Inode()
1207 {
1208 	free(const_cast<char*>(fName));
1209 }
1210 
1211 
1212 status_t
1213 Inode::InitCheck()
1214 {
1215 	if (fName == NULL)
1216 		return B_NO_MEMORY;
1217 
1218 	return B_OK;
1219 }
1220 
1221 
1222 status_t
1223 Inode::SetName(const char* name)
1224 {
1225 	if (name == NULL || !name[0]
1226 		|| strchr(name, '/') != NULL
1227 		|| strchr(name, '\n') != NULL)
1228 		return B_BAD_VALUE;
1229 
1230 	name = strdup(name);
1231 	if (name == NULL)
1232 		return B_NO_MEMORY;
1233 
1234 	free(fName);
1235 	fName = (char*)name;
1236 	return B_OK;
1237 }
1238 
1239 
1240 Attribute*
1241 Inode::FindAttribute(const char* name) const
1242 {
1243 	if (name == NULL || !name[0])
1244 		return NULL;
1245 
1246 	AttributeList::ConstIterator iterator = fAttributes.GetIterator();
1247 
1248 	while (iterator.HasNext()) {
1249 		Attribute* attribute = iterator.Next();
1250 		if (!strcmp(attribute->Name(), name))
1251 			return attribute;
1252 	}
1253 
1254 	return NULL;
1255 }
1256 
1257 
1258 status_t
1259 Inode::AddAttribute(Attribute* attribute, bool overwrite)
1260 {
1261 	Attribute* oldAttribute = FindAttribute(attribute->Name());
1262 	if (oldAttribute != NULL) {
1263 		if (!overwrite)
1264 			return B_NAME_IN_USE;
1265 
1266 		fAttributes.Remove(oldAttribute);
1267 		delete oldAttribute;
1268 	}
1269 
1270 	fAttributes.Add(attribute);
1271 	return B_OK;
1272 }
1273 
1274 
1275 status_t
1276 Inode::AddAttribute(const char* name, type_code type, bool overwrite,
1277 	const uint8* data, size_t length)
1278 {
1279 	Attribute* attribute = new Attribute(name, type);
1280 	if (attribute == NULL)
1281 		return B_NO_MEMORY;
1282 
1283 	status_t status = attribute->InitCheck();
1284 	if (status == B_OK && data != NULL && length != 0)
1285 		status = attribute->WriteAt(0, data, &length);
1286 	if (status == B_OK)
1287 		status = AddAttribute(attribute, overwrite);
1288 	if (status != B_OK) {
1289 		delete attribute;
1290 		return status;
1291 	}
1292 
1293 	return B_OK;
1294 }
1295 
1296 
1297 status_t
1298 Inode::AddAttribute(const char* name, type_code type, const char* string)
1299 {
1300 	if (string == NULL)
1301 		return B_BAD_VALUE;
1302 
1303 	return AddAttribute(name, type, true, (const uint8*)string,
1304 		strlen(string));
1305 }
1306 
1307 
1308 status_t
1309 Inode::AddAttribute(const char* name, type_code type, uint32 value)
1310 {
1311 	uint32 data = B_HOST_TO_LENDIAN_INT32(value);
1312 	return AddAttribute(name, type, true, (const uint8*)&data, sizeof(uint32));
1313 }
1314 
1315 
1316 status_t
1317 Inode::AddAttribute(const char* name, type_code type, uint64 value)
1318 {
1319 	uint64 data = B_HOST_TO_LENDIAN_INT64(value);
1320 	return AddAttribute(name, type, true, (const uint8*)&data, sizeof(uint64));
1321 }
1322 
1323 
1324 status_t
1325 Inode::RemoveAttribute(const char* name, bool checkNamespace)
1326 {
1327 	if (name == NULL || !name[0])
1328 		return B_ENTRY_NOT_FOUND;
1329 
1330 	AttributeList::Iterator iterator = fAttributes.GetIterator();
1331 
1332 	while (iterator.HasNext()) {
1333 		Attribute* attribute = iterator.Next();
1334 		if (!strcmp(attribute->Name(), name)) {
1335 			// check for restricted namespace if required.
1336 			if (checkNamespace && attribute->IsProtectedNamespace())
1337 				return B_NOT_ALLOWED;
1338 			// look for attribute in cookies
1339 			AttrCookieList::Iterator i = fAttrCookies.GetIterator();
1340 			while (i.HasNext()) {
1341 				attr_cookie* cookie = i.Next();
1342 				if (cookie->current == attribute) {
1343 					cookie->current
1344 						= attribute->GetDoublyLinkedListLink()->next;
1345 				}
1346 			}
1347 
1348 			iterator.Remove();
1349 			delete attribute;
1350 			return B_OK;
1351 		}
1352 	}
1353 
1354 	return B_ENTRY_NOT_FOUND;
1355 }
1356 
1357 
1358 void
1359 Inode::AddAttrCookie(attr_cookie* cookie)
1360 {
1361 	fAttrCookies.Add(cookie);
1362 	RewindAttrCookie(cookie);
1363 }
1364 
1365 
1366 void
1367 Inode::RemoveAttrCookie(attr_cookie* cookie)
1368 {
1369 	fAttrCookies.Remove(cookie);
1370 }
1371 
1372 
1373 void
1374 Inode::RewindAttrCookie(attr_cookie* cookie)
1375 {
1376 	cookie->current = fAttributes.First();
1377 }
1378 
1379 
1380 //	#pragma mark - Module API
1381 
1382 
1383 static float
1384 cdda_identify_partition(int fd, partition_data* partition, void** _cookie)
1385 {
1386 	scsi_toc_toc* toc = (scsi_toc_toc*)malloc(2048);
1387 	if (toc == NULL)
1388 		return B_NO_MEMORY;
1389 
1390 	status_t status = read_table_of_contents(fd, toc, 2048);
1391 
1392 	// If we succeeded in reading the toc, check the tracks in the
1393 	// partition, which may not be the whole CD, and if any are audio,
1394 	// claim the partition.
1395 	if (status == B_OK) {
1396 		uint32 trackCount = toc->last_track + (uint32)1 - toc->first_track;
1397 		uint64 sessionStartLBA = partition->offset / partition->block_size;
1398 		uint64 sessionEndLBA	= sessionStartLBA
1399 			+ (partition->size / partition->block_size);
1400 		TRACE(("cdda_identify_partition: session at %lld-%lld\n",
1401 			sessionStartLBA, sessionEndLBA));
1402 		status = B_ENTRY_NOT_FOUND;
1403 		for (uint32 i = 0; i < trackCount; i++) {
1404 			// We have to get trackLBA from track.start.time since
1405 			// track.start.lba is useless for this.
1406 			// This is how session gets it.
1407 			uint64 trackLBA
1408 				= ((toc->tracks[i].start.time.minute * kFramesPerMinute)
1409 					+ (toc->tracks[i].start.time.second * kFramesPerSecond)
1410 					+ toc->tracks[i].start.time.frame - 150);
1411 			if (trackLBA >= sessionStartLBA && trackLBA < sessionEndLBA) {
1412 				if (is_data_track(toc->tracks[i])) {
1413 					TRACE(("cdda_identify_partition: track %ld at %lld is "
1414 						"data\n", i + 1, trackLBA));
1415 					status = B_BAD_TYPE;
1416 				} else {
1417 					TRACE(("cdda_identify_partition: track %ld at %lld is "
1418 						"audio\n", i + 1, trackLBA));
1419 					status = B_OK;
1420 					break;
1421 				}
1422 			}
1423 		}
1424 	}
1425 
1426 	if (status != B_OK) {
1427 		free(toc);
1428 		return status;
1429 	}
1430 
1431 	*_cookie = toc;
1432 	return 0.8f;
1433 }
1434 
1435 
1436 static status_t
1437 cdda_scan_partition(int fd, partition_data* partition, void* _cookie)
1438 {
1439 	scsi_toc_toc* toc = (scsi_toc_toc*)_cookie;
1440 
1441 	partition->status = B_PARTITION_VALID;
1442 	partition->flags |= B_PARTITION_FILE_SYSTEM;
1443 
1444 	// compute length
1445 
1446 	uint32 lastTrack = toc->last_track + 1 - toc->first_track;
1447 	scsi_cd_msf& end = toc->tracks[lastTrack].start.time;
1448 
1449 	partition->content_size = off_t(end.minute * kFramesPerMinute
1450 		+ end.second * kFramesPerSecond + end.frame) * kFrameSize;
1451 	partition->block_size = kFrameSize;
1452 
1453 	// determine volume title
1454 
1455 	char name[256];
1456 	Volume::DetermineName(compute_cddb_disc_id(*toc), fd, name, sizeof(name));
1457 
1458 	partition->content_name = strdup(name);
1459 	if (partition->content_name == NULL)
1460 		return B_NO_MEMORY;
1461 
1462 	return B_OK;
1463 }
1464 
1465 
1466 static void
1467 cdda_free_identify_partition_cookie(partition_data* partition, void* _cookie)
1468 {
1469 	free(_cookie);
1470 }
1471 
1472 
1473 static status_t
1474 cdda_mount(fs_volume* fsVolume, const char* device, uint32 flags,
1475 	const char* args, ino_t* _rootVnodeID)
1476 {
1477 	TRACE(("cdda_mount: entry\n"));
1478 
1479 	Volume* volume = new Volume(fsVolume);
1480 	if (volume == NULL)
1481 		return B_NO_MEMORY;
1482 
1483 	status_t status = volume->InitCheck();
1484 	if (status == B_OK)
1485 		status = volume->Mount(device);
1486 
1487 	if (status < B_OK) {
1488 		delete volume;
1489 		return status;
1490 	}
1491 
1492 	*_rootVnodeID = volume->RootNode().ID();
1493 	fsVolume->private_volume = volume;
1494 	fsVolume->ops = &gCDDAVolumeOps;
1495 
1496 	return B_OK;
1497 }
1498 
1499 
1500 static status_t
1501 cdda_unmount(fs_volume* _volume)
1502 {
1503 	struct Volume* volume = (struct Volume*)_volume->private_volume;
1504 
1505 	TRACE(("cdda_unmount: entry fs = %p\n", _volume));
1506 	delete volume;
1507 
1508 	return 0;
1509 }
1510 
1511 
1512 static status_t
1513 cdda_read_fs_stat(fs_volume* _volume, struct fs_info* info)
1514 {
1515 	Volume* volume = (Volume*)_volume->private_volume;
1516 	Locker locker(volume->Lock());
1517 
1518 	// File system flags.
1519 	info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR | B_FS_HAS_MIME
1520 		| B_FS_IS_REMOVABLE;
1521 	info->io_size = 65536;
1522 
1523 	info->block_size = 2048;
1524 	info->total_blocks = volume->NumBlocks();
1525 	info->free_blocks = 0;
1526 
1527 	// Volume name
1528 	strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name));
1529 
1530 	// File system name
1531 	strlcpy(info->fsh_name, "cdda", sizeof(info->fsh_name));
1532 
1533 	return B_OK;
1534 }
1535 
1536 
1537 static status_t
1538 cdda_write_fs_stat(fs_volume* _volume, const struct fs_info* info, uint32 mask)
1539 {
1540 	Volume* volume = (Volume*)_volume->private_volume;
1541 	Locker locker(volume->Lock());
1542 
1543 	status_t status = B_BAD_VALUE;
1544 
1545 	if ((mask & FS_WRITE_FSINFO_NAME) != 0) {
1546 		status = volume->SetName(info->volume_name);
1547 		if (status == B_OK) {
1548 			// The volume had its name changed from outside the filesystem
1549 			// add-on. Disable CDDB lookups. Note this will usually mean that
1550 			// the user manually renamed the volume or that cddblinkd (or other
1551 			// program) did this so we do not want to do it again.
1552 			volume->DisableCDDBLookUps();
1553 		}
1554 	}
1555 
1556 	return status;
1557 }
1558 
1559 
1560 static status_t
1561 cdda_sync(fs_volume* _volume)
1562 {
1563 	TRACE(("cdda_sync: entry\n"));
1564 
1565 	return B_OK;
1566 }
1567 
1568 
1569 static status_t
1570 cdda_lookup(fs_volume* _volume, fs_vnode* _dir, const char* name, ino_t* _id)
1571 {
1572 	Volume* volume = (Volume*)_volume->private_volume;
1573 	status_t status;
1574 
1575 	TRACE(("cdda_lookup: entry dir %p, name '%s'\n", _dir, name));
1576 
1577 	Inode* directory = (Inode*)_dir->private_node;
1578 	if (!S_ISDIR(directory->Type()))
1579 		return B_NOT_A_DIRECTORY;
1580 
1581 	Locker _(volume->Lock());
1582 
1583 	Inode* inode = volume->Find(name);
1584 	if (inode == NULL)
1585 		return B_ENTRY_NOT_FOUND;
1586 
1587 	status = get_vnode(volume->FSVolume(), inode->ID(), NULL);
1588 	if (status < B_OK)
1589 		return status;
1590 
1591 	*_id = inode->ID();
1592 	return B_OK;
1593 }
1594 
1595 
1596 static status_t
1597 cdda_get_vnode_name(fs_volume* _volume, fs_vnode* _node, char* buffer,
1598 	size_t bufferSize)
1599 {
1600 	Volume* volume = (Volume*)_volume->private_volume;
1601 	Inode* inode = (Inode*)_node->private_node;
1602 
1603 	TRACE(("cdda_get_vnode_name(): inode = %p\n", inode));
1604 
1605 	Locker _(volume->Lock());
1606 	strlcpy(buffer, inode->Name(), bufferSize);
1607 	return B_OK;
1608 }
1609 
1610 
1611 static status_t
1612 cdda_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _node, int* _type,
1613 	uint32* _flags, bool reenter)
1614 {
1615 	Volume* volume = (Volume*)_volume->private_volume;
1616 	Inode* inode;
1617 
1618 	TRACE(("cdda_getvnode: asking for vnode 0x%Lx, r %d\n", id, reenter));
1619 
1620 	inode = volume->Find(id);
1621 	if (inode == NULL)
1622 		return B_ENTRY_NOT_FOUND;
1623 
1624 	_node->private_node = inode;
1625 	_node->ops = &gCDDAVnodeOps;
1626 	*_type = inode->Type();
1627 	*_flags = 0;
1628 	return B_OK;
1629 }
1630 
1631 
1632 static status_t
1633 cdda_put_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
1634 {
1635 	return B_OK;
1636 }
1637 
1638 
1639 static status_t
1640 cdda_open(fs_volume* _volume, fs_vnode* _node, int openMode, void** _cookie)
1641 {
1642 	TRACE(("cdda_open(): node = %p, openMode = %d\n", _node, openMode));
1643 
1644 	file_cookie* cookie = (file_cookie*)malloc(sizeof(file_cookie));
1645 	if (cookie == NULL)
1646 		return B_NO_MEMORY;
1647 
1648 	TRACE(("  open cookie = %p\n", cookie));
1649 	cookie->open_mode = openMode;
1650 	cookie->buffer = NULL;
1651 
1652 	*_cookie = (void*)cookie;
1653 
1654 	return B_OK;
1655 }
1656 
1657 
1658 static status_t
1659 cdda_close(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1660 {
1661 	return B_OK;
1662 }
1663 
1664 
1665 static status_t
1666 cdda_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1667 {
1668 	file_cookie* cookie = (file_cookie*)_cookie;
1669 
1670 	TRACE(("cdda_freecookie: entry vnode %p, cookie %p\n", _node, _cookie));
1671 
1672 	free(cookie);
1673 	return B_OK;
1674 }
1675 
1676 
1677 static status_t
1678 cdda_fsync(fs_volume* _volume, fs_vnode* _node)
1679 {
1680 	return B_OK;
1681 }
1682 
1683 
1684 static status_t
1685 cdda_read(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t offset,
1686 	void* buffer, size_t* _length)
1687 {
1688 	file_cookie* cookie = (file_cookie*)_cookie;
1689 	Volume* volume = (Volume*)_volume->private_volume;
1690 	Inode* inode = (Inode*)_node->private_node;
1691 
1692 	TRACE(("cdda_read(vnode = %p, offset %Ld, length = %lu, mode = %d)\n",
1693 		_node, offset, *_length, cookie->open_mode));
1694 
1695 	if (S_ISDIR(inode->Type()))
1696 		return B_IS_A_DIRECTORY;
1697 	if (offset < 0)
1698 		return B_BAD_VALUE;
1699 
1700 	off_t maxSize = inode->Size() + sizeof(wav_header);
1701 	if (offset >= maxSize) {
1702 		*_length = 0;
1703 		return B_OK;
1704 	}
1705 
1706 	if (cookie->buffer == NULL) {
1707 		// TODO: move that to open() to make sure reading can't fail for this reason?
1708 		cookie->buffer = malloc(volume->BufferSize());
1709 		if (cookie->buffer == NULL)
1710 			return B_NO_MEMORY;
1711 
1712 		cookie->buffer_offset = -1;
1713 	}
1714 
1715 	size_t length = *_length;
1716 	if (offset + (off_t)length > maxSize)
1717 		length = maxSize - offset;
1718 
1719 	status_t status = B_OK;
1720 	size_t bytesRead = 0;
1721 
1722 	if (offset < (off_t)sizeof(wav_header)) {
1723 		// read fake WAV header
1724 		size_t size = sizeof(wav_header) - offset;
1725 		size = min_c(size, length);
1726 
1727 		if (user_memcpy(buffer, (uint8*)inode->WAVHeader() + offset, size)
1728 				< B_OK)
1729 			return B_BAD_ADDRESS;
1730 
1731 		buffer = (void*)((uint8*)buffer + size);
1732 		length -= size;
1733 		bytesRead += size;
1734 		offset = 0;
1735 	} else
1736 		offset -= sizeof(wav_header);
1737 
1738 	if (length > 0) {
1739 		// read actual CD data
1740 		offset += inode->StartFrame() * kFrameSize;
1741 
1742 		status = read_cdda_data(volume->Device(),
1743 			inode->StartFrame() + inode->FrameCount(), offset, buffer, length,
1744 			cookie->buffer_offset, cookie->buffer, volume->BufferSize());
1745 
1746 		bytesRead += length;
1747 	}
1748 	if (status == B_OK)
1749 		*_length = bytesRead;
1750 
1751 	return status;
1752 }
1753 
1754 
1755 static bool
1756 cdda_can_page(fs_volume* _volume, fs_vnode* _node, void* cookie)
1757 {
1758 	return false;
1759 }
1760 
1761 
1762 static status_t
1763 cdda_read_pages(fs_volume* _volume, fs_vnode* _node, void* cookie, off_t pos,
1764 	const iovec* vecs, size_t count, size_t* _numBytes)
1765 {
1766 	return B_NOT_ALLOWED;
1767 }
1768 
1769 
1770 static status_t
1771 cdda_write_pages(fs_volume* _volume, fs_vnode* _node, void* cookie, off_t pos,
1772 	const iovec* vecs, size_t count, size_t* _numBytes)
1773 {
1774 	return B_NOT_ALLOWED;
1775 }
1776 
1777 
1778 static status_t
1779 cdda_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat)
1780 {
1781 	Volume* volume = (Volume*)_volume->private_volume;
1782 	Inode* inode = (Inode*)_node->private_node;
1783 
1784 	TRACE(("cdda_read_stat: vnode %p (0x%Lx), stat %p\n", inode, inode->ID(),
1785 		stat));
1786 
1787 	fill_stat_buffer(volume, inode, NULL, *stat);
1788 
1789 	return B_OK;
1790 }
1791 
1792 
1793 status_t
1794 cdda_rename(fs_volume* _volume, fs_vnode* _oldDir, const char* oldName,
1795 	fs_vnode* _newDir, const char* newName)
1796 {
1797 	if (_oldDir->private_node != _newDir->private_node)
1798 		return B_BAD_VALUE;
1799 
1800 	// we only have a single directory which simplifies things a bit :-)
1801 
1802 	Volume *volume = (Volume*)_volume->private_volume;
1803 	Locker _(volume->Lock());
1804 
1805 	Inode* inode = volume->Find(oldName);
1806 	if (inode == NULL)
1807 		return B_ENTRY_NOT_FOUND;
1808 
1809 	if (volume->Find(newName) != NULL)
1810 		return B_NAME_IN_USE;
1811 
1812 	status_t status = inode->SetName(newName);
1813 	if (status == B_OK) {
1814 		// One of the tracks had its name edited from outside the filesystem
1815 		// add-on. Disable CDDB lookups. Note this will usually mean that the
1816 		// user manually renamed a track or that cddblinkd (or other program)
1817 		// did this so we do not want to do it again.
1818 		volume->DisableCDDBLookUps();
1819 
1820 		notify_entry_moved(volume->ID(), volume->RootNode().ID(), oldName,
1821 			volume->RootNode().ID(), newName, inode->ID());
1822 	}
1823 
1824 	return status;
1825 }
1826 
1827 
1828 //	#pragma mark - directory functions
1829 
1830 
1831 static status_t
1832 cdda_open_dir(fs_volume* _volume, fs_vnode* _node, void** _cookie)
1833 {
1834 	Volume* volume = (Volume*)_volume->private_volume;
1835 
1836 	TRACE(("cdda_open_dir(): vnode = %p\n", _node));
1837 
1838 	Inode* inode = (Inode*)_node->private_node;
1839 	if (!S_ISDIR(inode->Type()))
1840 		return B_NOT_A_DIRECTORY;
1841 
1842 	if (inode != &volume->RootNode())
1843 		panic("pipefs: found directory that's not the root!");
1844 
1845 	dir_cookie* cookie = (dir_cookie*)malloc(sizeof(dir_cookie));
1846 	if (cookie == NULL)
1847 		return B_NO_MEMORY;
1848 
1849 	cookie->current = volume->FirstEntry();
1850 	cookie->state = ITERATION_STATE_BEGIN;
1851 
1852 	*_cookie = (void*)cookie;
1853 	return B_OK;
1854 }
1855 
1856 
1857 static status_t
1858 cdda_read_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie,
1859 	struct dirent* buffer, size_t bufferSize, uint32* _num)
1860 {
1861 	Volume* volume = (Volume*)_volume->private_volume;
1862 	Inode* inode = (Inode*)_node->private_node;
1863 
1864 	TRACE(("cdda_read_dir: vnode %p, cookie %p, buffer = %p, bufferSize = %ld,"
1865 		" num = %p\n", _node, _cookie, buffer, bufferSize,_num));
1866 
1867 	if ((Inode*)_node->private_node != &volume->RootNode())
1868 		return B_BAD_VALUE;
1869 
1870 	Locker _(volume->Lock());
1871 
1872 	dir_cookie* cookie = (dir_cookie*)_cookie;
1873 	Inode* childNode = NULL;
1874 	const char* name = NULL;
1875 	Inode* nextChildNode = NULL;
1876 	int nextState = cookie->state;
1877 	uint32 max = *_num;
1878 	uint32 count = 0;
1879 
1880 	while (count < max && bufferSize > sizeof(dirent)) {
1881 		switch (cookie->state) {
1882 			case ITERATION_STATE_DOT:
1883 				childNode = inode;
1884 				name = ".";
1885 				nextChildNode = volume->FirstEntry();
1886 				nextState = cookie->state + 1;
1887 				break;
1888 			case ITERATION_STATE_DOT_DOT:
1889 				childNode = inode; // parent of the root node is the root node
1890 				name = "..";
1891 				nextChildNode = volume->FirstEntry();
1892 				nextState = cookie->state + 1;
1893 				break;
1894 			default:
1895 				childNode = cookie->current;
1896 				if (childNode) {
1897 					name = childNode->Name();
1898 					nextChildNode = childNode->Next();
1899 				}
1900 				break;
1901 		}
1902 
1903 		if (childNode == NULL) {
1904 			// we're at the end of the directory
1905 			break;
1906 		}
1907 
1908 		buffer->d_dev = volume->FSVolume()->id;
1909 		buffer->d_ino = childNode->ID();
1910 		buffer->d_reclen = strlen(name) + sizeof(struct dirent);
1911 
1912 		if (buffer->d_reclen > bufferSize) {
1913 			if (count == 0)
1914 				return ENOBUFS;
1915 
1916 			break;
1917 		}
1918 
1919 		strcpy(buffer->d_name, name);
1920 
1921 		bufferSize -= buffer->d_reclen;
1922 		buffer = (struct dirent*)((uint8*)buffer + buffer->d_reclen);
1923 		count++;
1924 
1925 		cookie->current = nextChildNode;
1926 		cookie->state = nextState;
1927 	}
1928 
1929 	*_num = count;
1930 	return B_OK;
1931 }
1932 
1933 
1934 static status_t
1935 cdda_rewind_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1936 {
1937 	Volume* volume = (Volume*)_volume->private_volume;
1938 
1939 	dir_cookie* cookie = (dir_cookie*)_cookie;
1940 	cookie->current = volume->FirstEntry();
1941 	cookie->state = ITERATION_STATE_BEGIN;
1942 
1943 	return B_OK;
1944 }
1945 
1946 
1947 static status_t
1948 cdda_close_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1949 {
1950 	TRACE(("cdda_close: entry vnode %p, cookie %p\n", _node, _cookie));
1951 
1952 	return 0;
1953 }
1954 
1955 
1956 static status_t
1957 cdda_free_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1958 {
1959 	dir_cookie* cookie = (dir_cookie*)_cookie;
1960 
1961 	TRACE(("cdda_freecookie: entry vnode %p, cookie %p\n", _node, cookie));
1962 
1963 	free(cookie);
1964 	return 0;
1965 }
1966 
1967 
1968 //	#pragma mark - attribute functions
1969 
1970 
1971 static status_t
1972 cdda_open_attr_dir(fs_volume* _volume, fs_vnode* _node, void** _cookie)
1973 {
1974 	Volume* volume = (Volume*)_volume->private_volume;
1975 	Inode* inode = (Inode*)_node->private_node;
1976 
1977 	attr_cookie* cookie = new(std::nothrow) attr_cookie;
1978 	if (cookie == NULL)
1979 		return B_NO_MEMORY;
1980 
1981 	Locker _(volume->Lock());
1982 
1983 	inode->AddAttrCookie(cookie);
1984 	*_cookie = cookie;
1985 	return B_OK;
1986 }
1987 
1988 
1989 static status_t
1990 cdda_close_attr_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1991 {
1992 	return B_OK;
1993 }
1994 
1995 
1996 static status_t
1997 cdda_free_attr_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1998 {
1999 	Volume* volume = (Volume*)_volume->private_volume;
2000 	Inode* inode = (Inode*)_node->private_node;
2001 	attr_cookie* cookie = (attr_cookie*)_cookie;
2002 
2003 	Locker _(volume->Lock());
2004 
2005 	inode->RemoveAttrCookie(cookie);
2006 	delete cookie;
2007 	return B_OK;
2008 }
2009 
2010 
2011 static status_t
2012 cdda_rewind_attr_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie)
2013 {
2014 	Volume* volume = (Volume*)_volume->private_volume;
2015 	Inode* inode = (Inode*)_node->private_node;
2016 	attr_cookie* cookie = (attr_cookie*)_cookie;
2017 
2018 	Locker _(volume->Lock());
2019 
2020 	inode->RewindAttrCookie(cookie);
2021 	return B_OK;
2022 }
2023 
2024 
2025 static status_t
2026 cdda_read_attr_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie,
2027 	struct dirent* dirent, size_t bufferSize, uint32* _num)
2028 {
2029 	Volume* volume = (Volume*)_volume->private_volume;
2030 	Inode* inode = (Inode*)_node->private_node;
2031 	attr_cookie* cookie = (attr_cookie*)_cookie;
2032 
2033 	Locker _(volume->Lock());
2034 	Attribute* attribute = cookie->current;
2035 
2036 	if (attribute == NULL) {
2037 		*_num = 0;
2038 		return B_OK;
2039 	}
2040 
2041 	size_t length = strlcpy(dirent->d_name, attribute->Name(), bufferSize);
2042 	dirent->d_dev = volume->FSVolume()->id;
2043 	dirent->d_ino = inode->ID();
2044 	dirent->d_reclen = sizeof(struct dirent) + length;
2045 
2046 	cookie->current = attribute->GetDoublyLinkedListLink()->next;
2047 	*_num = 1;
2048 	return B_OK;
2049 }
2050 
2051 
2052 static status_t
2053 cdda_create_attr(fs_volume* _volume, fs_vnode* _node, const char* name,
2054 	uint32 type, int openMode, void** _cookie)
2055 {
2056 	Volume *volume = (Volume*)_volume->private_volume;
2057 	Inode *inode = (Inode*)_node->private_node;
2058 
2059 	Locker _(volume->Lock());
2060 
2061 	Attribute* attribute = inode->FindAttribute(name);
2062 	if (attribute == NULL) {
2063 		if (Attribute::IsProtectedNamespace(name))
2064 			return B_NOT_ALLOWED;
2065 		status_t status = inode->AddAttribute(name, type, true, NULL, 0);
2066 		if (status != B_OK)
2067 			return status;
2068 
2069 		notify_attribute_changed(volume->ID(), inode->ID(), name,
2070 			B_ATTR_CREATED);
2071 	} else if ((openMode & O_EXCL) == 0) {
2072 		if (attribute->IsProtectedNamespace())
2073 			return B_NOT_ALLOWED;
2074 		attribute->SetType(type);
2075 		if ((openMode & O_TRUNC) != 0)
2076 			attribute->Truncate();
2077 	} else
2078 		return B_FILE_EXISTS;
2079 
2080 	*_cookie = strdup(name);
2081 	if (*_cookie == NULL)
2082 		return B_NO_MEMORY;
2083 
2084 	return B_OK;
2085 }
2086 
2087 
2088 static status_t
2089 cdda_open_attr(fs_volume* _volume, fs_vnode* _node, const char* name,
2090 	int openMode, void** _cookie)
2091 {
2092 	Volume* volume = (Volume*)_volume->private_volume;
2093 	Inode* inode = (Inode*)_node->private_node;
2094 
2095 	Locker _(volume->Lock());
2096 
2097 	Attribute* attribute = inode->FindAttribute(name);
2098 	if (attribute == NULL)
2099 		return B_ENTRY_NOT_FOUND;
2100 
2101 	*_cookie = strdup(name);
2102 	if (*_cookie == NULL)
2103 		return B_NO_MEMORY;
2104 
2105 	return B_OK;
2106 }
2107 
2108 
2109 static status_t
2110 cdda_close_attr(fs_volume* _volume, fs_vnode* _node, void* cookie)
2111 {
2112 	return B_OK;
2113 }
2114 
2115 
2116 static status_t
2117 cdda_free_attr_cookie(fs_volume* _volume, fs_vnode* _node, void* cookie)
2118 {
2119 	free(cookie);
2120 	return B_OK;
2121 }
2122 
2123 
2124 static status_t
2125 cdda_read_attr(fs_volume* _volume, fs_vnode* _node, void* _cookie,
2126 	off_t offset, void* buffer, size_t* _length)
2127 {
2128 	Volume* volume = (Volume*)_volume->private_volume;
2129 	Inode* inode = (Inode*)_node->private_node;
2130 
2131 	Locker _(volume->Lock());
2132 
2133 	Attribute* attribute = inode->FindAttribute((const char*)_cookie);
2134 	if (attribute == NULL)
2135 		return B_ENTRY_NOT_FOUND;
2136 
2137 	return attribute->ReadAt(offset, (uint8*)buffer, _length);
2138 }
2139 
2140 
2141 static status_t
2142 cdda_write_attr(fs_volume* _volume, fs_vnode* _node, void* _cookie,
2143 	off_t offset, const void* buffer, size_t* _length)
2144 {
2145 	Volume* volume = (Volume*)_volume->private_volume;
2146 	Inode* inode = (Inode*)_node->private_node;
2147 
2148 	Locker _(volume->Lock());
2149 
2150 	Attribute* attribute = inode->FindAttribute((const char*)_cookie);
2151 	if (attribute == NULL)
2152 		return B_ENTRY_NOT_FOUND;
2153 
2154 	if (attribute->IsProtectedNamespace())
2155 		return B_NOT_ALLOWED;
2156 
2157 	status_t status = attribute->WriteAt(offset, (uint8*)buffer, _length);
2158 	if (status == B_OK) {
2159 		notify_attribute_changed(volume->ID(), inode->ID(), attribute->Name(),
2160 			B_ATTR_CHANGED);
2161 	}
2162 	return status;
2163 }
2164 
2165 
2166 static status_t
2167 cdda_read_attr_stat(fs_volume* _volume, fs_vnode* _node, void* _cookie,
2168 	struct stat* stat)
2169 {
2170 	Volume* volume = (Volume*)_volume->private_volume;
2171 	Inode* inode = (Inode*)_node->private_node;
2172 
2173 	Locker _(volume->Lock());
2174 
2175 	Attribute* attribute = inode->FindAttribute((const char*)_cookie);
2176 	if (attribute == NULL)
2177 		return B_ENTRY_NOT_FOUND;
2178 
2179 	fill_stat_buffer(volume, inode, attribute, *stat);
2180 	return B_OK;
2181 }
2182 
2183 
2184 static status_t
2185 cdda_write_attr_stat(fs_volume* _volume, fs_vnode* _node, void* cookie,
2186 	const struct stat* stat, int statMask)
2187 {
2188 	return EOPNOTSUPP;
2189 }
2190 
2191 
2192 static status_t
2193 cdda_remove_attr(fs_volume* _volume, fs_vnode* _node, const char* name)
2194 {
2195 	if (name == NULL)
2196 		return B_BAD_VALUE;
2197 
2198 	Volume* volume = (Volume*)_volume->private_volume;
2199 	Inode* inode = (Inode*)_node->private_node;
2200 
2201 	Locker _(volume->Lock());
2202 
2203 	status_t status = inode->RemoveAttribute(name, true);
2204 	if (status == B_OK) {
2205 		notify_attribute_changed(volume->ID(), inode->ID(), name,
2206 			B_ATTR_REMOVED);
2207 	}
2208 
2209 	return status;
2210 }
2211 
2212 
2213 fs_volume_ops gCDDAVolumeOps = {
2214 	cdda_unmount,
2215 	cdda_read_fs_stat,
2216 	cdda_write_fs_stat,
2217 	cdda_sync,
2218 	cdda_get_vnode,
2219 
2220 	// the other operations are not yet supported (indices, queries)
2221 	NULL,
2222 };
2223 
2224 fs_vnode_ops gCDDAVnodeOps = {
2225 	cdda_lookup,
2226 	cdda_get_vnode_name,
2227 	cdda_put_vnode,
2228 	NULL,	// fs_remove_vnode()
2229 
2230 	cdda_can_page,
2231 	cdda_read_pages,
2232 	cdda_write_pages,
2233 
2234 	NULL,	// io()
2235 	NULL,	// cancel_io()
2236 
2237 	NULL,	// get_file_map()
2238 
2239 	// common
2240 	NULL,	// fs_ioctl()
2241 	NULL,	// fs_set_flags()
2242 	NULL,	// fs_select()
2243 	NULL,	// fs_deselect()
2244 	cdda_fsync,
2245 
2246 	NULL,	// fs_read_link()
2247 	NULL,	// fs_symlink()
2248 	NULL,	// fs_link()
2249 	NULL,	// fs_unlink()
2250 	cdda_rename,
2251 
2252 	NULL,	// fs_access()
2253 	cdda_read_stat,
2254 	NULL,	// fs_write_stat()
2255 	NULL,	// fs_preallocate()
2256 
2257 	// file
2258 	NULL,	// fs_create()
2259 	cdda_open,
2260 	cdda_close,
2261 	cdda_free_cookie,
2262 	cdda_read,
2263 	NULL,	// fs_write()
2264 
2265 	// directory
2266 	NULL,	// fs_create_dir()
2267 	NULL,	// fs_remove_dir()
2268 	cdda_open_dir,
2269 	cdda_close_dir,
2270 	cdda_free_dir_cookie,
2271 	cdda_read_dir,
2272 	cdda_rewind_dir,
2273 
2274 	// attribute directory operations
2275 	cdda_open_attr_dir,
2276 	cdda_close_attr_dir,
2277 	cdda_free_attr_dir_cookie,
2278 	cdda_read_attr_dir,
2279 	cdda_rewind_attr_dir,
2280 
2281 	// attribute operations
2282 	cdda_create_attr,
2283 	cdda_open_attr,
2284 	cdda_close_attr,
2285 	cdda_free_attr_cookie,
2286 	cdda_read_attr,
2287 	cdda_write_attr,
2288 
2289 	cdda_read_attr_stat,
2290 	cdda_write_attr_stat,
2291 	NULL,	// fs_rename_attr()
2292 	cdda_remove_attr,
2293 
2294 	NULL,	// fs_create_special_node()
2295 };
2296 
2297 static file_system_module_info sCDDAFileSystem = {
2298 	{
2299 		"file_systems/cdda" B_CURRENT_FS_API_VERSION,
2300 		0,
2301 		NULL,
2302 	},
2303 
2304 	"cdda",					// short_name
2305 	"CDDA File System",		// pretty_name
2306 	0,	// DDM flags
2307 
2308 	cdda_identify_partition,
2309 	cdda_scan_partition,
2310 	cdda_free_identify_partition_cookie,
2311 	NULL,	// free_partition_content_cookie()
2312 
2313 	cdda_mount,
2314 
2315 	// all other functions are not supported
2316 	NULL,
2317 };
2318 
2319 module_info* modules[] = {
2320 	(module_info*)&sCDDAFileSystem,
2321 	NULL,
2322 };
2323