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