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