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() %ld\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 (fOffset == -2) { 51 *_nameLength = 3; 52 strlcpy(name, "..", *_nameLength); 53 if (fInode->ID() == 1) 54 *_id = fInode->ID(); 55 else 56 *_id = fInode->Parent(); 57 fOffset = -1; 58 TRACE("DirectoryIterator::GetNext() found ..\n"); 59 return B_OK; 60 } else if (fOffset == -1) { 61 *_nameLength = 2; 62 strlcpy(name, ".", *_nameLength); 63 *_id = fInode->ID(); 64 fOffset = 0; 65 TRACE("DirectoryIterator::GetNext() found .\n"); 66 return B_OK; 67 } 68 69 uchar unicodeName[EXFAT_FILENAME_MAX_LENGTH]; 70 size_t nameLength = EXFAT_FILENAME_MAX_LENGTH; 71 status_t status = _GetNext(unicodeName, &nameLength, _id, visitor); 72 if (status == B_OK && name != NULL) { 73 unicode_to_utf8(unicodeName, nameLength, (uint8 *)name , _nameLength); 74 TRACE("DirectoryIterator::GetNext() %ld %s, %" B_PRIdINO "\n", 75 fInode->Cluster(), name, *_id); 76 } 77 78 return status; 79 } 80 81 82 status_t 83 DirectoryIterator::Lookup(const char* name, size_t nameLength, ino_t* _id) 84 { 85 if (strcmp(name, ".") == 0) { 86 *_id = fInode->ID(); 87 return B_OK; 88 } else if (strcmp(name, "..") == 0) { 89 if (fInode->ID() == 1) 90 *_id = fInode->ID(); 91 else 92 *_id = fInode->Parent(); 93 return B_OK; 94 } 95 96 Rewind(); 97 fOffset = 0; 98 99 uchar currentName[EXFAT_FILENAME_MAX_LENGTH]; 100 size_t currentLength = EXFAT_FILENAME_MAX_LENGTH; 101 while (_GetNext((uchar*)currentName, ¤tLength, _id) == B_OK) { 102 char utfName[EXFAT_FILENAME_MAX_LENGTH]; 103 size_t utfLength = EXFAT_FILENAME_MAX_LENGTH; 104 unicode_to_utf8(currentName, currentLength, (uint8*)utfName, &utfLength); 105 if (nameLength == utfLength 106 && strncmp(utfName, name, nameLength) == 0) { 107 TRACE("DirectoryIterator::Lookup() found ID %" B_PRIdINO "\n", *_id); 108 return B_OK; 109 } 110 currentLength = EXFAT_FILENAME_MAX_LENGTH; 111 } 112 113 TRACE("DirectoryIterator::Lookup() not found %s\n", name); 114 115 return B_ENTRY_NOT_FOUND; 116 } 117 118 119 status_t 120 DirectoryIterator::LookupEntry(EntryVisitor* visitor) 121 { 122 fCluster = fInode->Cluster(); 123 fOffset = fInode->Offset(); 124 125 uchar unicodeName[EXFAT_FILENAME_MAX_LENGTH]; 126 size_t nameLength = EXFAT_FILENAME_MAX_LENGTH; 127 return _GetNext(unicodeName, &nameLength, NULL, visitor); 128 } 129 130 131 status_t 132 DirectoryIterator::Rewind() 133 { 134 fOffset = -2; 135 fCluster = fInode->StartCluster(); 136 return B_OK; 137 } 138 139 140 void 141 DirectoryIterator::Iterate(EntryVisitor &visitor) 142 { 143 fOffset = 0; 144 fCluster = fInode->StartCluster(); 145 146 while (_NextEntry() != B_ENTRY_NOT_FOUND) { 147 switch (fCurrent->type) { 148 case EXFAT_ENTRY_TYPE_BITMAP: 149 visitor.VisitBitmap(fCurrent); 150 break; 151 case EXFAT_ENTRY_TYPE_UPPERCASE: 152 visitor.VisitUppercase(fCurrent); 153 break; 154 case EXFAT_ENTRY_TYPE_LABEL: 155 visitor.VisitLabel(fCurrent); 156 break; 157 case EXFAT_ENTRY_TYPE_FILE: 158 visitor.VisitFile(fCurrent); 159 break; 160 case EXFAT_ENTRY_TYPE_FILEINFO: 161 visitor.VisitFileInfo(fCurrent); 162 break; 163 case EXFAT_ENTRY_TYPE_FILENAME: 164 visitor.VisitFilename(fCurrent); 165 break; 166 } 167 } 168 } 169 170 171 status_t 172 DirectoryIterator::_GetNext(uchar* name, size_t* _nameLength, ino_t* _id, 173 EntryVisitor* visitor) 174 { 175 size_t nameMax = *_nameLength; 176 size_t nameIndex = 0; 177 status_t status; 178 int32 chunkCount = 1; 179 while ((status = _NextEntry()) == B_OK) { 180 TRACE("DirectoryIterator::_GetNext() %ld/%p, type 0x%x, offset %lld\n", 181 fInode->Cluster(), fCurrent, fCurrent->type, fOffset); 182 if (fCurrent->type == EXFAT_ENTRY_TYPE_FILE) { 183 chunkCount = fCurrent->file.chunkCount; 184 if (_id != NULL) { 185 *_id = fInode->GetVolume()->GetIno(fCluster, fOffset - 1, 186 fInode->ID()); 187 } 188 if (visitor != NULL) 189 visitor->VisitFile(fCurrent); 190 TRACE("DirectoryIterator::_GetNext() File chunkCount %ld\n", 191 chunkCount); 192 } else if (fCurrent->type == EXFAT_ENTRY_TYPE_FILEINFO) { 193 chunkCount--; 194 TRACE("DirectoryIterator::_GetNext() Filename length %d\n", 195 fCurrent->file_info.name_length); 196 *_nameLength = fCurrent->file_info.name_length * 2; 197 if (visitor != NULL) 198 visitor->VisitFileInfo(fCurrent); 199 } else if (fCurrent->type == EXFAT_ENTRY_TYPE_FILENAME) { 200 TRACE("DirectoryIterator::_GetNext() Filename\n"); 201 memcpy((uint8*)name + nameIndex, fCurrent->name_label.name, 202 sizeof(fCurrent->name_label.name)); 203 nameIndex += sizeof(fCurrent->name_label.name); 204 name[nameIndex] = '\0'; 205 chunkCount--; 206 if (visitor != NULL) 207 visitor->VisitFilename(fCurrent); 208 } 209 210 if (chunkCount == 0 || nameIndex >= nameMax) 211 break; 212 } 213 214 if (status == B_OK) { 215 //*_nameLength = nameIndex; 216 #ifdef TRACE_EXFAT 217 char utfName[EXFAT_FILENAME_MAX_LENGTH]; 218 size_t utfLen = EXFAT_FILENAME_MAX_LENGTH; 219 unicode_to_utf8(name, nameIndex, (uint8*)utfName, &utfLen); 220 TRACE("DirectoryIterator::_GetNext() Found %s %ld\n", utfName, 221 *_nameLength); 222 #endif 223 } 224 225 return status; 226 } 227 228 229 status_t 230 DirectoryIterator::_NextEntry() 231 { 232 if (fCurrent == NULL) { 233 fsblock_t block; 234 fInode->GetVolume()->ClusterToBlock(fCluster, block); 235 block += (fOffset / fInode->GetVolume()->EntriesPerBlock()) 236 % (1 << fInode->GetVolume()->SuperBlock().BlocksPerClusterShift()); 237 TRACE("DirectoryIterator::_NextEntry() init to block %lld\n", block); 238 fCurrent = (struct exfat_entry*)fBlock.SetTo(block) 239 + fOffset % fInode->GetVolume()->EntriesPerBlock(); 240 } else if ((fOffset % fInode->GetVolume()->EntriesPerBlock()) == 0) { 241 fsblock_t block; 242 if ((fOffset % fInode->GetVolume()->EntriesPerCluster()) == 0) { 243 fCluster = fInode->NextCluster(fCluster); 244 if (fCluster == EXFAT_CLUSTER_END) 245 return B_ENTRY_NOT_FOUND; 246 fInode->GetVolume()->ClusterToBlock(fCluster, block); 247 } else 248 block = fBlock.BlockNumber() + 1; 249 TRACE("DirectoryIterator::_NextEntry() block %lld\n", block); 250 fCurrent = (struct exfat_entry*)fBlock.SetTo(block); 251 } else 252 fCurrent++; 253 fOffset++; 254 255 return fCurrent->type == 0 ? B_ENTRY_NOT_FOUND : B_OK; 256 } 257 258 259