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