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