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