#ifndef FsckDBTable_h_drBbZVcUQetDB8UVnMd1Uj #define FsckDBTable_h_drBbZVcUQetDB8UVnMd1Uj #include #include #include #include #include #include #include #include #include #include #include #include class FsckDBDentryTable { public: struct ByParent { db::EntryID parent; /* 12 */ db::EntryID target; /* 24 */ uint32_t entryType; /* 28 */ typedef std::pair KeyType; KeyType pkey() const { return KeyType(parent, target); } }; class NameBuffer { public: NameBuffer(const std::string& path, unsigned id): fileID(id) { fd = ::open(path.c_str(), O_RDWR | O_CREAT, 0660); if(fd < 0) throw std::runtime_error("could not open name file"); streamBuf.reserve(4096 * 1024); writePos = ::lseek(fd, 0, SEEK_END); if (writePos < 0) { close(fd); fd = -1; throw std::runtime_error("could not open name file"); } } NameBuffer(NameBuffer&&) = delete; NameBuffer& operator=(NameBuffer&&) = delete; ~NameBuffer() { if (fd >= 0) { flush(); close(fd); } } uint64_t put(const std::string& name) { if (streamBuf.size() + name.size() + 1 > streamBuf.capacity()) flush(); if (name.size() > streamBuf.capacity()) streamBuf.reserve(name.size()); uint64_t result = writePos + streamBuf.size(); streamBuf.insert(streamBuf.end(), name.begin(), name.end()); streamBuf.push_back(0); return result; } std::string get(uint64_t offset) { flush(); char buffer[255 + 1]; ssize_t readRes = ::pread(fd, buffer, sizeof(buffer), offset); // if the read did not fill the full buffer, we can // a) have an error, or // b) have a short read. // errors should be reported directly. short reads are likely caused be trying to // read past the end of the file. since we cannot possible have a sane read that // stretches beyond writePos here (as we have flushed the write buffer), we can // reliably detect both. if (readRes < 0 || ssize_t(offset) + readRes > writePos) throw std::runtime_error("could not read name file: " + std::string(strerror(errno))); return buffer; } unsigned id() const { return fileID; } private: unsigned fileID; std::vector streamBuf; int fd; ssize_t writePos; void flush() { ssize_t writeRes = ::pwrite(fd, &streamBuf[0], streamBuf.size(), writePos); if (writeRes < 0 || size_t(writeRes) < streamBuf.size()) throw std::runtime_error("error in flush: " + std::string(strerror(errno))); writePos += writeRes; streamBuf.resize(0); } }; struct BulkHandle { boost::shared_ptr > dentries; boost::shared_ptr > byParent; NameBuffer* nameBuffer; }; private: struct NameCacheEntry { db::EntryID id; db::EntryID parent; std::string name; std::list::iterator lruLink; }; public: FsckDBDentryTable(const std::string& dbPath, size_t fragmentSize, size_t nameCacheLimit, bool allowCreate) : dbPath(dbPath), table(dbPath + "/dentries", fragmentSize, allowCreate), byParent(dbPath + "/dentriesbyparent", fragmentSize, allowCreate), insertSeqNo(0), nameCacheLimit(nameCacheLimit) { } void insert(FsckDirEntryList& dentries, const BulkHandle* handle = NULL); void updateFieldsExceptParent(FsckDirEntryList& dentries); void remove(FsckDirEntryList& dentries); Table::QueryType get(); std::pair getAnyFor(db::EntryID id); Table::QueryType getByParent(); std::string getNameOf(const db::DirEntry& dentry); std::string getPathOf(const db::DirEntry& dentry); private: std::string dbPath; Table table; Table byParent; std::map > nameBuffers; uint64_t insertSeqNo; std::map nameCache; std::list nameCacheLRU; size_t nameCacheLimit; NameBuffer& getNameBuffer(unsigned id) { if(this->nameBuffers.count(id) ) return *this->nameBuffers[id]; const std::string& nextNameFile = dbPath + "/dentrynames." + StringTk::intToStr(id); boost::shared_ptr buf = boost::make_shared( nextNameFile, id); this->nameBuffers[id] = buf; return *buf; } void addToCache(const db::DirEntry& entry, const std::string& name) { if(nameCache.size() >= nameCacheLimit) { nameCache.erase(nameCacheLRU.front()->id); nameCacheLRU.pop_front(); } NameCacheEntry cacheEntry = { entry.id, entry.parentDirID, name, nameCacheLRU.end() }; std::pair::iterator, bool> pos = nameCache.insert( std::make_pair(entry.id, cacheEntry) ); if(!pos.second) return; pos.first->second.lruLink = nameCacheLRU.insert(nameCacheLRU.end(), &pos.first->second); } const NameCacheEntry* getFromCache(db::EntryID id) { std::map::iterator pos = nameCache.find(id); if(pos == nameCache.end() ) return NULL; nameCacheLRU.splice(nameCacheLRU.end(), nameCacheLRU, pos->second.lruLink); return &pos->second; } public: void clear() { this->table.clear(); this->byParent.clear(); this->insertSeqNo = 0; this->nameCache.clear(); this->nameCacheLRU.clear(); } BulkHandle newBulkHandle() { BulkHandle result = { this->table.bulkInsert(), this->byParent.bulkInsert(), &getNameBuffer(this->nameBuffers.size() ), }; return result; } void flush(BulkHandle& handle) { handle = {{}, {}, nullptr}; } void insert(FsckDirEntryList& dentries, const BulkHandle& handle) { insert(dentries, &handle); } std::string getNameOf(db::EntryID id) { if(id == db::EntryID::anchor() || id == db::EntryID::root() ) return ""; else if(id == db::EntryID::disposal() ) return "[]"; else if(id == db::EntryID::mdisposal() ) return "[]"; std::pair item = getAnyFor(id); if(!item.first) return "[]"; else return getNameOf(item.second); } std::string getPathOf(db::EntryID id) { std::pair item = getAnyFor(id); if(!item.first) return "[]"; else return getPathOf(item.second); } void commitChanges() { table.commitChanges(); } }; class FsckDBFileInodesTable { public: typedef std::tuple< boost::shared_ptr >, boost::shared_ptr > > BulkHandle; struct UniqueInlinedInode { typedef boost::tuple KeyType; typedef db::FileInode ProjType; typedef int GroupType; KeyType key(const db::FileInode& fi) { return boost::make_tuple(fi.id, fi.saveInode, fi.saveNodeID, fi.saveDevice); } db::FileInode project(const db::FileInode& fi) { return fi; } void step(const db::FileInode& fi) {} GroupType finish() { return 0; } }; struct SelectFirst { typedef db::FileInode result_type; db::FileInode operator()(std::pair& pair) const { return pair.first; } }; typedef Select< Group< Table::QueryType, UniqueInlinedInode>, SelectFirst> InodesQueryType; public: FsckDBFileInodesTable(const std::string& dbPath, size_t fragmentSize, bool allowCreate) : inodes(dbPath + "/fileinodes", fragmentSize, allowCreate), targets(dbPath + "/filetargets", fragmentSize, allowCreate) { } void insert(FsckFileInodeList& fileInodes, const BulkHandle* handle = NULL); void update(FsckFileInodeList& inodes); void remove(FsckFileInodeList& fileInodes); InodesQueryType getInodes(); Table::QueryType getTargets(); UInt16Vector getStripeTargetsByKey(const db::EntryID& id); std::pair get(std::string id); private: Table inodes; Table targets; public: void clear() { inodes.clear(); targets.clear(); } void remove(db::EntryID id) { inodes.remove(id); targets.remove(id); } BulkHandle newBulkHandle() { return BulkHandle(this->inodes.bulkInsert(), this->targets.bulkInsert() ); } void flush(BulkHandle& handle) { handle = {}; } void insert(FsckFileInodeList& fileInodes, const BulkHandle& handle) { insert(fileInodes, &handle); } void commitChanges() { inodes.commitChanges(); targets.commitChanges(); } }; class FsckDBDirInodesTable { public: typedef boost::shared_ptr > BulkHandle; public: FsckDBDirInodesTable(const std::string& dbPath, size_t fragmentSize, bool allowCreate) : table(dbPath + "/dirinodes", fragmentSize, allowCreate) { } void insert(FsckDirInodeList& dirInodes, const BulkHandle* handle = NULL); void update(FsckDirInodeList& inodes); void remove(FsckDirInodeList& dirInodes); Table::QueryType get(); std::pair get(std::string id); private: Table table; public: void clear() { this->table.clear(); } BulkHandle newBulkHandle() { return this->table.bulkInsert(); } void flush(BulkHandle& handle) { handle = {}; } void insert(FsckDirInodeList& fileInodes, const BulkHandle& handle) { insert(fileInodes, &handle); } void commitChanges() { table.commitChanges(); } }; class FsckDBChunksTable { public: typedef boost::shared_ptr > BulkHandle; public: FsckDBChunksTable(const std::string& dbPath, size_t fragmentSize, bool allowCreate) : table(dbPath + "/chunks", fragmentSize, allowCreate) { } void insert(FsckChunkList& chunks, const BulkHandle* handle = NULL); void update(FsckChunkList& chunks); void remove(const db::Chunk::KeyType& id); Table::QueryType get(); private: Table table; public: void clear() { table.clear(); } BulkHandle newBulkHandle() { return this->table.bulkInsert(); } void flush(BulkHandle& handle) { handle = {}; } void insert(FsckChunkList& chunks, const BulkHandle& handle) { insert(chunks, &handle); } void commitChanges() { table.commitChanges(); } }; class FsckDBContDirsTable { public: typedef boost::shared_ptr > BulkHandle; public: FsckDBContDirsTable(const std::string& dbPath, size_t fragmentSize, bool allowCreate) : table(dbPath + "/contdirs", fragmentSize, allowCreate) { } void insert(FsckContDirList& contDirs, const BulkHandle* handle = NULL); Table::QueryType get(); private: Table table; public: void clear() { table.clear(); } BulkHandle newBulkHandle() { return this->table.bulkInsert(); } void flush(BulkHandle& handle) { handle = {}; } void insert(FsckContDirList& contDirs, const BulkHandle& handle) { insert(contDirs, &handle); } void commitChanges() { table.commitChanges(); } }; class FsckDBFsIDsTable { public: typedef boost::shared_ptr > BulkHandle; public: FsckDBFsIDsTable(const std::string& dbPath, size_t fragmentSize, bool allowCreate) : table(dbPath + "/fsids", fragmentSize, allowCreate) { } void insert(FsckFsIDList& fsIDs, const BulkHandle* handle = NULL); void remove(FsckFsIDList& fsIDs); Table::QueryType get(); private: Table table; public: void clear() { this->table.clear(); } BulkHandle newBulkHandle() { return this->table.bulkInsert(); } void flush(BulkHandle& handle) { handle = {}; } void insert(FsckFsIDList& fsIDs, const BulkHandle& handle) { insert(fsIDs, &handle); } void commitChanges() { table.commitChanges(); } }; template class FsckUniqueTable { public: typedef boost::shared_ptr > BulkHandle; protected: FsckUniqueTable(const std::string& name, size_t bufferSize, bool allowCreate) : set(name, allowCreate), bufferSize(bufferSize) {} private: Set set; size_t bufferSize; std::vector > > bulkHandles; public: void clear() { set.clear(); bulkHandles.clear(); } BulkHandle newBulkHandle() { BulkHandle result(new Buffer(set, bufferSize) ); bulkHandles.push_back(result); return result; } void flush(BulkHandle& handle) { handle = {}; } SetFragmentCursor get() { if(!bulkHandles.empty() ) { for(size_t i = 0; i < bulkHandles.size(); i++) { if(!bulkHandles[i].expired() ) throw std::runtime_error("still has open bulk handles"); } bulkHandles.clear(); set.makeUnique(); } return set.cursor(); } }; class FsckDBUsedTargetIDsTable : public FsckUniqueTable { public: FsckDBUsedTargetIDsTable(const std::string& dbPath, size_t fragmentSize, bool allowCreate) : FsckUniqueTable(dbPath + "/usedtargets", fragmentSize, allowCreate) { } void insert(FsckTargetIDList& targetIDs, const BulkHandle& handle); }; class FsckDBModificationEventsTable : public FsckUniqueTable { public: FsckDBModificationEventsTable(const std::string& dbPath, size_t fragmentSize, bool allowCreate) : FsckUniqueTable(dbPath + "/modevents", fragmentSize, allowCreate) { } void insert(const FsckModificationEventList& events, const BulkHandle& handle); }; #endif