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