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