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