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