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