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