1 /* 2 * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "Query.h" 8 9 #include <file_systems/QueryParser.h> 10 11 #include "AttributeCookie.h" 12 #include "Directory.h" 13 #include "Index.h" 14 #include "Node.h" 15 #include "Volume.h" 16 17 18 // #pragma mark - QueryPolicy 19 20 21 struct Query::QueryPolicy { 22 typedef Query Context; 23 typedef ::Node Entry; 24 typedef ::Node Node; 25 26 struct Index { 27 Query* query; 28 ::Index* index; 29 30 Index(Context* context) 31 : 32 query(context) 33 { 34 } 35 }; 36 37 struct IndexIterator : ::IndexIterator { 38 ::Index* index; 39 40 IndexIterator(::Index* index) 41 : 42 index(index) 43 { 44 } 45 }; 46 47 static const int32 kMaxFileNameLength = B_FILE_NAME_LENGTH; 48 49 // Entry interface 50 51 static ino_t EntryGetParentID(Entry* entry) 52 { 53 return entry->Parent()->ID(); 54 } 55 56 static Node* EntryGetNode(Entry* entry) 57 { 58 return entry; 59 } 60 61 static ino_t EntryGetNodeID(Entry* entry) 62 { 63 return entry->ID(); 64 } 65 66 static ssize_t EntryGetName(Entry* entry, void* buffer, size_t bufferSize) 67 { 68 const char* name = entry->Name(); 69 size_t nameLength = strlen(name); 70 if (nameLength >= bufferSize) 71 return B_BUFFER_OVERFLOW; 72 73 memcpy(buffer, name, nameLength + 1); 74 return nameLength + 1; 75 } 76 77 static const char* EntryGetNameNoCopy(Entry* entry, void* buffer, 78 size_t bufferSize) 79 { 80 return entry->Name(); 81 } 82 83 // Index interface 84 85 static status_t IndexSetTo(Index& index, const char* attribute) 86 { 87 index.index = index.query->fVolume->FindIndex(StringKey(attribute)); 88 return index.index != NULL ? B_OK : B_ENTRY_NOT_FOUND; 89 } 90 91 static void IndexUnset(Index& index) 92 { 93 index.index = NULL; 94 } 95 96 static int32 IndexGetWeightedScore(Index& index, int32 score) 97 { 98 // should be inversely proportional to the index size; max input score 99 // is 2048 100 static const int32 maxFactor = (1024 * 1024) - 1; 101 return score * (maxFactor / 102 std::min(maxFactor, std::max((int32)1, index.index->CountEntries()))); 103 } 104 105 static type_code IndexGetType(Index& index) 106 { 107 return index.index->Type(); 108 } 109 110 static int32 IndexGetKeySize(Index& index) 111 { 112 return index.index->KeyLength(); 113 } 114 115 static IndexIterator* IndexCreateIterator(Index& index) 116 { 117 IndexIterator* iterator = new(std::nothrow) IndexIterator(index.index); 118 if (iterator == NULL) 119 return NULL; 120 121 if (!index.index->GetIterator(*iterator)) { 122 delete iterator; 123 return NULL; 124 } 125 126 return iterator; 127 } 128 129 // IndexIterator interface 130 131 static void IndexIteratorDelete(IndexIterator* indexIterator) 132 { 133 delete indexIterator; 134 } 135 136 static status_t IndexIteratorFind(IndexIterator* indexIterator, 137 const void* value, size_t size) 138 { 139 if (!indexIterator->index->Find(value, size, *indexIterator)) 140 return B_ENTRY_NOT_FOUND; 141 142 return B_OK; 143 } 144 145 static status_t IndexIteratorGetNextEntry(IndexIterator* indexIterator, 146 void* value, size_t* _valueLength, size_t bufferSize, Entry** _entry) 147 { 148 Node* node = indexIterator->Next(value, _valueLength); 149 if (node == NULL) 150 return B_ENTRY_NOT_FOUND; 151 152 *_entry = node; 153 return B_OK; 154 } 155 156 static void IndexIteratorSuspend(IndexIterator* indexIterator) 157 { 158 indexIterator->Suspend(); 159 } 160 161 static void IndexIteratorResume(IndexIterator* indexIterator) 162 { 163 indexIterator->Resume(); 164 } 165 166 // Node interface 167 168 static const off_t NodeGetSize(Node* node) 169 { 170 return node->FileSize(); 171 } 172 173 static time_t NodeGetLastModifiedTime(Node* node) 174 { 175 return node->ModifiedTime().tv_sec; 176 } 177 178 static status_t NodeGetAttribute(Node* node, const char* attribute, 179 void* buffer, size_t* _size, int32* _type) 180 { 181 // TODO: Creating a cookie is quite a bit of overhead. 182 AttributeCookie* cookie; 183 status_t error = node->OpenAttribute(StringKey(attribute), O_RDONLY, 184 cookie); 185 if (error != B_OK) 186 return error; 187 188 error = cookie->ReadAttribute(0, buffer, _size); 189 190 // also get the attribute type 191 if (error == B_OK) { 192 struct stat st; 193 error = cookie->ReadAttributeStat(&st); 194 if (error == B_OK) 195 *_type = st.st_type; 196 } 197 198 cookie->Close(); 199 delete cookie; 200 201 return error; 202 } 203 204 static Entry* NodeGetFirstReferrer(Node* node) 205 { 206 return node; 207 } 208 209 static Entry* NodeGetNextReferrer(Node* node, Entry* entry) 210 { 211 return NULL; 212 } 213 214 // Volume interface 215 216 static dev_t ContextGetVolumeID(Context* context) 217 { 218 return context->fVolume->ID(); 219 } 220 }; 221 222 223 // #pragma mark - Query 224 225 226 Query::Query(Volume* volume) 227 : 228 fVolume(volume), 229 fImpl(NULL) 230 { 231 } 232 233 234 Query::~Query() 235 { 236 if (fImpl != NULL) { 237 if ((fImpl->Flags() & B_LIVE_QUERY) != 0) 238 fVolume->RemoveQuery(this); 239 240 delete fImpl; 241 } 242 } 243 244 245 /*static*/ status_t 246 Query::Create(Volume* volume, const char* queryString, uint32 flags, 247 port_id port, uint32 token, Query*& _query) 248 { 249 Query* query = new(std::nothrow) Query(volume); 250 if (query == NULL) 251 return B_NO_MEMORY; 252 253 status_t error = query->_Init(queryString, flags, port, token); 254 if (error != B_OK) { 255 delete query; 256 return error; 257 } 258 259 _query = query; 260 return B_OK; 261 } 262 263 264 status_t 265 Query::Rewind() 266 { 267 return fImpl->Rewind(); 268 } 269 270 271 status_t 272 Query::GetNextEntry(struct dirent* entry, size_t size) 273 { 274 return fImpl->GetNextEntry(entry, size); 275 } 276 277 278 void 279 Query::LiveUpdate(Node* node, const char* attribute, int32 type, 280 const void* oldKey, size_t oldLength, const void* newKey, size_t newLength) 281 { 282 fImpl->LiveUpdate(node, node, attribute, type, (const uint8*)oldKey, 283 oldLength, (const uint8*)newKey, newLength); 284 } 285 286 287 status_t 288 Query::_Init(const char* queryString, uint32 flags, port_id port, uint32 token) 289 { 290 status_t error = QueryImpl::Create(this, queryString, flags, port, token, 291 fImpl); 292 if (error != B_OK) 293 return error; 294 295 if ((fImpl->Flags() & B_LIVE_QUERY) != 0) 296 fVolume->AddQuery(this); 297 298 return B_OK; 299 } 300