xref: /haiku/src/add-ons/kernel/file_systems/cdda/kernel_interface.cpp (revision df44e515de8caa190aa2b45f2ce928b961592c28)
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 
40 class Attribute;
41 class Inode;
42 struct attr_cookie;
43 struct dir_cookie;
44 
45 typedef DoublyLinkedList<Attribute> AttributeList;
46 typedef SinglyLinkedList<attr_cookie> AttrCookieList;
47 
48 struct riff_header {
49 	uint32		magic;
50 	uint32		length;
51 	uint32		id;
52 } _PACKED;
53 
54 struct riff_chunk {
55 	uint32		fourcc;
56 	uint32		length;
57 } _PACEKD;
58 
59 struct 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 
68 struct wav_header {
69 	riff_header			header;
70 	wav_format_chunk	format;
71 	riff_chunk			data;
72 } _PACKED;
73 
74 enum attr_mode {
75 	kDiscIDAttributes,
76 	kSharedAttributes,
77 	kDeviceAttributes
78 };
79 
80 class Volume {
81 public:
82 							Volume(fs_volume* fsVolume);
83 							~Volume();
84 
85 			status_t		InitCheck();
86 
FSVolume() const87 			fs_volume*		FSVolume() const { return fFSVolume; }
ID() const88 			dev_t			ID() const { return fFSVolume->id; }
DiscID() const89 			uint32			DiscID() const { return fDiscID; }
RootNode() const90 			Inode&			RootNode() const { return *fRootNode; }
91 
92 			status_t		Mount(const char* device);
Device() const93 			int				Device() const { return fDevice; }
GetNextNodeID()94 			ino_t			GetNextNodeID() { return fNextID++; }
95 
Name() const96 			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 
FirstEntry() const104 			Inode*			FirstEntry() const { return fFirstEntry; }
105 
NumBlocks() const106 			off_t			NumBlocks() const { return fNumBlocks; }
BufferSize() const107 			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 
115 private:
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 
140 class Attribute : public DoublyLinkedListLinkImpl<Attribute> {
141 public:
142 							Attribute(const char* name, type_code type);
143 							~Attribute();
144 
InitCheck() const145 			status_t		InitCheck() const
146 								{ return fName != NULL ? B_OK : B_NO_MEMORY; }
147 			status_t		SetTo(const char* name, type_code type);
SetType(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 
Name() const158 			const char*		Name() const { return fName; }
Size() const159 			off_t			Size() const { return fSize; }
Type() const160 			type_code		Type() const { return fType; }
Data() const161 			uint8*			Data() const { return fData; }
162 
163 			bool			IsProtectedNamespace();
164 	static	bool			IsProtectedNamespace(const char* name);
165 
166 private:
167 			char*			fName;
168 			type_code		fType;
169 			uint8*			fData;
170 			off_t			fSize;
171 };
172 
173 class Inode {
174 public:
175 							Inode(Volume* volume, Inode* parent,
176 								const char* name, uint64 start, uint64 frames,
177 								int32 type);
178 							~Inode();
179 
180 			status_t		InitCheck();
ID() const181 			ino_t			ID() const { return fID; }
182 
Name() const183 			const char*		Name() const { return fName; }
184 			status_t		SetName(const char* name);
185 
Type() const186 			int32			Type() const
187 								{ return fType; }
GroupID() const188 			gid_t			GroupID() const
189 								{ return fGroupID; }
UserID() const190 			uid_t			UserID() const
191 								{ return fUserID; }
CreationTime() const192 			time_t			CreationTime() const
193 								{ return fCreationTime; }
ModificationTime() const194 			time_t			ModificationTime() const
195 								{ return fModificationTime; }
StartFrame() const196 			uint64			StartFrame() const
197 								{ return fStartFrame; }
FrameCount() const198 			uint64			FrameCount() const
199 								{ return fFrameCount; }
Size() const200 			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 
Attributes() const222 			AttributeList::ConstIterator Attributes() const
223 								{ return fAttributes.GetIterator(); }
224 
WAVHeader() const225 			const wav_header* WAVHeader() const
226 								{ return &fWAVHeader; }
227 
Next() const228 			Inode*			Next() const { return fNext; }
SetNext(Inode * inode)229 			void			SetNext(Inode *inode) { fNext = inode; }
230 
231 private:
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 
247 struct dir_cookie {
248 	Inode*		current;
249 	int			state;	// iteration state
250 };
251 
252 // directory iteration states
253 enum {
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 
260 struct attr_cookie : SinglyLinkedListLinkImpl<attr_cookie> {
261 	Attribute*	current;
262 };
263 
264 struct file_cookie {
265 	int			open_mode;
266 	off_t		buffer_offset;
267 	void*		buffer;
268 };
269 
270 static const uint32 kMaxAttributeSize = 65536;
271 static const uint32 kMaxAttributes = 64;
272 
273 static const char* kProtectedAttrNamespace = "CD:";
274 
275 static const char* kCddbIdAttribute = "CD:cddbid";
276 static const char* kDoLookupAttribute = "CD:do_lookup";
277 static const char* kTocAttribute = "CD:toc";
278 
279 extern fs_volume_ops gCDDAVolumeOps;
280 extern 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 */
290 static bool
is_special_attribute(const char * name,attr_mode attrMode)291 is_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 
324 static void
write_line(int fd,const char * line)325 write_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 
336 static void
write_attributes(int fd,Inode * inode,attr_mode attrMode=kDiscIDAttributes)337 write_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 
387 static bool
read_line(int fd,char * line,size_t length)388 read_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 
413 static bool
read_attributes(int fd,Inode * inode)414 read_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 
460 static int
open_attributes(uint32 cddbID,int deviceFD,int mode,enum attr_mode attrMode)461 open_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 
508 static void
fill_stat_buffer(Volume * volume,Inode * inode,Attribute * attribute,struct stat & stat)509 fill_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 
542 bool
is_data_track(const scsi_toc_track & track)543 is_data_track(const scsi_toc_track& track)
544 {
545 	return (track.control & 4) != 0;
546 }
547 
548 
549 uint32
count_audio_tracks(scsi_toc_toc * toc)550 count_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 
Volume(fs_volume * fsVolume)566 Volume::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 
~Volume()581 Volume::~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 
610 status_t
InitCheck()611 Volume::InitCheck()
612 {
613 	return B_OK;
614 }
615 
616 
617 status_t
Mount(const char * device)618 Volume::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 
759 status_t
SetName(const char * name)760 Volume::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 
775 mutex&
Lock()776 Volume::Lock()
777 {
778 	return fLock;
779 }
780 
781 
782 Inode*
Find(ino_t id)783 Volume::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 
794 Inode*
Find(const char * name)795 Volume::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 
810 void
SetCDDBLookupsEnabled(bool doLookup)811 Volume::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
DetermineName(uint32 cddbID,int device,char * name,size_t length)821 Volume::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 
851 Inode*
_CreateNode(Inode * parent,const char * name,uint64 start,uint64 frames,int32 type)852 Volume::_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 */
888 int
_OpenAttributes(int mode,enum attr_mode attrMode)889 Volume::_OpenAttributes(int mode, enum attr_mode attrMode)
890 {
891 	return open_attributes(fDiscID, fDevice, mode, attrMode);
892 }
893 
894 
895 /*!	Reads the attributes, if any, that belong to the CD currently being
896 	mounted.
897 */
898 void
_RestoreAttributes()899 Volume::_RestoreAttributes()
900 {
901 	int fd = _OpenAttributes(O_RDONLY);
902 	if (fd < 0)
903 		return;
904 
905 	_RestoreAttributes(fd);
906 
907 	close(fd);
908 }
909 
910 
911 void
_RestoreAttributes(int fd)912 Volume::_RestoreAttributes(int fd)
913 {
914 	char line[B_FILE_NAME_LENGTH];
915 	if (!read_line(fd, line, B_FILE_NAME_LENGTH))
916 		return;
917 
918 	SetName(line);
919 
920 	for (Inode* inode = fFirstEntry; inode != NULL; inode = inode->Next()) {
921 		if (!read_line(fd, line, B_FILE_NAME_LENGTH))
922 			break;
923 
924 		inode->SetName(line);
925 	}
926 
927 	if (read_attributes(fd, fRootNode)) {
928 		for (Inode* inode = fFirstEntry; inode != NULL; inode = inode->Next()) {
929 			if (!read_attributes(fd, inode))
930 				break;
931 		}
932 	}
933 }
934 
935 
936 void
_StoreAttributes()937 Volume::_StoreAttributes()
938 {
939 	int fd = _OpenAttributes(O_WRONLY);
940 	if (fd < 0)
941 		return;
942 
943 	write_line(fd, Name());
944 
945 	for (Inode* inode = fFirstEntry; inode != NULL; inode = inode->Next()) {
946 		write_line(fd, inode->Name());
947 	}
948 
949 	write_attributes(fd, fRootNode);
950 
951 	for (Inode* inode = fFirstEntry; inode != NULL; inode = inode->Next()) {
952 		write_attributes(fd, inode);
953 	}
954 
955 	close(fd);
956 }
957 
958 
959 /*!	Restores the attributes, if any, that are shared between CDs; some are
960 	stored per device, others are stored for all CDs no matter which device.
961 */
962 void
_RestoreSharedAttributes()963 Volume::_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 
985 void
_StoreSharedAttributes()986 Volume::_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 
Attribute(const char * name,type_code type)1007 Attribute::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 
~Attribute()1018 Attribute::~Attribute()
1019 {
1020 	free(fName);
1021 	free(fData);
1022 }
1023 
1024 
1025 status_t
SetTo(const char * name,type_code type)1026 Attribute::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 
1043 status_t
ReadAt(off_t offset,uint8 * buffer,size_t * _length)1044 Attribute::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 */
1068 status_t
WriteAt(off_t offset,const uint8 * buffer,size_t * _length)1069 Attribute::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.
1109 void
Truncate()1110 Attribute::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 */
1121 status_t
SetSize(off_t size)1122 Attribute::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 
1140 bool
IsProtectedNamespace()1141 Attribute::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 
1150 bool
IsProtectedNamespace(const char * name)1151 Attribute::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 
Inode(Volume * volume,Inode * parent,const char * name,uint64 start,uint64 frames,int32 type)1163 Inode::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 
~Inode()1212 Inode::~Inode()
1213 {
1214 	free(const_cast<char*>(fName));
1215 }
1216 
1217 
1218 status_t
InitCheck()1219 Inode::InitCheck()
1220 {
1221 	if (fName == NULL)
1222 		return B_NO_MEMORY;
1223 
1224 	return B_OK;
1225 }
1226 
1227 
1228 status_t
SetName(const char * name)1229 Inode::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 
1246 Attribute*
FindAttribute(const char * name) const1247 Inode::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 
1264 status_t
AddAttribute(Attribute * attribute,bool overwrite)1265 Inode::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 
1281 status_t
AddAttribute(const char * name,type_code type,bool overwrite,const uint8 * data,size_t length)1282 Inode::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 
1303 status_t
AddAttribute(const char * name,type_code type,const char * string)1304 Inode::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 
1314 status_t
AddAttribute(const char * name,type_code type,uint32 value)1315 Inode::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 
1322 status_t
AddAttribute(const char * name,type_code type,uint64 value)1323 Inode::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 
1330 status_t
RemoveAttribute(const char * name,bool checkNamespace)1331 Inode::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::ConstIterator 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 
1364 void
AddAttrCookie(attr_cookie * cookie)1365 Inode::AddAttrCookie(attr_cookie* cookie)
1366 {
1367 	fAttrCookies.Add(cookie);
1368 	RewindAttrCookie(cookie);
1369 }
1370 
1371 
1372 void
RemoveAttrCookie(attr_cookie * cookie)1373 Inode::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 
1380 void
RewindAttrCookie(attr_cookie * cookie)1381 Inode::RewindAttrCookie(attr_cookie* cookie)
1382 {
1383 	cookie->current = fAttributes.First();
1384 }
1385 
1386 
1387 //	#pragma mark - Module API
1388 
1389 
1390 static float
cdda_identify_partition(int fd,partition_data * partition,void ** _cookie)1391 cdda_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 
1443 static status_t
cdda_scan_partition(int fd,partition_data * partition,void * _cookie)1444 cdda_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 
1473 static void
cdda_free_identify_partition_cookie(partition_data * partition,void * _cookie)1474 cdda_free_identify_partition_cookie(partition_data* partition, void* _cookie)
1475 {
1476 	free(_cookie);
1477 }
1478 
1479 
1480 static status_t
cdda_mount(fs_volume * fsVolume,const char * device,uint32 flags,const char * args,ino_t * _rootVnodeID)1481 cdda_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 
1507 static status_t
cdda_unmount(fs_volume * _volume)1508 cdda_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 
1519 static status_t
cdda_read_fs_stat(fs_volume * _volume,struct fs_info * info)1520 cdda_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 
1544 static status_t
cdda_write_fs_stat(fs_volume * _volume,const struct fs_info * info,uint32 mask)1545 cdda_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 
1560 static status_t
cdda_sync(fs_volume * _volume)1561 cdda_sync(fs_volume* _volume)
1562 {
1563 	TRACE(("cdda_sync: entry\n"));
1564 
1565 	return B_OK;
1566 }
1567 
1568 
1569 static status_t
cdda_lookup(fs_volume * _volume,fs_vnode * _dir,const char * name,ino_t * _id)1570 cdda_lookup(fs_volume* _volume, fs_vnode* _dir, const char* name, ino_t* _id)
1571 {
1572 	Volume* volume = (Volume*)_volume->private_volume;
1573 	status_t status;
1574 
1575 	TRACE(("cdda_lookup: entry dir %p, name '%s'\n", _dir, name));
1576 
1577 	Inode* directory = (Inode*)_dir->private_node;
1578 	if (!S_ISDIR(directory->Type()))
1579 		return B_NOT_A_DIRECTORY;
1580 
1581 	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 
1596 static status_t
cdda_get_vnode_name(fs_volume * _volume,fs_vnode * _node,char * buffer,size_t bufferSize)1597 cdda_get_vnode_name(fs_volume* _volume, fs_vnode* _node, char* buffer,
1598 	size_t bufferSize)
1599 {
1600 	Volume* volume = (Volume*)_volume->private_volume;
1601 	Inode* inode = (Inode*)_node->private_node;
1602 
1603 	TRACE(("cdda_get_vnode_name(): inode = %p\n", inode));
1604 
1605 	MutexLocker _(volume->Lock());
1606 	strlcpy(buffer, inode->Name(), bufferSize);
1607 	return B_OK;
1608 }
1609 
1610 
1611 static status_t
cdda_get_vnode(fs_volume * _volume,ino_t id,fs_vnode * _node,int * _type,uint32 * _flags,bool reenter)1612 cdda_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _node, int* _type,
1613 	uint32* _flags, bool reenter)
1614 {
1615 	Volume* volume = (Volume*)_volume->private_volume;
1616 	Inode* inode;
1617 
1618 	TRACE(("cdda_getvnode: asking for vnode 0x%Lx, r %d\n", id, reenter));
1619 
1620 	inode = volume->Find(id);
1621 	if (inode == NULL)
1622 		return B_ENTRY_NOT_FOUND;
1623 
1624 	_node->private_node = inode;
1625 	_node->ops = &gCDDAVnodeOps;
1626 	*_type = inode->Type();
1627 	*_flags = 0;
1628 	return B_OK;
1629 }
1630 
1631 
1632 static status_t
cdda_put_vnode(fs_volume * _volume,fs_vnode * _node,bool reenter)1633 cdda_put_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
1634 {
1635 	return B_OK;
1636 }
1637 
1638 
1639 static status_t
cdda_open(fs_volume * _volume,fs_vnode * _node,int openMode,void ** _cookie)1640 cdda_open(fs_volume* _volume, fs_vnode* _node, int openMode, void** _cookie)
1641 {
1642 	TRACE(("cdda_open(): node = %p, openMode = %d\n", _node, openMode));
1643 
1644 	file_cookie* cookie = (file_cookie*)malloc(sizeof(file_cookie));
1645 	if (cookie == NULL)
1646 		return B_NO_MEMORY;
1647 
1648 	TRACE(("  open cookie = %p\n", cookie));
1649 	cookie->open_mode = openMode;
1650 	cookie->buffer = NULL;
1651 
1652 	*_cookie = (void*)cookie;
1653 
1654 	return B_OK;
1655 }
1656 
1657 
1658 static status_t
cdda_close(fs_volume * _volume,fs_vnode * _node,void * _cookie)1659 cdda_close(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1660 {
1661 	return B_OK;
1662 }
1663 
1664 
1665 static status_t
cdda_free_cookie(fs_volume * _volume,fs_vnode * _node,void * _cookie)1666 cdda_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1667 {
1668 	file_cookie* cookie = (file_cookie*)_cookie;
1669 
1670 	TRACE(("cdda_freecookie: entry vnode %p, cookie %p\n", _node, _cookie));
1671 
1672 	free(cookie);
1673 	return B_OK;
1674 }
1675 
1676 
1677 static status_t
cdda_fsync(fs_volume * _volume,fs_vnode * _node)1678 cdda_fsync(fs_volume* _volume, fs_vnode* _node)
1679 {
1680 	return B_OK;
1681 }
1682 
1683 
1684 static status_t
cdda_read(fs_volume * _volume,fs_vnode * _node,void * _cookie,off_t offset,void * buffer,size_t * _length)1685 cdda_read(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t offset,
1686 	void* buffer, size_t* _length)
1687 {
1688 	file_cookie* cookie = (file_cookie*)_cookie;
1689 	Volume* volume = (Volume*)_volume->private_volume;
1690 	Inode* inode = (Inode*)_node->private_node;
1691 
1692 	TRACE(("cdda_read(vnode = %p, offset %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 
1755 static bool
cdda_can_page(fs_volume * _volume,fs_vnode * _node,void * cookie)1756 cdda_can_page(fs_volume* _volume, fs_vnode* _node, void* cookie)
1757 {
1758 	return false;
1759 }
1760 
1761 
1762 static status_t
cdda_read_pages(fs_volume * _volume,fs_vnode * _node,void * cookie,off_t pos,const iovec * vecs,size_t count,size_t * _numBytes)1763 cdda_read_pages(fs_volume* _volume, fs_vnode* _node, void* cookie, off_t pos,
1764 	const iovec* vecs, size_t count, size_t* _numBytes)
1765 {
1766 	return B_NOT_ALLOWED;
1767 }
1768 
1769 
1770 static status_t
cdda_write_pages(fs_volume * _volume,fs_vnode * _node,void * cookie,off_t pos,const iovec * vecs,size_t count,size_t * _numBytes)1771 cdda_write_pages(fs_volume* _volume, fs_vnode* _node, void* cookie, off_t pos,
1772 	const iovec* vecs, size_t count, size_t* _numBytes)
1773 {
1774 	return B_NOT_ALLOWED;
1775 }
1776 
1777 
1778 static status_t
cdda_read_stat(fs_volume * _volume,fs_vnode * _node,struct stat * stat)1779 cdda_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat)
1780 {
1781 	Volume* volume = (Volume*)_volume->private_volume;
1782 	Inode* inode = (Inode*)_node->private_node;
1783 
1784 	TRACE(("cdda_read_stat: vnode %p (0x%Lx), stat %p\n", inode, inode->ID(),
1785 		stat));
1786 
1787 	fill_stat_buffer(volume, inode, NULL, *stat);
1788 
1789 	return B_OK;
1790 }
1791 
1792 
1793 status_t
cdda_rename(fs_volume * _volume,fs_vnode * _oldDir,const char * oldName,fs_vnode * _newDir,const char * newName)1794 cdda_rename(fs_volume* _volume, fs_vnode* _oldDir, const char* oldName,
1795 	fs_vnode* _newDir, const char* newName)
1796 {
1797 	if (_oldDir->private_node != _newDir->private_node)
1798 		return B_BAD_VALUE;
1799 
1800 	// we only have a single directory which simplifies things a bit :-)
1801 
1802 	Volume *volume = (Volume*)_volume->private_volume;
1803 	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 
1831 static status_t
cdda_open_dir(fs_volume * _volume,fs_vnode * _node,void ** _cookie)1832 cdda_open_dir(fs_volume* _volume, fs_vnode* _node, void** _cookie)
1833 {
1834 	Volume* volume = (Volume*)_volume->private_volume;
1835 
1836 	TRACE(("cdda_open_dir(): vnode = %p\n", _node));
1837 
1838 	Inode* inode = (Inode*)_node->private_node;
1839 	if (!S_ISDIR(inode->Type()))
1840 		return B_NOT_A_DIRECTORY;
1841 
1842 	if (inode != &volume->RootNode())
1843 		panic("pipefs: found directory that's not the root!");
1844 
1845 	dir_cookie* cookie = (dir_cookie*)malloc(sizeof(dir_cookie));
1846 	if (cookie == NULL)
1847 		return B_NO_MEMORY;
1848 
1849 	cookie->current = volume->FirstEntry();
1850 	cookie->state = ITERATION_STATE_BEGIN;
1851 
1852 	*_cookie = (void*)cookie;
1853 	return B_OK;
1854 }
1855 
1856 
1857 static status_t
cdda_read_dir(fs_volume * _volume,fs_vnode * _node,void * _cookie,struct dirent * buffer,size_t bufferSize,uint32 * _num)1858 cdda_read_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie,
1859 	struct dirent* buffer, size_t bufferSize, uint32* _num)
1860 {
1861 	Volume* volume = (Volume*)_volume->private_volume;
1862 	Inode* inode = (Inode*)_node->private_node;
1863 
1864 	TRACE(("cdda_read_dir: vnode %p, cookie %p, buffer = %p, bufferSize = %ld,"
1865 		" num = %p\n", _node, _cookie, buffer, bufferSize,_num));
1866 
1867 	if ((Inode*)_node->private_node != &volume->RootNode())
1868 		return B_BAD_VALUE;
1869 
1870 	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 
1934 static status_t
cdda_rewind_dir(fs_volume * _volume,fs_vnode * _node,void * _cookie)1935 cdda_rewind_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1936 {
1937 	Volume* volume = (Volume*)_volume->private_volume;
1938 
1939 	dir_cookie* cookie = (dir_cookie*)_cookie;
1940 	cookie->current = volume->FirstEntry();
1941 	cookie->state = ITERATION_STATE_BEGIN;
1942 
1943 	return B_OK;
1944 }
1945 
1946 
1947 static status_t
cdda_close_dir(fs_volume * _volume,fs_vnode * _node,void * _cookie)1948 cdda_close_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1949 {
1950 	TRACE(("cdda_close: entry vnode %p, cookie %p\n", _node, _cookie));
1951 
1952 	return 0;
1953 }
1954 
1955 
1956 static status_t
cdda_free_dir_cookie(fs_volume * _volume,fs_vnode * _node,void * _cookie)1957 cdda_free_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1958 {
1959 	dir_cookie* cookie = (dir_cookie*)_cookie;
1960 
1961 	TRACE(("cdda_freecookie: entry vnode %p, cookie %p\n", _node, cookie));
1962 
1963 	free(cookie);
1964 	return 0;
1965 }
1966 
1967 
1968 //	#pragma mark - attribute functions
1969 
1970 
1971 static status_t
cdda_open_attr_dir(fs_volume * _volume,fs_vnode * _node,void ** _cookie)1972 cdda_open_attr_dir(fs_volume* _volume, fs_vnode* _node, void** _cookie)
1973 {
1974 	Volume* volume = (Volume*)_volume->private_volume;
1975 	Inode* inode = (Inode*)_node->private_node;
1976 
1977 	attr_cookie* cookie = new(std::nothrow) attr_cookie;
1978 	if (cookie == NULL)
1979 		return B_NO_MEMORY;
1980 
1981 	MutexLocker _(volume->Lock());
1982 
1983 	inode->AddAttrCookie(cookie);
1984 	*_cookie = cookie;
1985 	return B_OK;
1986 }
1987 
1988 
1989 static status_t
cdda_close_attr_dir(fs_volume * _volume,fs_vnode * _node,void * _cookie)1990 cdda_close_attr_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1991 {
1992 	return B_OK;
1993 }
1994 
1995 
1996 static status_t
cdda_free_attr_dir_cookie(fs_volume * _volume,fs_vnode * _node,void * _cookie)1997 cdda_free_attr_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1998 {
1999 	Volume* volume = (Volume*)_volume->private_volume;
2000 	Inode* inode = (Inode*)_node->private_node;
2001 	attr_cookie* cookie = (attr_cookie*)_cookie;
2002 
2003 	MutexLocker _(volume->Lock());
2004 
2005 	inode->RemoveAttrCookie(cookie);
2006 	delete cookie;
2007 	return B_OK;
2008 }
2009 
2010 
2011 static status_t
cdda_rewind_attr_dir(fs_volume * _volume,fs_vnode * _node,void * _cookie)2012 cdda_rewind_attr_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie)
2013 {
2014 	Volume* volume = (Volume*)_volume->private_volume;
2015 	Inode* inode = (Inode*)_node->private_node;
2016 	attr_cookie* cookie = (attr_cookie*)_cookie;
2017 
2018 	MutexLocker _(volume->Lock());
2019 
2020 	inode->RewindAttrCookie(cookie);
2021 	return B_OK;
2022 }
2023 
2024 
2025 static status_t
cdda_read_attr_dir(fs_volume * _volume,fs_vnode * _node,void * _cookie,struct dirent * dirent,size_t bufferSize,uint32 * _num)2026 cdda_read_attr_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie,
2027 	struct dirent* dirent, size_t bufferSize, uint32* _num)
2028 {
2029 	Volume* volume = (Volume*)_volume->private_volume;
2030 	Inode* inode = (Inode*)_node->private_node;
2031 	attr_cookie* cookie = (attr_cookie*)_cookie;
2032 
2033 	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 
2052 static status_t
cdda_create_attr(fs_volume * _volume,fs_vnode * _node,const char * name,uint32 type,int openMode,void ** _cookie)2053 cdda_create_attr(fs_volume* _volume, fs_vnode* _node, const char* name,
2054 	uint32 type, int openMode, void** _cookie)
2055 {
2056 	Volume *volume = (Volume*)_volume->private_volume;
2057 	Inode *inode = (Inode*)_node->private_node;
2058 
2059 	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 
2088 static status_t
cdda_open_attr(fs_volume * _volume,fs_vnode * _node,const char * name,int openMode,void ** _cookie)2089 cdda_open_attr(fs_volume* _volume, fs_vnode* _node, const char* name,
2090 	int openMode, void** _cookie)
2091 {
2092 	Volume* volume = (Volume*)_volume->private_volume;
2093 	Inode* inode = (Inode*)_node->private_node;
2094 
2095 	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 
2109 static status_t
cdda_close_attr(fs_volume * _volume,fs_vnode * _node,void * cookie)2110 cdda_close_attr(fs_volume* _volume, fs_vnode* _node, void* cookie)
2111 {
2112 	return B_OK;
2113 }
2114 
2115 
2116 static status_t
cdda_free_attr_cookie(fs_volume * _volume,fs_vnode * _node,void * cookie)2117 cdda_free_attr_cookie(fs_volume* _volume, fs_vnode* _node, void* cookie)
2118 {
2119 	free(cookie);
2120 	return B_OK;
2121 }
2122 
2123 
2124 static status_t
cdda_read_attr(fs_volume * _volume,fs_vnode * _node,void * _cookie,off_t offset,void * buffer,size_t * _length)2125 cdda_read_attr(fs_volume* _volume, fs_vnode* _node, void* _cookie,
2126 	off_t offset, void* buffer, size_t* _length)
2127 {
2128 	Volume* volume = (Volume*)_volume->private_volume;
2129 	Inode* inode = (Inode*)_node->private_node;
2130 
2131 	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 
2141 static status_t
cdda_write_attr(fs_volume * _volume,fs_vnode * _node,void * _cookie,off_t offset,const void * buffer,size_t * _length)2142 cdda_write_attr(fs_volume* _volume, fs_vnode* _node, void* _cookie,
2143 	off_t offset, const void* buffer, size_t* _length)
2144 {
2145 	Volume* volume = (Volume*)_volume->private_volume;
2146 	Inode* inode = (Inode*)_node->private_node;
2147 
2148 	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 
2166 static status_t
cdda_read_attr_stat(fs_volume * _volume,fs_vnode * _node,void * _cookie,struct stat * stat)2167 cdda_read_attr_stat(fs_volume* _volume, fs_vnode* _node, void* _cookie,
2168 	struct stat* stat)
2169 {
2170 	Volume* volume = (Volume*)_volume->private_volume;
2171 	Inode* inode = (Inode*)_node->private_node;
2172 
2173 	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 
2184 static status_t
cdda_write_attr_stat(fs_volume * _volume,fs_vnode * _node,void * cookie,const struct stat * stat,int statMask)2185 cdda_write_attr_stat(fs_volume* _volume, fs_vnode* _node, void* cookie,
2186 	const struct stat* stat, int statMask)
2187 {
2188 	return EOPNOTSUPP;
2189 }
2190 
2191 
2192 static status_t
cdda_remove_attr(fs_volume * _volume,fs_vnode * _node,const char * name)2193 cdda_remove_attr(fs_volume* _volume, fs_vnode* _node, const char* name)
2194 {
2195 	if (name == NULL)
2196 		return B_BAD_VALUE;
2197 
2198 	Volume* volume = (Volume*)_volume->private_volume;
2199 	Inode* inode = (Inode*)_node->private_node;
2200 
2201 	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 
2213 fs_volume_ops gCDDAVolumeOps = {
2214 	cdda_unmount,
2215 	cdda_read_fs_stat,
2216 	cdda_write_fs_stat,
2217 	cdda_sync,
2218 	cdda_get_vnode,
2219 
2220 	// the other operations are not yet supported (indices, queries)
2221 	NULL,
2222 };
2223 
2224 fs_vnode_ops gCDDAVnodeOps = {
2225 	cdda_lookup,
2226 	cdda_get_vnode_name,
2227 	cdda_put_vnode,
2228 	NULL,	// fs_remove_vnode()
2229 
2230 	cdda_can_page,
2231 	cdda_read_pages,
2232 	cdda_write_pages,
2233 
2234 	NULL,	// io()
2235 	NULL,	// cancel_io()
2236 
2237 	NULL,	// get_file_map()
2238 
2239 	// common
2240 	NULL,	// fs_ioctl()
2241 	NULL,	// fs_set_flags()
2242 	NULL,	// fs_select()
2243 	NULL,	// fs_deselect()
2244 	cdda_fsync,
2245 
2246 	NULL,	// fs_read_link()
2247 	NULL,	// fs_symlink()
2248 	NULL,	// fs_link()
2249 	NULL,	// fs_unlink()
2250 	cdda_rename,
2251 
2252 	NULL,	// fs_access()
2253 	cdda_read_stat,
2254 	NULL,	// fs_write_stat()
2255 	NULL,	// fs_preallocate()
2256 
2257 	// file
2258 	NULL,	// fs_create()
2259 	cdda_open,
2260 	cdda_close,
2261 	cdda_free_cookie,
2262 	cdda_read,
2263 	NULL,	// fs_write()
2264 
2265 	// directory
2266 	NULL,	// fs_create_dir()
2267 	NULL,	// fs_remove_dir()
2268 	cdda_open_dir,
2269 	cdda_close_dir,
2270 	cdda_free_dir_cookie,
2271 	cdda_read_dir,
2272 	cdda_rewind_dir,
2273 
2274 	// attribute directory operations
2275 	cdda_open_attr_dir,
2276 	cdda_close_attr_dir,
2277 	cdda_free_attr_dir_cookie,
2278 	cdda_read_attr_dir,
2279 	cdda_rewind_attr_dir,
2280 
2281 	// attribute operations
2282 	cdda_create_attr,
2283 	cdda_open_attr,
2284 	cdda_close_attr,
2285 	cdda_free_attr_cookie,
2286 	cdda_read_attr,
2287 	cdda_write_attr,
2288 
2289 	cdda_read_attr_stat,
2290 	cdda_write_attr_stat,
2291 	NULL,	// fs_rename_attr()
2292 	cdda_remove_attr,
2293 
2294 	NULL,	// fs_create_special_node()
2295 };
2296 
2297 static file_system_module_info sCDDAFileSystem = {
2298 	{
2299 		"file_systems/cdda" B_CURRENT_FS_API_VERSION,
2300 		0,
2301 		NULL,
2302 	},
2303 
2304 	"cdda",					// short_name
2305 	"CDDA File System",		// pretty_name
2306 	0,	// DDM flags
2307 
2308 	cdda_identify_partition,
2309 	cdda_scan_partition,
2310 	cdda_free_identify_partition_cookie,
2311 	NULL,	// free_partition_content_cookie()
2312 
2313 	cdda_mount,
2314 
2315 	// all other functions are not supported
2316 	NULL,
2317 };
2318 
2319 module_info* modules[] = {
2320 	(module_info*)&sCDDAFileSystem,
2321 	NULL,
2322 };
2323