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; 101 return score * (maxFactor 102 / std::min(maxFactor, 103 std::max((int32)1, index.index->CountEntries()))); 104 } 105 106 static type_code IndexGetType(Index& index) 107 { 108 return index.index->Type(); 109 } 110 111 static int32 IndexGetKeySize(Index& index) 112 { 113 return index.index->KeyLength(); 114 } 115 116 static IndexIterator* IndexCreateIterator(Index& index) 117 { 118 IndexIterator* iterator = new(std::nothrow) IndexIterator(index.index); 119 if (iterator == NULL) 120 return NULL; 121 122 if (!index.index->GetIterator(*iterator)) { 123 delete iterator; 124 return NULL; 125 } 126 127 return iterator; 128 } 129 130 // IndexIterator interface 131 132 static void IndexIteratorDelete(IndexIterator* indexIterator) 133 { 134 delete indexIterator; 135 } 136 137 static status_t IndexIteratorFind(IndexIterator* indexIterator, 138 const void* value, size_t size) 139 { 140 if (!indexIterator->index->Find(value, size, *indexIterator)) 141 return B_ENTRY_NOT_FOUND; 142 143 return B_OK; 144 } 145 146 static status_t IndexIteratorGetNextEntry(IndexIterator* indexIterator, 147 void* value, size_t* _valueLength, size_t bufferSize, Entry** _entry) 148 { 149 Node* node = indexIterator->Next(value, _valueLength); 150 if (node == NULL) 151 return B_ENTRY_NOT_FOUND; 152 153 *_entry = node; 154 return B_OK; 155 } 156 157 static void IndexIteratorSuspend(IndexIterator* indexIterator) 158 { 159 indexIterator->Suspend(); 160 } 161 162 static void IndexIteratorResume(IndexIterator* indexIterator) 163 { 164 indexIterator->Resume(); 165 } 166 167 // Node interface 168 169 static const off_t NodeGetSize(Node* node) 170 { 171 return node->FileSize(); 172 } 173 174 static time_t NodeGetLastModifiedTime(Node* node) 175 { 176 return node->ModifiedTime().tv_sec; 177 } 178 179 static status_t NodeGetAttribute(Node* node, const char* attribute, 180 void* buffer, size_t* _size, int32* _type) 181 { 182 // TODO: Creating a cookie is quite a bit of overhead. 183 AttributeCookie* cookie; 184 status_t error = node->OpenAttribute(StringKey(attribute), O_RDONLY, 185 cookie); 186 if (error != B_OK) 187 return error; 188 189 error = cookie->ReadAttribute(0, buffer, _size); 190 191 // also get the attribute type 192 if (error == B_OK) { 193 struct stat st; 194 error = cookie->ReadAttributeStat(&st); 195 if (error == B_OK) 196 *_type = st.st_type; 197 } 198 199 cookie->Close(); 200 delete cookie; 201 202 return error; 203 } 204 205 static Entry* NodeGetFirstReferrer(Node* node) 206 { 207 return node; 208 } 209 210 static Entry* NodeGetNextReferrer(Node* node, Entry* entry) 211 { 212 return NULL; 213 } 214 215 // Volume interface 216 217 static dev_t ContextGetVolumeID(Context* context) 218 { 219 return context->fVolume->ID(); 220 } 221 }; 222 223 224 // #pragma mark - Query 225 226 227 Query::Query(Volume* volume) 228 : 229 fVolume(volume), 230 fImpl(NULL) 231 { 232 } 233 234 235 Query::~Query() 236 { 237 if (fImpl != NULL) { 238 if ((fImpl->Flags() & B_LIVE_QUERY) != 0) 239 fVolume->RemoveQuery(this); 240 241 delete fImpl; 242 } 243 } 244 245 246 /*static*/ status_t 247 Query::Create(Volume* volume, const char* queryString, uint32 flags, 248 port_id port, uint32 token, Query*& _query) 249 { 250 Query* query = new(std::nothrow) Query(volume); 251 if (query == NULL) 252 return B_NO_MEMORY; 253 254 status_t error = query->_Init(queryString, flags, port, token); 255 if (error != B_OK) { 256 delete query; 257 return error; 258 } 259 260 _query = query; 261 return B_OK; 262 } 263 264 265 status_t 266 Query::Rewind() 267 { 268 return fImpl->Rewind(); 269 } 270 271 272 status_t 273 Query::GetNextEntry(struct dirent* entry, size_t size) 274 { 275 return fImpl->GetNextEntry(entry, size); 276 } 277 278 279 void 280 Query::LiveUpdate(Node* node, const char* attribute, int32 type, 281 const void* oldKey, size_t oldLength, const void* newKey, size_t newLength) 282 { 283 fImpl->LiveUpdate(node, node, attribute, type, (const uint8*)oldKey, 284 oldLength, (const uint8*)newKey, newLength); 285 } 286 287 288 status_t 289 Query::_Init(const char* queryString, uint32 flags, port_id port, uint32 token) 290 { 291 status_t error = QueryImpl::Create(this, queryString, flags, port, token, 292 fImpl); 293 if (error != B_OK) 294 return error; 295 296 if ((fImpl->Flags() & B_LIVE_QUERY) != 0) 297 fVolume->AddQuery(this); 298 299 return B_OK; 300 } 301