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