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