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, &utfLength); 112 if (nameLength == utfLength 113 && strncmp(utfName, name, nameLength) == 0) { 114 TRACE("DirectoryIterator::Lookup() found ID %" B_PRIdINO "\n", *_id); 115 return B_OK; 116 } 117 currentLength = EXFAT_FILENAME_MAX_LENGTH; 118 } 119 120 TRACE("DirectoryIterator::Lookup() not found %s\n", name); 121 122 return B_ENTRY_NOT_FOUND; 123 } 124 125 126 status_t 127 DirectoryIterator::LookupEntry(EntryVisitor* visitor) 128 { 129 fCluster = fInode->Cluster(); 130 fOffset = fInode->Offset(); 131 132 uchar unicodeName[EXFAT_FILENAME_MAX_LENGTH + 1]; 133 size_t nameLength = EXFAT_FILENAME_MAX_LENGTH; 134 return _GetNext(unicodeName, &nameLength, NULL, visitor); 135 } 136 137 138 status_t 139 DirectoryIterator::Rewind() 140 { 141 fOffset = -2; 142 fCluster = fInode->StartCluster(); 143 return B_OK; 144 } 145 146 147 void 148 DirectoryIterator::Iterate(EntryVisitor &visitor) 149 { 150 fOffset = 0; 151 fCluster = fInode->StartCluster(); 152 153 while (_NextEntry() != B_ENTRY_NOT_FOUND) { 154 switch (fCurrent->type) { 155 case EXFAT_ENTRY_TYPE_BITMAP: 156 visitor.VisitBitmap(fCurrent); 157 break; 158 case EXFAT_ENTRY_TYPE_UPPERCASE: 159 visitor.VisitUppercase(fCurrent); 160 break; 161 case EXFAT_ENTRY_TYPE_LABEL: 162 visitor.VisitLabel(fCurrent); 163 break; 164 case EXFAT_ENTRY_TYPE_FILE: 165 visitor.VisitFile(fCurrent); 166 break; 167 case EXFAT_ENTRY_TYPE_FILEINFO: 168 visitor.VisitFileInfo(fCurrent); 169 break; 170 case EXFAT_ENTRY_TYPE_FILENAME: 171 visitor.VisitFilename(fCurrent); 172 break; 173 } 174 } 175 } 176 177 178 status_t 179 DirectoryIterator::_GetNext(uchar* name, size_t* _nameLength, ino_t* _id, 180 EntryVisitor* visitor) 181 { 182 size_t nameMax = *_nameLength; 183 size_t nameIndex = 0; 184 status_t status; 185 int32 chunkCount = 1; 186 while ((status = _NextEntry()) == B_OK) { 187 TRACE("DirectoryIterator::_GetNext() %" B_PRIu32 "/%p, type 0x%x, " 188 "offset %" B_PRId64 "\n", fInode->Cluster(), fCurrent, 189 fCurrent->type, fOffset); 190 if (fCurrent->type == EXFAT_ENTRY_TYPE_FILE) { 191 chunkCount = fCurrent->file.chunkCount; 192 if (_id != NULL) { 193 *_id = fInode->GetVolume()->GetIno(fCluster, fOffset - 1, 194 fInode->ID()); 195 } 196 if (visitor != NULL) 197 visitor->VisitFile(fCurrent); 198 TRACE("DirectoryIterator::_GetNext() File chunkCount %" B_PRId32 199 "\n", chunkCount); 200 } else if (fCurrent->type == EXFAT_ENTRY_TYPE_FILEINFO) { 201 chunkCount--; 202 TRACE("DirectoryIterator::_GetNext() Filename length %d\n", 203 fCurrent->file_info.name_length); 204 *_nameLength = fCurrent->file_info.name_length * 2; 205 if (visitor != NULL) 206 visitor->VisitFileInfo(fCurrent); 207 } else if (fCurrent->type == EXFAT_ENTRY_TYPE_FILENAME) { 208 TRACE("DirectoryIterator::_GetNext() Filename\n"); 209 memcpy((uint8*)name + nameIndex, fCurrent->name_label.name, 210 sizeof(fCurrent->name_label.name)); 211 nameIndex += sizeof(fCurrent->name_label.name); 212 name[nameIndex] = '\0'; 213 chunkCount--; 214 if (visitor != NULL) 215 visitor->VisitFilename(fCurrent); 216 } 217 218 if (chunkCount == 0 || nameIndex >= nameMax) 219 break; 220 } 221 222 if (status == B_OK) { 223 //*_nameLength = nameIndex; 224 #ifdef TRACE_EXFAT 225 char utfName[EXFAT_FILENAME_MAX_LENGTH]; 226 size_t utfLen = EXFAT_FILENAME_MAX_LENGTH; 227 unicode_to_utf8(name, nameIndex, (uint8*)utfName, &utfLen); 228 TRACE("DirectoryIterator::_GetNext() Found %s %ld\n", utfName, 229 *_nameLength); 230 #endif 231 } 232 233 return status; 234 } 235 236 237 status_t 238 DirectoryIterator::_NextEntry() 239 { 240 if (fCurrent == NULL) { 241 fsblock_t block; 242 fInode->GetVolume()->ClusterToBlock(fCluster, block); 243 block += (fOffset / fInode->GetVolume()->EntriesPerBlock()) 244 % (1 << fInode->GetVolume()->SuperBlock().BlocksPerClusterShift()); 245 TRACE("DirectoryIterator::_NextEntry() init to block %" B_PRIu64 "\n", 246 block); 247 fCurrent = (struct exfat_entry*)fBlock.SetTo(block) 248 + fOffset % fInode->GetVolume()->EntriesPerBlock(); 249 } else if ((fOffset % fInode->GetVolume()->EntriesPerBlock()) == 0) { 250 fsblock_t block; 251 if ((fOffset % fInode->GetVolume()->EntriesPerCluster()) == 0) { 252 fCluster = fInode->NextCluster(fCluster); 253 if (fCluster == EXFAT_CLUSTER_END) 254 return B_ENTRY_NOT_FOUND; 255 fInode->GetVolume()->ClusterToBlock(fCluster, block); 256 } else 257 block = fBlock.BlockNumber() + 1; 258 TRACE("DirectoryIterator::_NextEntry() block %" B_PRIu64 "\n", block); 259 fCurrent = (struct exfat_entry*)fBlock.SetTo(block); 260 } else 261 fCurrent++; 262 fOffset++; 263 264 return fCurrent->type == 0 ? B_ENTRY_NOT_FOUND : B_OK; 265 } 266 267 268