1 //---------------------------------------------------------------------- 2 // This software is part of the OpenBeOS distribution and is covered 3 // by the OpenBeOS license. 4 // 5 // Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net 6 //--------------------------------------------------------------------- 7 #ifndef _UDF_ICB_H 8 #define _UDF_ICB_H 9 10 /*! \file Icb.h 11 */ 12 13 #ifndef _IMPEXP_KERNEL 14 # define _IMPEXP_KERNEL 15 #endif 16 #ifdef COMPILE_FOR_R5 17 extern "C" { 18 #endif 19 #include "fsproto.h" 20 #ifdef COMPILE_FOR_R5 21 } 22 #endif 23 24 #include "kernel_cpp.h" 25 #include "UdfDebug.h" 26 27 #include "CachedBlock.h" 28 #include "UdfStructures.h" 29 #include "SinglyLinkedList.h" 30 31 namespace Udf { 32 33 class DirectoryIterator; 34 class Volume; 35 36 /*! \brief Abstract interface to file entry structure members 37 that are not commonly accessible through file_icb_entry(). 38 39 This is necessary, since we can't use virtual functions in 40 the disk structure structs themselves, since we generally 41 don't create disk structure objects by calling new, but 42 rather just cast a chunk of memory read off disk to be 43 a pointer to the struct of interest (which works fine 44 for regular functions, but fails miserably for virtuals 45 due to the vtable not being setup properly). 46 */ 47 class AbstractFileEntry { 48 public: 49 virtual uint8* AllocationDescriptors() = 0; 50 virtual uint32 AllocationDescriptorsLength() = 0; 51 }; 52 53 template <class Descriptor> 54 class FileEntry : public AbstractFileEntry { 55 public: 56 FileEntry(CachedBlock *descriptorBlock = NULL); 57 void SetTo(CachedBlock *descriptorBlock); 58 virtual uint8* AllocationDescriptors(); 59 virtual uint32 AllocationDescriptorsLength(); 60 private: 61 Descriptor* _Descriptor(); 62 63 CachedBlock *fDescriptorBlock; 64 }; 65 66 class Icb { 67 public: 68 Icb(Volume *volume, long_address address); 69 status_t InitCheck(); 70 vnode_id Id() { return fId; } 71 72 // categorization 73 uint8 Type() { return IcbTag().file_type(); } 74 bool IsFile() { return InitCheck() == B_OK && Type() == ICB_TYPE_REGULAR_FILE; } 75 bool IsDirectory() { return InitCheck() == B_OK 76 && (Type() == ICB_TYPE_DIRECTORY || Type() == ICB_TYPE_STREAM_DIRECTORY); } 77 78 uint32 Uid() { return 0; }//FileEntry()->uid(); } 79 uint32 Gid() { return 0; } 80 uint16 FileLinkCount() { return FileEntry()->file_link_count(); } 81 uint64 Length() { return FileEntry()->information_length(); } 82 mode_t Mode() { return (IsDirectory() ? S_IFDIR : S_IFREG) | S_IRUSR | S_IRGRP | S_IROTH; } 83 time_t AccessTime(); 84 time_t ModificationTime(); 85 86 uint8 *AllocationDescriptors() { return AbstractEntry()->AllocationDescriptors(); } 87 uint32 AllocationDescriptorsSize() { return AbstractEntry()->AllocationDescriptorsLength(); } 88 89 status_t Read(off_t pos, void *buffer, size_t *length, uint32 *block = NULL); 90 91 // for directories only 92 status_t GetDirectoryIterator(DirectoryIterator **iterator); 93 status_t Find(const char *filename, vnode_id *id); 94 95 Volume* GetVolume() const { return fVolume; } 96 97 private: 98 Icb(); // unimplemented 99 100 descriptor_tag & Tag() { return (reinterpret_cast<icb_header*>(fData.Block()))->tag(); } 101 icb_entry_tag& IcbTag() { return (reinterpret_cast<icb_header*>(fData.Block()))->icb_tag(); } 102 AbstractFileEntry* AbstractEntry() { 103 DEBUG_INIT("Icb"); 104 return (Tag().id() == TAGID_EXTENDED_FILE_ENTRY) 105 // ? reinterpret_cast<extended_file_icb_entry*>(fData.Block()) 106 // : reinterpret_cast<file_icb_entry*>(fData.Block())); 107 ? &fExtendedEntry 108 : &fFileEntry; 109 } 110 file_icb_entry* FileEntry() { return (reinterpret_cast<file_icb_entry*>(fData.Block())); } 111 extended_file_icb_entry& ExtendedEntry() { return *(reinterpret_cast<extended_file_icb_entry*>(fData.Block())); } 112 113 template <class DescriptorList> 114 status_t _Read(DescriptorList &list, off_t pos, void *buffer, size_t *length, uint32 *block); 115 116 117 private: 118 Volume *fVolume; 119 CachedBlock fData; 120 status_t fInitStatus; 121 vnode_id fId; 122 SinglyLinkedList<DirectoryIterator*> fIteratorList; 123 /* [zooey]: gcc-2.95.3 requires the explicit namespace here, otherwise 124 it complains about a syntax error(!). This is most probably a bug. */ 125 Udf::FileEntry<file_icb_entry> fFileEntry; 126 Udf::FileEntry<extended_file_icb_entry> fExtendedEntry; 127 }; 128 129 /*! \brief Does the dirty work of reading using the given DescriptorList object 130 to access the allocation descriptors properly. 131 */ 132 template <class DescriptorList> 133 status_t 134 Icb::_Read(DescriptorList &list, off_t pos, void *_buffer, size_t *length, uint32 *block) 135 { 136 DEBUG_INIT_ETC("Icb", ("list: %p, pos: %Ld, buffer: %p, length: (%p)->%ld", 137 &list, pos, _buffer, length, (length ? *length : 0))); 138 if (!_buffer || !length) 139 RETURN(B_BAD_VALUE); 140 141 uint64 bytesLeftInFile = uint64(pos) > Length() ? 0 : Length() - pos; 142 size_t bytesLeft = (*length >= bytesLeftInFile) ? bytesLeftInFile : *length; 143 size_t bytesRead = 0; 144 145 Volume *volume = GetVolume(); 146 status_t error = B_OK; 147 uint8 *buffer = reinterpret_cast<uint8*>(_buffer); 148 bool isFirstBlock = true; 149 150 while (bytesLeft > 0 && !error) { 151 152 PRINT(("pos: %Ld\n", pos)); 153 PRINT(("bytesLeft: %ld\n", bytesLeft)); 154 155 long_address extent; 156 bool isEmpty = false; 157 error = list.FindExtent(pos, &extent, &isEmpty); 158 if (!error) { 159 PRINT(("found extent for offset %Ld: (block: %ld, partition: %d, length: %ld, type: %d)\n", 160 pos, extent.block(), extent.partition(), extent.length(), extent.type())); 161 162 switch (extent.type()) { 163 case EXTENT_TYPE_RECORDED: 164 isEmpty = false; 165 break; 166 167 case EXTENT_TYPE_ALLOCATED: 168 case EXTENT_TYPE_UNALLOCATED: 169 isEmpty = true; 170 break; 171 172 default: 173 PRINT(("Invalid extent type found: %d\n", extent.type())); 174 error = B_ERROR; 175 break; 176 } 177 178 if (!error) { 179 // Note the unmapped first block of the total read in 180 // the block output parameter if provided 181 if (isFirstBlock) { 182 isFirstBlock = false; 183 if (block) 184 *block = extent.block(); 185 } 186 187 off_t blockOffset = pos - off_t((pos >> volume->BlockShift()) << volume->BlockShift()); 188 size_t fullBlocksLeft = bytesLeft >> volume->BlockShift(); 189 190 if (fullBlocksLeft > 0 && blockOffset == 0) { 191 PRINT(("reading full block (or more)\n")); 192 // Block aligned and at least one full block left. Read in using 193 // cached_read() calls. 194 off_t diskBlock; 195 error = volume->MapBlock(extent, &diskBlock); 196 if (!error) { 197 size_t fullBlockBytesLeft = fullBlocksLeft << volume->BlockShift(); 198 size_t readLength = fullBlockBytesLeft < extent.length() 199 ? fullBlockBytesLeft 200 : extent.length(); 201 202 if (isEmpty) { 203 PRINT(("reading %ld empty bytes as zeros\n", readLength)); 204 memset(buffer, 0, readLength); 205 } else { 206 off_t diskBlock; 207 error = volume->MapBlock(extent, &diskBlock); 208 if (!error) { 209 PRINT(("reading %ld bytes from disk block %Ld using cached_read()\n", 210 readLength, diskBlock)); 211 error = cached_read(volume->Device(), diskBlock, buffer, 212 readLength >> volume->BlockShift(), 213 volume->BlockSize()); 214 } 215 } 216 217 if (!error) { 218 bytesLeft -= readLength; 219 bytesRead += readLength; 220 pos += readLength; 221 buffer += readLength; 222 } 223 } 224 225 } else { 226 PRINT(("partial block\n")); 227 off_t partialOffset; 228 size_t partialLength; 229 if (blockOffset == 0) { 230 // Block aligned, but only a partial block's worth remaining. Read 231 // in remaining bytes of file 232 partialOffset = 0; 233 partialLength = bytesLeft; 234 } else { 235 // Not block aligned, so just read up to the next block boundary. 236 partialOffset = blockOffset; 237 partialLength = volume->BlockSize() - blockOffset; 238 if (bytesLeft < partialLength) 239 partialLength = bytesLeft; 240 } 241 242 PRINT(("partialOffset: %Ld\n", partialOffset)); 243 PRINT(("partialLength: %ld\n", partialLength)); 244 245 if (isEmpty) { 246 PRINT(("reading %ld empty bytes as zeros\n", partialLength)); 247 memset(buffer, 0, partialLength); 248 } else { 249 off_t diskBlock; 250 error = volume->MapBlock(extent, &diskBlock); 251 if (!error) { 252 PRINT(("reading %ld bytes from disk block %Ld using get_block()\n", 253 partialLength, diskBlock)); 254 uint8 *data = (uint8*)get_block(volume->Device(), diskBlock, volume->BlockSize()); 255 error = data ? B_OK : B_BAD_DATA; 256 if (!error) { 257 memcpy(buffer, data+partialOffset, partialLength); 258 release_block(volume->Device(), diskBlock); 259 } 260 } 261 } 262 263 if (!error) { 264 bytesLeft -= partialLength; 265 bytesRead += partialLength; 266 pos += partialLength; 267 buffer += partialLength; 268 } 269 } 270 } 271 } else { 272 PRINT(("error finding extent for offset %Ld: 0x%lx, `%s'", pos, 273 error, strerror(error))); 274 break; 275 } 276 } 277 278 *length = bytesRead; 279 280 RETURN(error); 281 } 282 283 template <class Descriptor> 284 FileEntry<Descriptor>::FileEntry(CachedBlock *descriptorBlock) 285 : fDescriptorBlock(descriptorBlock) 286 { 287 } 288 289 template <class Descriptor> 290 void 291 FileEntry<Descriptor>::SetTo(CachedBlock *descriptorBlock) 292 { 293 fDescriptorBlock = descriptorBlock; 294 } 295 296 template <class Descriptor> 297 uint8* 298 FileEntry<Descriptor>::AllocationDescriptors() 299 { 300 Descriptor* descriptor = _Descriptor(); 301 return descriptor ? descriptor->allocation_descriptors() : NULL; 302 } 303 304 template <class Descriptor> 305 uint32 306 FileEntry<Descriptor>::AllocationDescriptorsLength() 307 { 308 Descriptor* descriptor = _Descriptor(); 309 return descriptor ? descriptor->allocation_descriptors_length() : 0; 310 } 311 312 template <class Descriptor> 313 Descriptor* 314 FileEntry<Descriptor>::_Descriptor() 315 { 316 return fDescriptorBlock ? reinterpret_cast<Descriptor*>(fDescriptorBlock->Block()) : NULL; 317 } 318 319 }; // namespace Udf 320 321 #endif // _UDF_ICB_H 322