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