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