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