1 /* 2 * Copyright 2011, Jérôme Duval, korli@users.berlios.de. 3 * Copyright 2014 Haiku, Inc. All rights reserved. 4 * 5 * Distributed under the terms of the MIT License. 6 * 7 * Authors: 8 * Jérôme Duval, korli@users.berlios.de 9 * John Scipione, jscipione@gmail.com 10 */ 11 12 13 #include "DirectoryIterator.h" 14 15 #include <stdlib.h> 16 17 #include "convertutf.h" 18 19 #include "Inode.h" 20 21 22 //#define TRACE_EXFAT 23 #ifdef TRACE_EXFAT 24 # define TRACE(x...) dprintf("\33[34mexfat:\33[0m " x) 25 #else 26 # define TRACE(x...) ; 27 #endif 28 29 #define ERROR(x...) dprintf("\33[34mexfat:\33[0m " x) 30 31 32 // #pragma mark - DirectoryIterator 33 34 35 DirectoryIterator::DirectoryIterator(Inode* inode) 36 : 37 fOffset(-2), 38 fCluster(inode->StartCluster()), 39 fInode(inode), 40 fBlock(inode->GetVolume()), 41 fCurrent(NULL) 42 { 43 TRACE("DirectoryIterator::DirectoryIterator() %" B_PRIu32 "\n", fCluster); 44 } 45 46 47 DirectoryIterator::~DirectoryIterator() 48 { 49 } 50 51 52 status_t 53 DirectoryIterator::InitCheck() 54 { 55 return B_OK; 56 } 57 58 59 status_t 60 DirectoryIterator::GetNext(char* name, size_t* _nameLength, ino_t* _id, 61 EntryVisitor* visitor) 62 { 63 if (fCluster == EXFAT_CLUSTER_END) 64 return B_ENTRY_NOT_FOUND; 65 66 if (fOffset == -2) { 67 if (*_nameLength < 3) 68 return B_BUFFER_OVERFLOW; 69 70 *_nameLength = 2; 71 strlcpy(name, "..", *_nameLength + 1); 72 if (fInode->ID() == 1) 73 *_id = fInode->ID(); 74 else 75 *_id = fInode->Parent(); 76 77 fOffset = -1; 78 TRACE("DirectoryIterator::GetNext() found \"..\"\n"); 79 80 return B_OK; 81 } else if (fOffset == -1) { 82 if (*_nameLength < 2) 83 return B_BUFFER_OVERFLOW; 84 85 *_nameLength = 1; 86 strlcpy(name, ".", *_nameLength + 1); 87 *_id = fInode->ID(); 88 fOffset = 0; 89 TRACE("DirectoryIterator::GetNext() found \".\"\n"); 90 91 return B_OK; 92 } 93 94 size_t utf16CodeUnitCount = EXFAT_FILENAME_MAX_LENGTH / sizeof(uint16); 95 uint16 utf16Name[utf16CodeUnitCount]; 96 status_t status = _GetNext(utf16Name, &utf16CodeUnitCount, _id, visitor); 97 if (status == B_OK && utf16CodeUnitCount > 0) { 98 ssize_t lengthOrStatus = utf16le_to_utf8(utf16Name, utf16CodeUnitCount, 99 name, *_nameLength); 100 if (lengthOrStatus < 0) { 101 status = (status_t)lengthOrStatus; 102 if (status == B_NAME_TOO_LONG) 103 *_nameLength = strlen(name); 104 } else 105 *_nameLength = (size_t)lengthOrStatus; 106 } 107 108 if (status == B_OK) { 109 TRACE("DirectoryIterator::GetNext() cluster: %" B_PRIu32 " id: " 110 "%" B_PRIdINO " name: \"%s\", length: %zu\n", fInode->Cluster(), 111 *_id, name, *_nameLength); 112 } else if (status != B_ENTRY_NOT_FOUND) { 113 ERROR("DirectoryIterator::GetNext() (%s)\n", strerror(status)); 114 } 115 116 return status; 117 } 118 119 120 status_t 121 DirectoryIterator::Lookup(const char* name, size_t nameLength, ino_t* _id) 122 { 123 if (strcmp(name, ".") == 0) { 124 *_id = fInode->ID(); 125 return B_OK; 126 } else if (strcmp(name, "..") == 0) { 127 if (fInode->ID() == 1) 128 *_id = fInode->ID(); 129 else 130 *_id = fInode->Parent(); 131 132 return B_OK; 133 } 134 135 Rewind(); 136 fOffset = 0; 137 138 size_t utf16CodeUnitCount = EXFAT_FILENAME_MAX_LENGTH / sizeof(uint16); 139 uint16 utf16Name[utf16CodeUnitCount]; 140 while (_GetNext(utf16Name, &utf16CodeUnitCount, _id) == B_OK) { 141 char utf8Name[nameLength + 1]; 142 ssize_t lengthOrStatus = utf16le_to_utf8(utf16Name, utf16CodeUnitCount, 143 utf8Name, sizeof(utf8Name)); 144 if (lengthOrStatus > 0 && (size_t)lengthOrStatus == nameLength 145 && strncmp(utf8Name, name, nameLength) == 0) { 146 TRACE("DirectoryIterator::Lookup() found ID %" B_PRIdINO "\n", 147 *_id); 148 return B_OK; 149 } 150 utf16CodeUnitCount = EXFAT_FILENAME_MAX_LENGTH / sizeof(uint16); 151 } 152 153 TRACE("DirectoryIterator::Lookup() not found %s\n", name); 154 155 return B_ENTRY_NOT_FOUND; 156 } 157 158 159 status_t 160 DirectoryIterator::LookupEntry(EntryVisitor* visitor) 161 { 162 fCluster = fInode->Cluster(); 163 fOffset = fInode->Offset(); 164 165 size_t utf16CodeUnitCount = EXFAT_FILENAME_MAX_LENGTH / sizeof(uint16); 166 uint16 utf16Name[utf16CodeUnitCount]; 167 return _GetNext(utf16Name, &utf16CodeUnitCount, NULL, visitor); 168 } 169 170 171 status_t 172 DirectoryIterator::Rewind() 173 { 174 fOffset = -2; 175 fCluster = fInode->StartCluster(); 176 return B_OK; 177 } 178 179 180 void 181 DirectoryIterator::Iterate(EntryVisitor &visitor) 182 { 183 fOffset = 0; 184 fCluster = fInode->StartCluster(); 185 186 while (_NextEntry() != B_ENTRY_NOT_FOUND) { 187 switch (fCurrent->type) { 188 case EXFAT_ENTRY_TYPE_BITMAP: 189 visitor.VisitBitmap(fCurrent); 190 break; 191 case EXFAT_ENTRY_TYPE_UPPERCASE: 192 visitor.VisitUppercase(fCurrent); 193 break; 194 case EXFAT_ENTRY_TYPE_LABEL: 195 visitor.VisitLabel(fCurrent); 196 break; 197 case EXFAT_ENTRY_TYPE_FILE: 198 visitor.VisitFile(fCurrent); 199 break; 200 case EXFAT_ENTRY_TYPE_FILEINFO: 201 visitor.VisitFileInfo(fCurrent); 202 break; 203 case EXFAT_ENTRY_TYPE_FILENAME: 204 visitor.VisitFilename(fCurrent); 205 break; 206 } 207 } 208 } 209 210 211 status_t 212 DirectoryIterator::_GetNext(uint16* utf16Name, size_t* _codeUnitCount, 213 ino_t* _id, EntryVisitor* visitor) 214 { 215 size_t nameMax = *_codeUnitCount; 216 size_t nameIndex = 0; 217 status_t status; 218 int32 chunkCount = 1; 219 220 while ((status = _NextEntry()) == B_OK) { 221 TRACE("DirectoryIterator::_GetNext() %" B_PRIu32 "/%p, type 0x%x, " 222 "offset %" B_PRId64 "\n", fInode->Cluster(), fCurrent, 223 fCurrent->type, fOffset); 224 if (fCurrent->type == EXFAT_ENTRY_TYPE_FILE) { 225 chunkCount = fCurrent->file.chunkCount; 226 if (_id != NULL) { 227 *_id = fInode->GetVolume()->GetIno(fCluster, fOffset - 1, 228 fInode->ID()); 229 } 230 TRACE("DirectoryIterator::_GetNext() File chunkCount %" B_PRId32 231 "\n", chunkCount); 232 if (visitor != NULL) 233 visitor->VisitFile(fCurrent); 234 } else if (fCurrent->type == EXFAT_ENTRY_TYPE_FILEINFO) { 235 chunkCount--; 236 *_codeUnitCount = (size_t)fCurrent->file_info.name_length; 237 TRACE("DirectoryIterator::_GetNext() Filename chunk: %" B_PRId32 238 ", code unit count: %" B_PRIu8 "\n", chunkCount, *_codeUnitCount); 239 if (visitor != NULL) 240 visitor->VisitFileInfo(fCurrent); 241 } else if (fCurrent->type == EXFAT_ENTRY_TYPE_FILENAME) { 242 chunkCount--; 243 size_t utf16Length = sizeof(fCurrent->file_name.name); 244 memcpy(utf16Name + nameIndex, fCurrent->file_name.name, utf16Length); 245 nameIndex += utf16Length / sizeof(uint16); 246 TRACE("DirectoryIterator::_GetNext() Filename index: %zu\n", 247 nameIndex); 248 if (visitor != NULL) 249 visitor->VisitFilename(fCurrent); 250 } 251 252 if (chunkCount == 0 || nameIndex >= nameMax) 253 break; 254 } 255 256 #ifdef TRACE_EXFAT 257 if (status == B_OK) { 258 size_t utf8Length = B_FILE_NAME_LENGTH * 4; 259 char utf8Name[utf8Length + 1]; 260 ssize_t length = utf16le_to_utf8(utf16Name, *_codeUnitCount, utf8Name, 261 utf8Length); 262 if (length > 0) { 263 TRACE("DirectoryIterator::_GetNext() found name: \"%s\", " 264 "length: %d\n", utf8Name, length); 265 } 266 } 267 #endif 268 269 return status; 270 } 271 272 273 status_t 274 DirectoryIterator::_NextEntry() 275 { 276 if (fCurrent == NULL) { 277 fsblock_t block; 278 if (fInode->GetVolume()->ClusterToBlock(fCluster, block) != B_OK) 279 return B_BAD_DATA; 280 block += (fOffset / fInode->GetVolume()->EntriesPerBlock()) 281 % (1 << fInode->GetVolume()->SuperBlock().BlocksPerClusterShift()); 282 TRACE("DirectoryIterator::_NextEntry() init to block %" B_PRIu64 "\n", 283 block); 284 fCurrent = (struct exfat_entry*)fBlock.SetTo(block) 285 + fOffset % fInode->GetVolume()->EntriesPerBlock(); 286 } else if ((fOffset % fInode->GetVolume()->EntriesPerBlock()) == 0) { 287 fsblock_t block; 288 if ((fOffset % fInode->GetVolume()->EntriesPerCluster()) == 0) { 289 fCluster = fInode->NextCluster(fCluster); 290 if (fCluster == EXFAT_CLUSTER_END) 291 return B_ENTRY_NOT_FOUND; 292 293 if (fInode->GetVolume()->ClusterToBlock(fCluster, block) != B_OK) 294 return B_BAD_DATA; 295 } else 296 block = fBlock.BlockNumber() + 1; 297 298 TRACE("DirectoryIterator::_NextEntry() block %" B_PRIu64 "\n", block); 299 fCurrent = (struct exfat_entry*)fBlock.SetTo(block); 300 } else 301 fCurrent++; 302 303 fOffset++; 304 return fCurrent->type == 0 ? B_ENTRY_NOT_FOUND : B_OK; 305 } 306