mirror of
https://github.com/RetroShare/RetroShare.git
synced 2025-08-02 03:06:31 -04:00

Channels are now able to take advantage of the new deep search goodies Rebase on top of master 2021/10/19
1082 lines
38 KiB
C++
1082 lines
38 KiB
C++
/*******************************************************************************
|
|
* libretroshare/src/file_sharing: directory_storage.cc *
|
|
* *
|
|
* libretroshare: retroshare core library *
|
|
* *
|
|
* Copyright (C) 2016 Mr.Alice <mralice@users.sourceforge.net> *
|
|
* Copyright (C) 2018-2021 Gioacchino Mazzurco <gio@eigenlab.org> *
|
|
* Copyright (C) 2019-2021 Asociación Civil Altermundi <info@altermundi.net> *
|
|
* *
|
|
* This program is free software: you can redistribute it and/or modify *
|
|
* it under the terms of the GNU Lesser General Public License as *
|
|
* published by the Free Software Foundation, either version 3 of the *
|
|
* License, or (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU Lesser General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU Lesser General Public License *
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
|
|
* *
|
|
******************************************************************************/
|
|
#include <set>
|
|
|
|
#include "util/rstime.h"
|
|
#include "serialiser/rstlvbinary.h"
|
|
#include "retroshare/rspeers.h"
|
|
#include "util/rsdir.h"
|
|
#include "util/rsstring.h"
|
|
#include "file_sharing_defaults.h"
|
|
#include "directory_storage.h"
|
|
#include "dir_hierarchy.h"
|
|
#include "filelist_io.h"
|
|
#include "util/cxx17retrocompat.h"
|
|
|
|
#ifdef RS_DEEP_FILES_INDEX
|
|
# include "deep_search/filesindex.hpp"
|
|
#endif // def RS_DEEP_FILES_INDEX
|
|
|
|
//#define DEBUG_REMOTE_DIRECTORY_STORAGE 1
|
|
|
|
/******************************************************************************************************************/
|
|
/* Iterators */
|
|
/******************************************************************************************************************/
|
|
|
|
DirectoryStorage::DirIterator::DirIterator(DirectoryStorage *s,DirectoryStorage::EntryIndex i)
|
|
{
|
|
mStorage = s->mFileHierarchy ;
|
|
mParentIndex = i;
|
|
mDirTabIndex = 0;
|
|
}
|
|
|
|
DirectoryStorage::FileIterator::FileIterator(DirectoryStorage *s,DirectoryStorage::EntryIndex i)
|
|
{
|
|
mStorage = s->mFileHierarchy ;
|
|
mParentIndex = i;
|
|
mFileTabIndex = 0;
|
|
}
|
|
|
|
DirectoryStorage::DirIterator& DirectoryStorage::DirIterator::operator++()
|
|
{
|
|
++mDirTabIndex ;
|
|
|
|
return *this;
|
|
}
|
|
DirectoryStorage::FileIterator& DirectoryStorage::FileIterator::operator++()
|
|
{
|
|
++mFileTabIndex ;
|
|
|
|
return *this;
|
|
}
|
|
DirectoryStorage::EntryIndex DirectoryStorage::FileIterator::operator*() const
|
|
{ return mStorage->getSubFileIndex(mParentIndex, mFileTabIndex); }
|
|
|
|
DirectoryStorage::EntryIndex DirectoryStorage::DirIterator::operator*() const
|
|
{ return mStorage->getSubDirIndex(mParentIndex, mDirTabIndex); }
|
|
|
|
DirectoryStorage::FileIterator::operator bool() const { return **this != DirectoryStorage::NO_INDEX; }
|
|
DirectoryStorage::DirIterator ::operator bool() const { return **this != DirectoryStorage::NO_INDEX; }
|
|
|
|
RsFileHash DirectoryStorage::FileIterator::hash() const { const InternalFileHierarchyStorage::FileEntry *f = mStorage->getFileEntry(**this) ; return f?(f->file_hash):RsFileHash(); }
|
|
uint64_t DirectoryStorage::FileIterator::size() const { const InternalFileHierarchyStorage::FileEntry *f = mStorage->getFileEntry(**this) ; return f?(f->file_size):0; }
|
|
std::string DirectoryStorage::FileIterator::name() const { const InternalFileHierarchyStorage::FileEntry *f = mStorage->getFileEntry(**this) ; return f?(f->file_name):std::string(); }
|
|
rstime_t DirectoryStorage::FileIterator::modtime() const { const InternalFileHierarchyStorage::FileEntry *f = mStorage->getFileEntry(**this) ; return f?(f->file_modtime):0; }
|
|
|
|
std::string DirectoryStorage::DirIterator::name() const { const InternalFileHierarchyStorage::DirEntry *d = mStorage->getDirEntry(**this) ; return d?(d->dir_name):std::string(); }
|
|
|
|
/******************************************************************************************************************/
|
|
/* Directory Storage */
|
|
/******************************************************************************************************************/
|
|
|
|
DirectoryStorage::DirectoryStorage(const RsPeerId &pid,const std::string& fname)
|
|
: mPeerId(pid), mDirStorageMtx("Directory storage "+pid.toStdString()),mLastSavedTime(0),mChanged(false),mFileName(fname)
|
|
{
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx) ;
|
|
mFileHierarchy = new InternalFileHierarchyStorage();
|
|
}
|
|
load(fname) ;
|
|
}
|
|
|
|
DirectoryStorage::EntryIndex DirectoryStorage::root() const
|
|
{
|
|
return EntryIndex(0) ;
|
|
}
|
|
int DirectoryStorage::parentRow(EntryIndex e) const
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx) ;
|
|
|
|
return mFileHierarchy->parentRow(e) ;
|
|
}
|
|
bool DirectoryStorage::getChildIndex(EntryIndex e,int row,EntryIndex& c) const
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx) ;
|
|
|
|
return mFileHierarchy->getChildIndex(e,row,c) ;
|
|
}
|
|
|
|
uint32_t DirectoryStorage::getEntryType(const EntryIndex& indx)
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx) ;
|
|
|
|
switch(mFileHierarchy->getType(indx))
|
|
{
|
|
case InternalFileHierarchyStorage::FileStorageNode::TYPE_DIR: return DIR_TYPE_DIR ;
|
|
case InternalFileHierarchyStorage::FileStorageNode::TYPE_FILE: return DIR_TYPE_FILE ;
|
|
default:
|
|
return DIR_TYPE_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
bool DirectoryStorage::getDirectoryUpdateTime (EntryIndex index,rstime_t& update_TS) const { RS_STACK_MUTEX(mDirStorageMtx) ; return mFileHierarchy->getTS(index,update_TS,&InternalFileHierarchyStorage::DirEntry::dir_update_time ); }
|
|
bool DirectoryStorage::getDirectoryRecursModTime(EntryIndex index,rstime_t& rec_md_TS) const { RS_STACK_MUTEX(mDirStorageMtx) ; return mFileHierarchy->getTS(index,rec_md_TS,&InternalFileHierarchyStorage::DirEntry::dir_most_recent_time); }
|
|
bool DirectoryStorage::getDirectoryLocalModTime (EntryIndex index,rstime_t& loc_md_TS) const { RS_STACK_MUTEX(mDirStorageMtx) ; return mFileHierarchy->getTS(index,loc_md_TS,&InternalFileHierarchyStorage::DirEntry::dir_modtime ); }
|
|
|
|
bool DirectoryStorage::setDirectoryUpdateTime (EntryIndex index,rstime_t update_TS) { RS_STACK_MUTEX(mDirStorageMtx) ; return mFileHierarchy->setTS(index,update_TS,&InternalFileHierarchyStorage::DirEntry::dir_update_time ); }
|
|
bool DirectoryStorage::setDirectoryRecursModTime(EntryIndex index,rstime_t rec_md_TS) { RS_STACK_MUTEX(mDirStorageMtx) ; return mFileHierarchy->setTS(index,rec_md_TS,&InternalFileHierarchyStorage::DirEntry::dir_most_recent_time); }
|
|
bool DirectoryStorage::setDirectoryLocalModTime (EntryIndex index,rstime_t loc_md_TS) { RS_STACK_MUTEX(mDirStorageMtx) ; return mFileHierarchy->setTS(index,loc_md_TS,&InternalFileHierarchyStorage::DirEntry::dir_modtime ); }
|
|
|
|
bool DirectoryStorage::updateSubDirectoryList(const EntryIndex& indx, const std::set<std::string> &subdirs, const RsFileHash& hash_salt)
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx) ;
|
|
bool res = mFileHierarchy->updateSubDirectoryList(indx,subdirs,hash_salt) ;
|
|
mChanged = true ;
|
|
return res ;
|
|
}
|
|
bool DirectoryStorage::updateSubFilesList(
|
|
const EntryIndex& indx, const std::map<std::string,FileTS>& subfiles,
|
|
std::map<std::string,FileTS>& new_files )
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx) ;
|
|
bool res = mFileHierarchy->updateSubFilesList(indx,subfiles,new_files) ;
|
|
mChanged = true ;
|
|
return res ;
|
|
}
|
|
bool DirectoryStorage::removeDirectory(const EntryIndex& indx)
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx) ;
|
|
bool res = mFileHierarchy->removeDirectory(indx);
|
|
mChanged = true ;
|
|
|
|
return res ;
|
|
}
|
|
|
|
void DirectoryStorage::locked_check()
|
|
{
|
|
std::string error ;
|
|
if(!mFileHierarchy->check(error))
|
|
std::cerr << "Check error: " << error << std::endl;
|
|
}
|
|
|
|
void DirectoryStorage::getStatistics(SharedDirStats& stats)
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx) ;
|
|
mFileHierarchy->getStatistics(stats);
|
|
}
|
|
|
|
bool DirectoryStorage::load(const std::string& local_file_name)
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx) ;
|
|
mChanged = false ;
|
|
return mFileHierarchy->load(local_file_name);
|
|
}
|
|
void DirectoryStorage::save(const std::string& local_file_name)
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx) ;
|
|
mFileHierarchy->save(local_file_name);
|
|
}
|
|
void DirectoryStorage::print()
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx) ;
|
|
mFileHierarchy->print();
|
|
}
|
|
|
|
int DirectoryStorage::searchTerms(
|
|
const std::list<std::string>& terms,
|
|
std::list<EntryIndex>& results ) const
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx) ;
|
|
return mFileHierarchy->searchTerms(terms,results);
|
|
}
|
|
int DirectoryStorage::searchBoolExp(RsRegularExpression::Expression * exp, std::list<EntryIndex> &results) const
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx) ;
|
|
return mFileHierarchy->searchBoolExp(exp,results);
|
|
}
|
|
|
|
bool DirectoryStorage::extractData(const EntryIndex& indx,DirDetails& d)
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx) ;
|
|
|
|
d.children.clear() ;
|
|
uint32_t type = mFileHierarchy->getType(indx) ;
|
|
|
|
d.ref = (void*)(intptr_t)indx ;
|
|
|
|
if (type == InternalFileHierarchyStorage::FileStorageNode::TYPE_DIR) /* has children --- fill */
|
|
{
|
|
const InternalFileHierarchyStorage::DirEntry *dir_entry = mFileHierarchy->getDirEntry(indx) ;
|
|
|
|
/* extract all the entries */
|
|
|
|
for(DirectoryStorage::DirIterator it(this,indx);it;++it)
|
|
{
|
|
DirStub stub;
|
|
stub.type = DIR_TYPE_DIR;
|
|
stub.name = it.name();
|
|
stub.ref = (void*)(intptr_t)*it; // this is updated by the caller, who knows which friend we're dealing with
|
|
|
|
d.children.push_back(stub);
|
|
}
|
|
|
|
for(DirectoryStorage::FileIterator it(this,indx);it;++it)
|
|
{
|
|
DirStub stub;
|
|
stub.type = DIR_TYPE_FILE;
|
|
stub.name = it.name();
|
|
stub.ref = (void*)(intptr_t)*it;
|
|
|
|
d.children.push_back(stub);
|
|
}
|
|
|
|
d.type = DIR_TYPE_DIR;
|
|
d.hash.clear() ;
|
|
d.size = dir_entry->dir_cumulated_size;//dir_entry->subdirs.size() + dir_entry->subfiles.size();
|
|
d.max_mtime = dir_entry->dir_most_recent_time ;
|
|
d.mtime = dir_entry->dir_modtime ;
|
|
d.name = dir_entry->dir_name;
|
|
d.path = RsDirUtil::makePath(dir_entry->dir_parent_path, dir_entry->dir_name) ;
|
|
d.parent = (void*)(intptr_t)dir_entry->parent_index ;
|
|
|
|
if(indx == 0)
|
|
{
|
|
d.type = DIR_TYPE_PERSON ;
|
|
d.name = mPeerId.toStdString();
|
|
}
|
|
}
|
|
else if(type == InternalFileHierarchyStorage::FileStorageNode::TYPE_FILE)
|
|
{
|
|
const InternalFileHierarchyStorage::FileEntry *file_entry = mFileHierarchy->getFileEntry(indx) ;
|
|
|
|
d.type = DIR_TYPE_FILE;
|
|
d.size = file_entry->file_size;
|
|
d.max_mtime = file_entry->file_modtime ;
|
|
d.name = file_entry->file_name;
|
|
d.hash = file_entry->file_hash;
|
|
d.mtime = file_entry->file_modtime;
|
|
d.parent = (void*)(intptr_t)file_entry->parent_index ;
|
|
|
|
const InternalFileHierarchyStorage::DirEntry *parent_dir_entry = mFileHierarchy->getDirEntry(file_entry->parent_index);
|
|
|
|
if(parent_dir_entry != NULL)
|
|
d.path = RsDirUtil::makePath(parent_dir_entry->dir_parent_path, parent_dir_entry->dir_name) ;
|
|
else
|
|
d.path = "" ;
|
|
}
|
|
else
|
|
return false;
|
|
|
|
d.flags.clear() ;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool DirectoryStorage::getDirHashFromIndex(const EntryIndex& index,RsFileHash& hash) const
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx) ;
|
|
return mFileHierarchy->getDirHashFromIndex(index,hash) ;
|
|
}
|
|
bool DirectoryStorage::getIndexFromDirHash(const RsFileHash& hash,EntryIndex& index) const
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx) ;
|
|
return mFileHierarchy->getIndexFromDirHash(hash,index) ;
|
|
}
|
|
|
|
void DirectoryStorage::checkSave()
|
|
{
|
|
rstime_t now = time(NULL);
|
|
|
|
if(mChanged && mLastSavedTime + MIN_INTERVAL_BETWEEN_REMOTE_DIRECTORY_SAVE < now)
|
|
{
|
|
mFileHierarchy->recursUpdateCumulatedSize(mFileHierarchy->mRoot);
|
|
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx) ;
|
|
locked_check();
|
|
}
|
|
|
|
save(mFileName);
|
|
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx) ;
|
|
mLastSavedTime = now ;
|
|
mChanged = false ;
|
|
}
|
|
}
|
|
}
|
|
/******************************************************************************************************************/
|
|
/* Local Directory Storage */
|
|
/******************************************************************************************************************/
|
|
|
|
LocalDirectoryStorage::LocalDirectoryStorage(const std::string& fname,const RsPeerId& own_id)
|
|
: DirectoryStorage(own_id,fname)
|
|
{
|
|
mTSChanged = false ;
|
|
}
|
|
|
|
RsFileHash LocalDirectoryStorage::makeEncryptedHash(const RsFileHash& hash)
|
|
{
|
|
return RsDirUtil::sha1sum(hash.toByteArray(),hash.SIZE_IN_BYTES);
|
|
}
|
|
bool LocalDirectoryStorage::locked_findRealHash(const RsFileHash& hash, RsFileHash& real_hash) const
|
|
{
|
|
std::map<RsFileHash,RsFileHash>::const_iterator it = mEncryptedHashes.find(hash) ;
|
|
|
|
if(it == mEncryptedHashes.end())
|
|
return false ;
|
|
|
|
real_hash = it->second ;
|
|
return true ;
|
|
}
|
|
|
|
int LocalDirectoryStorage::searchHash(const RsFileHash& hash, RsFileHash& real_hash, EntryIndex& result) const
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx) ;
|
|
|
|
if(locked_findRealHash(hash,real_hash) && mFileHierarchy->searchHash(real_hash,result))
|
|
return true ;
|
|
|
|
if(mFileHierarchy->searchHash(hash,result))
|
|
{
|
|
real_hash.clear();
|
|
return true ;
|
|
}
|
|
return false ;
|
|
}
|
|
|
|
void LocalDirectoryStorage::setSharedDirectoryList(
|
|
const std::list<SharedDirInfo>& lst )
|
|
{
|
|
std::set<std::string> dirs_with_new_virtualname ;
|
|
bool dirs_with_changed_flags = false ;
|
|
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx) ;
|
|
|
|
// Chose virtual name if not supplied, and remove duplicates.
|
|
|
|
std::set<std::string> virtual_names ; // maps virtual to real name
|
|
std::list<SharedDirInfo> processed_list ;
|
|
|
|
for(std::list<SharedDirInfo>::const_iterator it(lst.begin());it!= lst.end();++it)
|
|
{
|
|
int i=0;
|
|
std::string candidate_virtual_name = it->virtualname ;
|
|
|
|
if(candidate_virtual_name.empty())
|
|
candidate_virtual_name = RsDirUtil::getTopDir(it->filename);
|
|
|
|
while(virtual_names.find(candidate_virtual_name) != virtual_names.end())
|
|
rs_sprintf_append(candidate_virtual_name, "-%d", ++i);
|
|
|
|
SharedDirInfo d(*it);
|
|
d.virtualname = candidate_virtual_name ;
|
|
processed_list.push_back(d) ;
|
|
|
|
virtual_names.insert(candidate_virtual_name) ;
|
|
}
|
|
|
|
/* now for each member of the processed list, check if it is an existing
|
|
* shared directory that has been changed. If so, we need to update the
|
|
* dir TS of that directory */
|
|
|
|
std::map<std::string,SharedDirInfo> new_dirs ;
|
|
|
|
for(std::list<SharedDirInfo>::const_iterator it(processed_list.begin());it!=processed_list.end();++it)
|
|
{
|
|
std::map<std::string,SharedDirInfo>::iterator it2 = mLocalDirs.find(it->filename) ;
|
|
|
|
if(it2 != mLocalDirs.end())
|
|
{
|
|
if(it2->second.virtualname != it->virtualname)
|
|
dirs_with_new_virtualname.insert(it->filename) ;
|
|
|
|
if(!SharedDirInfo::sameLists((*it).parent_groups,it2->second.parent_groups) || (*it).shareflags != it2->second.shareflags)
|
|
dirs_with_changed_flags = true ;
|
|
}
|
|
|
|
new_dirs[it->filename] = *it;
|
|
}
|
|
|
|
mLocalDirs = new_dirs ;
|
|
mTSChanged = true ;
|
|
}
|
|
|
|
// now update the TS off-mutex.
|
|
|
|
for(DirIterator dirit(this,root());dirit;++dirit)
|
|
if(dirs_with_new_virtualname.find(dirit.name()) != dirs_with_new_virtualname.end())
|
|
{
|
|
std::cerr << "Updating TS of local dir \"" << dirit.name() << "\" with changed virtual name" << std::endl;
|
|
setDirectoryLocalModTime(*dirit,time(NULL));
|
|
}
|
|
|
|
if(dirs_with_changed_flags)
|
|
setDirectoryLocalModTime(0,time(NULL)) ;
|
|
}
|
|
|
|
void LocalDirectoryStorage::getSharedDirectoryList(std::list<SharedDirInfo>& lst)
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx) ;
|
|
|
|
lst.clear();
|
|
|
|
for(std::map<std::string,SharedDirInfo>::iterator it(mLocalDirs.begin());it!=mLocalDirs.end();++it)
|
|
lst.push_back(it->second) ;
|
|
}
|
|
void LocalDirectoryStorage::updateShareFlags(const SharedDirInfo& info)
|
|
{
|
|
bool changed = false ;
|
|
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx) ;
|
|
|
|
std::map<std::string,SharedDirInfo>::iterator it = mLocalDirs.find(info.filename) ;
|
|
|
|
if(it == mLocalDirs.end())
|
|
{
|
|
std::cerr << "(EE) LocalDirectoryStorage::updateShareFlags: directory \"" << info.filename << "\" not found" << std::endl;
|
|
return ;
|
|
}
|
|
|
|
// we compare the new info with the old one. If the two group lists have a different order, they will be seen as different. Not a big deal. We just
|
|
// want to make sure that if they are different, flags get updated.
|
|
|
|
if(!SharedDirInfo::sameLists(it->second.parent_groups,info.parent_groups) || it->second.filename != info.filename || it->second.shareflags != info.shareflags || it->second.virtualname != info.virtualname)
|
|
{
|
|
it->second = info;
|
|
|
|
#ifdef DEBUG_LOCAL_DIRECTORY_STORAGE
|
|
std::cerr << "Updating dir mod time because flags at level 0 have changed." << std::endl;
|
|
#endif
|
|
changed = true ;
|
|
}
|
|
}
|
|
|
|
if(changed)
|
|
{
|
|
setDirectoryLocalModTime(0,time(NULL)) ;
|
|
mTSChanged = true ;
|
|
}
|
|
}
|
|
|
|
bool LocalDirectoryStorage::convertSharedFilePath(const std::string& path, std::string& fullpath)
|
|
{
|
|
std::string shpath = RsDirUtil::removeRootDir(path);
|
|
std::string basedir = RsDirUtil::getRootDir(path);
|
|
std::string realroot ;
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx) ;
|
|
realroot = locked_findRealRootFromVirtualFilename(basedir);
|
|
}
|
|
|
|
if (realroot.empty())
|
|
return false;
|
|
|
|
/* construct full name */
|
|
fullpath = realroot + "/";
|
|
fullpath += shpath;
|
|
|
|
return true;
|
|
}
|
|
|
|
void LocalDirectoryStorage::notifyTSChanged()
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx) ;
|
|
mTSChanged = true ;
|
|
}
|
|
void LocalDirectoryStorage::updateTimeStamps()
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx) ;
|
|
|
|
if(mTSChanged)
|
|
{
|
|
#ifdef DEBUG_LOCAL_DIRECTORY_STORAGE
|
|
std::cerr << "Updating recursive TS for local shared dirs..." << std::endl;
|
|
#endif
|
|
|
|
bool unfinished_files_below ;
|
|
|
|
rstime_t last_modf_time = mFileHierarchy->recursUpdateLastModfTime(EntryIndex(0),unfinished_files_below) ;
|
|
mTSChanged = false ;
|
|
|
|
#ifdef DEBUG_LOCAL_DIRECTORY_STORAGE
|
|
std::cerr << "LocalDirectoryStorage: global last modf time is " << last_modf_time << " (which is " << time(NULL) - last_modf_time << " secs ago), unfinished files below=" << unfinished_files_below << std::endl;
|
|
#else
|
|
// remove unused variable warning
|
|
// variable is only used for debugging
|
|
(void)last_modf_time;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
bool LocalDirectoryStorage::updateHash(
|
|
const EntryIndex& index, const RsFileHash& hash,
|
|
bool update_internal_hierarchy )
|
|
{
|
|
bool ret = false;
|
|
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx);
|
|
|
|
mEncryptedHashes[makeEncryptedHash(hash)] = hash ;
|
|
mChanged = true ;
|
|
|
|
#ifdef DEBUG_LOCAL_DIRECTORY_STORAGE
|
|
std::cerr << "Updating index of hash " << hash << " update_internal="
|
|
<< update_internal_hierarchy << std::endl;
|
|
#endif
|
|
|
|
ret = (!update_internal_hierarchy) ||
|
|
mFileHierarchy->updateHash(index, hash);
|
|
} // RS_STACK_MUTEX(mDirStorageMtx);
|
|
|
|
#ifdef RS_DEEP_FILES_INDEX
|
|
FileInfo fInfo;
|
|
if( ret && getFileInfo(index, fInfo) &&
|
|
fInfo.storage_permission_flags & DIR_FLAGS_ANONYMOUS_SEARCH )
|
|
{
|
|
DeepFilesIndex dfi(DeepFilesIndex::dbDefaultPath());
|
|
ret &= !dfi.indexFile(fInfo.path, fInfo.fname, hash);
|
|
}
|
|
#endif // def RS_DEEP_FILES_INDEX
|
|
|
|
return ret;
|
|
}
|
|
std::string LocalDirectoryStorage::locked_findRealRootFromVirtualFilename(const std::string& virtual_rootdir) const
|
|
{
|
|
/**** MUST ALREADY BE LOCKED ****/
|
|
|
|
std::map<std::string, SharedDirInfo>::const_iterator cit = mLocalDirs.find(virtual_rootdir) ;
|
|
|
|
if (cit == mLocalDirs.end())
|
|
{
|
|
std::cerr << "(EE) locked_findRealRootFromVirtualFilename() Invalid RootDir: " << virtual_rootdir << std::endl;
|
|
return std::string();
|
|
}
|
|
return cit->second.filename;
|
|
}
|
|
|
|
bool LocalDirectoryStorage::extractData(const EntryIndex& indx,DirDetails& d)
|
|
{
|
|
bool res = DirectoryStorage::extractData(indx,d) ;
|
|
|
|
if(!res)
|
|
return false;
|
|
|
|
// here we should update the file sharing flags
|
|
|
|
return getFileSharingPermissions(indx,d.flags,d.parent_groups) ;
|
|
}
|
|
|
|
bool LocalDirectoryStorage::getFileInfo(DirectoryStorage::EntryIndex i,FileInfo& info)
|
|
{
|
|
DirDetails d;
|
|
extractData(i,d) ;
|
|
|
|
if(d.type != DIR_TYPE_FILE)
|
|
{
|
|
std::cerr << "(EE) LocalDirectoryStorage: asked for file info for index " << i << " which is not a file." << std::endl;
|
|
return false;
|
|
}
|
|
|
|
info.storage_permission_flags = d.flags; // Combination of the four RS_DIR_FLAGS_*. Updated when the file is a local stored file.
|
|
info.parent_groups = d.parent_groups;
|
|
info.transfer_info_flags = TransferRequestFlags(); // various flags from RS_FILE_HINTS_*
|
|
info.path = d.path + "/" + d.name;
|
|
info.fname = d.name;
|
|
info.hash = d.hash;
|
|
info.size = d.size;
|
|
|
|
// all this stuff below is not useful in this case.
|
|
|
|
info.mId = 0; /* (GUI) Model Id -> unique number */
|
|
info.ext.clear();
|
|
info.avail = 0; /* how much we have */
|
|
info.rank = 0;
|
|
info.age = 0;
|
|
info.queue_position =0;
|
|
info.searchId = 0; /* 0 if none */
|
|
|
|
/* Transfer Stuff */
|
|
info.transfered = 0;
|
|
info.tfRate = 0; /* in kbytes */
|
|
info.downloadStatus = FT_STATE_COMPLETE ;
|
|
//std::list<TransferInfo> peers;
|
|
|
|
info.priority = SPEED_NORMAL;
|
|
info.lastTS = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool LocalDirectoryStorage::getFileSharingPermissions(const EntryIndex& indx,FileStorageFlags& flags,std::list<RsNodeGroupId>& parent_groups)
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx) ;
|
|
return locked_getFileSharingPermissions(indx,flags,parent_groups) ;
|
|
}
|
|
|
|
bool LocalDirectoryStorage::locked_getFileSharingPermissions(
|
|
const EntryIndex& indx, FileStorageFlags& flags,
|
|
std::list<RsNodeGroupId>& parent_groups )
|
|
{
|
|
flags.clear();
|
|
parent_groups.clear();
|
|
|
|
/* We got a request for root directory no need to do anything more after
|
|
* clearing outputs */
|
|
if(!indx) return true;
|
|
|
|
using FileStorageNode = InternalFileHierarchyStorage::FileStorageNode;
|
|
using EntryIndex = DirectoryStorage::EntryIndex;
|
|
|
|
rs_view_ptr<const FileStorageNode> n = mFileHierarchy->getNode(indx);
|
|
if(!n)
|
|
{
|
|
RS_ERR("Node for index: ", indx, "not found");
|
|
print_stacktrace();
|
|
return false;
|
|
}
|
|
|
|
// Climb down node tree up to root + 1
|
|
EntryIndex curIndex = indx;
|
|
while (n->parent_index)
|
|
{
|
|
curIndex = n->parent_index;
|
|
n = mFileHierarchy->getNode(curIndex);
|
|
}
|
|
|
|
// Retrieve base name
|
|
std::string tBaseName;
|
|
switch (n->type())
|
|
{
|
|
// Handle single file shared case
|
|
case InternalFileHierarchyStorage::FileStorageNode::TYPE_FILE:
|
|
tBaseName = mFileHierarchy->getFileEntry(curIndex)->file_name;
|
|
break;
|
|
// Handle shared directory case
|
|
case InternalFileHierarchyStorage::FileStorageNode::TYPE_DIR:
|
|
tBaseName = mFileHierarchy->getDirEntry(curIndex)->dir_name;
|
|
break;
|
|
default:
|
|
RS_ERR("Got unhandled node type: ", n->type());
|
|
print_stacktrace();
|
|
return false;
|
|
}
|
|
|
|
// Use base name to retrieve sharing permissions
|
|
if(!tBaseName.empty())
|
|
{
|
|
auto it = std::as_const(mLocalDirs).find(tBaseName);
|
|
|
|
if(it == mLocalDirs.end())
|
|
{
|
|
RS_ERR( "base name \"", tBaseName,
|
|
"\" for index: ", indx, " not found in shared dir list." );
|
|
print_stacktrace();
|
|
return false;
|
|
}
|
|
|
|
flags = it->second.shareflags;
|
|
parent_groups = it->second.parent_groups;
|
|
}
|
|
else
|
|
{
|
|
RS_ERR("base name for indx: ", indx, " is empty");
|
|
print_stacktrace();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
std::string LocalDirectoryStorage::locked_getVirtualDirName(EntryIndex indx) const
|
|
{
|
|
if(indx == 0)
|
|
return std::string() ;
|
|
|
|
const InternalFileHierarchyStorage::DirEntry *dir = mFileHierarchy->getDirEntry(indx);
|
|
|
|
if(dir->parent_index != 0)
|
|
return dir->dir_name ;
|
|
|
|
std::map<std::string,SharedDirInfo>::const_iterator it = mLocalDirs.find(dir->dir_name) ;
|
|
|
|
if(it == mLocalDirs.end())
|
|
{
|
|
std::cerr << "(EE) Cannot find real name " << dir->dir_name << " at level 1 among shared dirs. Bug?" << std::endl;
|
|
return std::string() ;
|
|
}
|
|
|
|
return it->second.virtualname ;
|
|
}
|
|
std::string LocalDirectoryStorage::locked_getVirtualPath(EntryIndex indx) const
|
|
{
|
|
if(indx == 0)
|
|
return std::string() ;
|
|
|
|
std::string res ;
|
|
const InternalFileHierarchyStorage::DirEntry *dir = mFileHierarchy->getDirEntry(indx);
|
|
|
|
while(dir->parent_index != 0)
|
|
{
|
|
dir = mFileHierarchy->getDirEntry(dir->parent_index) ;
|
|
res += dir->dir_name + "/"+ res ;
|
|
}
|
|
|
|
std::map<std::string,SharedDirInfo>::const_iterator it = mLocalDirs.find(dir->dir_name) ;
|
|
|
|
if(it == mLocalDirs.end())
|
|
{
|
|
std::cerr << "(EE) Cannot find real name " << dir->dir_name << " at level 1 among shared dirs. Bug?" << std::endl;
|
|
return std::string() ;
|
|
}
|
|
return it->second.virtualname + "/" + res;
|
|
}
|
|
|
|
bool LocalDirectoryStorage::serialiseDirEntry(
|
|
const EntryIndex& indx, RsTlvBinaryData& bindata,
|
|
const RsPeerId& client_id )
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx);
|
|
|
|
const InternalFileHierarchyStorage::DirEntry* dir =
|
|
mFileHierarchy->getDirEntry(indx);
|
|
|
|
#ifdef DEBUG_LOCAL_DIRECTORY_STORAGE
|
|
std::cerr << "Serialising Dir entry " << std::hex << indx << " for client id " << client_id << std::endl;
|
|
#endif
|
|
|
|
if(!dir)
|
|
{
|
|
RS_ERR("Cannot find entry ", indx);
|
|
return false;
|
|
}
|
|
|
|
// compute list of allowed subdirs
|
|
std::vector<RsFileHash> allowed_subdirs;
|
|
FileStorageFlags node_flags;
|
|
std::list<RsNodeGroupId> node_groups;
|
|
|
|
/* for each subdir, compute the node flags and groups, then ask rsPeers to
|
|
* compute the mask that result from these flags for the particular peer
|
|
* supplied in parameter */
|
|
|
|
for(uint32_t i=0;i<dir->subdirs.size();++i)
|
|
if(indx != 0 || (
|
|
locked_getFileSharingPermissions(
|
|
dir->subdirs[i], node_flags, node_groups ) &&
|
|
( rsPeers->computePeerPermissionFlags(
|
|
client_id, node_flags, node_groups ) &
|
|
RS_FILE_HINTS_BROWSABLE ) ))
|
|
{
|
|
RsFileHash hash;
|
|
if(!mFileHierarchy->getDirHashFromIndex(dir->subdirs[i],hash))
|
|
{
|
|
RS_ERR( "Cannot get hash from subdir index: ",
|
|
dir->subdirs[i], ". Weird bug." );
|
|
print_stacktrace();
|
|
return false;
|
|
}
|
|
allowed_subdirs.push_back(hash);
|
|
|
|
#ifdef DEBUG_LOCAL_DIRECTORY_STORAGE
|
|
std::cerr << " pushing subdir " << hash << ", array position=" << i << " indx=" << dir->subdirs[i] << std::endl;
|
|
#endif
|
|
}
|
|
#ifdef DEBUG_LOCAL_DIRECTORY_STORAGE
|
|
else
|
|
std::cerr << " not pushing subdir " << hash << ", array position=" << i << " indx=" << dir->subdirs[i] << ": permission denied for this peer." << std::endl;
|
|
#endif
|
|
|
|
/* now count the files that do not have a null hash (meaning the hash has
|
|
* indeed been computed), also in case files are shared singularly (without
|
|
* a shared directory) so they are child of root check browsability
|
|
* permission */
|
|
uint32_t allowed_subfiles = 0;
|
|
for(uint32_t i=0; i<dir->subfiles.size(); ++i)
|
|
{
|
|
const InternalFileHierarchyStorage::FileEntry* file =
|
|
mFileHierarchy->getFileEntry(dir->subfiles[i]);
|
|
if(file != nullptr && !file->file_hash.isNull()
|
|
&& ( indx !=0 || (
|
|
locked_getFileSharingPermissions(
|
|
dir->subfiles[i], node_flags, node_groups ) &&
|
|
rsPeers->computePeerPermissionFlags(
|
|
client_id, node_flags, node_groups ) &
|
|
RS_FILE_HINTS_BROWSABLE ) ))
|
|
allowed_subfiles++;
|
|
}
|
|
|
|
unsigned char* section_data = (unsigned char *)
|
|
rs_malloc(FL_BASE_TMP_SECTION_SIZE);
|
|
if(!section_data) return false;
|
|
|
|
uint32_t section_size = FL_BASE_TMP_SECTION_SIZE;
|
|
uint32_t section_offset = 0;
|
|
|
|
/* we need to send:
|
|
* - the name of the directory, its TS
|
|
* - the index entry for each subdir (the updte TS are exchanged at a
|
|
* higher level)
|
|
* - the file info for each subfile */
|
|
|
|
std::string virtual_dir_name = locked_getVirtualDirName(indx);
|
|
|
|
/* Manual serialization AGAIN! This is terrible and should be ported to the
|
|
* new serialization system ASAP! */
|
|
if(!FileListIO::writeField(
|
|
section_data, section_size, section_offset,
|
|
FILE_LIST_IO_TAG_DIR_NAME, virtual_dir_name ))
|
|
{ free(section_data); return false; }
|
|
if(!FileListIO::writeField(
|
|
section_data, section_size, section_offset,
|
|
FILE_LIST_IO_TAG_RECURS_MODIF_TS,
|
|
(uint32_t)dir->dir_most_recent_time ))
|
|
{ free(section_data); return false; }
|
|
if(!FileListIO::writeField(
|
|
section_data, section_size, section_offset,
|
|
FILE_LIST_IO_TAG_MODIF_TS, (uint32_t)dir->dir_modtime ))
|
|
{ free(section_data); return false;}
|
|
|
|
// serialise number of subdirs and number of subfiles
|
|
if(!FileListIO::writeField(
|
|
section_data, section_size, section_offset,
|
|
FILE_LIST_IO_TAG_RAW_NUMBER, (uint32_t)allowed_subdirs.size() ))
|
|
{ free(section_data); return false; }
|
|
if(!FileListIO::writeField(
|
|
section_data, section_size, section_offset,
|
|
FILE_LIST_IO_TAG_RAW_NUMBER, (uint32_t)allowed_subfiles ))
|
|
{ free(section_data); return false; }
|
|
|
|
// serialise subdirs entry indexes
|
|
for(uint32_t i=0; i<allowed_subdirs.size(); ++i)
|
|
if(!FileListIO::writeField(
|
|
section_data, section_size, section_offset,
|
|
FILE_LIST_IO_TAG_ENTRY_INDEX, allowed_subdirs[i] ))
|
|
{ free(section_data); return false; }
|
|
|
|
// serialise directory subfiles, with info for each of them
|
|
|
|
unsigned char* file_section_data =
|
|
(unsigned char *) rs_malloc(FL_BASE_TMP_SECTION_SIZE);
|
|
if(!file_section_data)
|
|
{
|
|
free(section_data);
|
|
return false;
|
|
}
|
|
|
|
uint32_t file_section_size = FL_BASE_TMP_SECTION_SIZE;
|
|
|
|
for(uint32_t i=0; i<dir->subfiles.size(); ++i)
|
|
{
|
|
uint32_t file_section_offset = 0;
|
|
|
|
const InternalFileHierarchyStorage::FileEntry* file =
|
|
mFileHierarchy->getFileEntry(dir->subfiles[i]);
|
|
|
|
if(file == nullptr || file->file_hash.isNull())
|
|
{
|
|
RS_INFO( "skipping unhashed or Null file entry ",
|
|
dir->subfiles[i], " to get/send file info." );
|
|
continue;
|
|
}
|
|
|
|
if(indx == 0)
|
|
{
|
|
if(!locked_getFileSharingPermissions(
|
|
dir->subfiles[i], node_flags, node_groups ))
|
|
{
|
|
RS_ERR( "Failure getting sharing permission for single file: ",
|
|
dir->subfiles[i] );
|
|
print_stacktrace();
|
|
continue;
|
|
}
|
|
|
|
if(!( rsPeers->computePeerPermissionFlags(
|
|
client_id, node_flags, node_groups ) &
|
|
RS_FILE_HINTS_BROWSABLE ))
|
|
{
|
|
RS_INFO( "Skipping single file shared without browse "
|
|
"permission" );
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if(!FileListIO::writeField(
|
|
file_section_data, file_section_size, file_section_offset,
|
|
FILE_LIST_IO_TAG_FILE_NAME, file->file_name ))
|
|
{ free(section_data); free(file_section_data); return false; }
|
|
if(!FileListIO::writeField(
|
|
file_section_data, file_section_size, file_section_offset,
|
|
FILE_LIST_IO_TAG_FILE_SIZE, file->file_size ))
|
|
{ free(section_data); free(file_section_data); return false; }
|
|
if(!FileListIO::writeField(
|
|
file_section_data, file_section_size, file_section_offset,
|
|
FILE_LIST_IO_TAG_FILE_SHA1_HASH, file->file_hash ))
|
|
{ free(section_data); free(file_section_data); return false; }
|
|
if(!FileListIO::writeField(
|
|
file_section_data, file_section_size, file_section_offset,
|
|
FILE_LIST_IO_TAG_MODIF_TS, (uint32_t)file->file_modtime ))
|
|
{ free(section_data); free(file_section_data); return false; }
|
|
|
|
// now write the whole string into a single section in the file
|
|
if(!FileListIO::writeField(
|
|
section_data, section_size, section_offset,
|
|
FILE_LIST_IO_TAG_REMOTE_FILE_ENTRY,
|
|
file_section_data, file_section_offset ))
|
|
{ free(section_data); free(file_section_data); return false; }
|
|
|
|
#ifdef DEBUG_LOCAL_DIRECTORY_STORAGE
|
|
std::cerr << " pushing subfile " << file->hash << ", array position=" << i << " indx=" << dir->subfiles[i] << std::endl;
|
|
#endif
|
|
}
|
|
free(file_section_data);
|
|
|
|
#ifdef DEBUG_LOCAL_DIRECTORY_STORAGE
|
|
std::cerr << "Serialised dir entry to send for entry index " << (void*)(intptr_t)indx << ". Data size is " << section_size << " bytes" << std::endl;
|
|
#endif
|
|
|
|
// Discards the possibly unused trailing bytes in the end of section_data
|
|
bindata.bin_data = realloc(section_data,section_offset);
|
|
bindata.bin_len = section_offset;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/******************************************************************************************************************/
|
|
/* Remote Directory Storage */
|
|
/******************************************************************************************************************/
|
|
|
|
RemoteDirectoryStorage::RemoteDirectoryStorage(const RsPeerId& pid,const std::string& fname)
|
|
: DirectoryStorage(pid,fname)
|
|
{
|
|
mLastSweepTime = time(NULL) - (RSRandom::random_u32() % DELAY_BETWEEN_REMOTE_DIRECTORIES_SWEEP) ;
|
|
|
|
std::cerr << "Loaded remote directory for peer " << pid << ", inited last sweep time to " << time(NULL) - mLastSweepTime << " secs ago." << std::endl;
|
|
#ifdef DEBUG_REMOTE_DIRECTORY_STORAGE
|
|
mFileHierarchy->print();
|
|
#endif
|
|
}
|
|
|
|
bool RemoteDirectoryStorage::deserialiseUpdateDirEntry(const EntryIndex& indx,const RsTlvBinaryData& bindata)
|
|
{
|
|
const unsigned char *section_data = (unsigned char*)bindata.bin_data ;
|
|
uint32_t section_size = bindata.bin_len ;
|
|
uint32_t section_offset=0 ;
|
|
|
|
#ifdef DEBUG_REMOTE_DIRECTORY_STORAGE
|
|
std::cerr << "RemoteDirectoryStorage::deserialiseDirEntry(): deserialising directory content for friend " << peerId() << ", and directory " << indx << std::endl;
|
|
#endif
|
|
|
|
std::string dir_name ;
|
|
uint32_t most_recent_time ,dir_modtime ;
|
|
|
|
if(!FileListIO::readField(section_data,section_size,section_offset,FILE_LIST_IO_TAG_DIR_NAME ,dir_name )) return false ;
|
|
if(!FileListIO::readField(section_data,section_size,section_offset,FILE_LIST_IO_TAG_RECURS_MODIF_TS,most_recent_time)) return false ;
|
|
if(!FileListIO::readField(section_data,section_size,section_offset,FILE_LIST_IO_TAG_MODIF_TS ,dir_modtime )) return false ;
|
|
|
|
#ifdef DEBUG_REMOTE_DIRECTORY_STORAGE
|
|
std::cerr << " dir name : \"" << dir_name << "\"" << std::endl;
|
|
std::cerr << " most recent time : " << most_recent_time << std::endl;
|
|
std::cerr << " modification time : " << dir_modtime << std::endl;
|
|
#endif
|
|
|
|
// serialise number of subdirs and number of subfiles
|
|
|
|
uint32_t n_subdirs,n_subfiles ;
|
|
|
|
if(!FileListIO::readField(section_data,section_size,section_offset,FILE_LIST_IO_TAG_RAW_NUMBER,n_subdirs )) return false ;
|
|
if(!FileListIO::readField(section_data,section_size,section_offset,FILE_LIST_IO_TAG_RAW_NUMBER,n_subfiles )) return false ;
|
|
|
|
#ifdef DEBUG_REMOTE_DIRECTORY_STORAGE
|
|
std::cerr << " number of subdirs : " << n_subdirs << std::endl;
|
|
std::cerr << " number of files : " << n_subfiles << std::endl;
|
|
#endif
|
|
|
|
// serialise subdirs entry indexes
|
|
|
|
std::vector<RsFileHash> subdirs_hashes ;
|
|
RsFileHash subdir_hash ;
|
|
|
|
for(uint32_t i=0;i<n_subdirs;++i)
|
|
{
|
|
if(!FileListIO::readField(section_data,section_size,section_offset,FILE_LIST_IO_TAG_ENTRY_INDEX ,subdir_hash)) return false ;
|
|
|
|
subdirs_hashes.push_back(subdir_hash) ;
|
|
}
|
|
|
|
// deserialise directory subfiles, with info for each of them
|
|
|
|
std::vector<InternalFileHierarchyStorage::FileEntry> subfiles_array ;
|
|
|
|
// Pre-allocate file_section_data, so that read_field does not need to do it many times.
|
|
|
|
unsigned char *file_section_data = (unsigned char *)rs_malloc(FL_BASE_TMP_SECTION_SIZE) ;
|
|
|
|
if(!file_section_data)
|
|
return false ;
|
|
|
|
uint32_t file_section_size = FL_BASE_TMP_SECTION_SIZE ;
|
|
|
|
for(uint32_t i=0;i<n_subfiles;++i)
|
|
{
|
|
// Read the full data section for the file
|
|
|
|
if(!FileListIO::readField(section_data,section_size,section_offset,FILE_LIST_IO_TAG_REMOTE_FILE_ENTRY,file_section_data,file_section_size)) { free(file_section_data); return false ; }
|
|
|
|
uint32_t file_section_offset = 0 ;
|
|
|
|
InternalFileHierarchyStorage::FileEntry f;
|
|
uint32_t modtime =0;
|
|
|
|
if(!FileListIO::readField(file_section_data,file_section_size,file_section_offset,FILE_LIST_IO_TAG_FILE_NAME ,f.file_name )) { free(file_section_data); return false ; }
|
|
if(!FileListIO::readField(file_section_data,file_section_size,file_section_offset,FILE_LIST_IO_TAG_FILE_SIZE ,f.file_size )) { free(file_section_data); return false ; }
|
|
if(!FileListIO::readField(file_section_data,file_section_size,file_section_offset,FILE_LIST_IO_TAG_FILE_SHA1_HASH,f.file_hash )) { free(file_section_data); return false ; }
|
|
if(!FileListIO::readField(file_section_data,file_section_size,file_section_offset,FILE_LIST_IO_TAG_MODIF_TS ,modtime )) { free(file_section_data); return false ; }
|
|
|
|
f.file_modtime = modtime ;
|
|
|
|
subfiles_array.push_back(f) ;
|
|
}
|
|
free(file_section_data) ;
|
|
|
|
RS_STACK_MUTEX(mDirStorageMtx) ;
|
|
#ifdef DEBUG_REMOTE_DIRECTORY_STORAGE
|
|
std::cerr << " updating dir entry..." << std::endl;
|
|
#endif
|
|
|
|
// First create the entries for each subdir and each subfile, if needed.
|
|
if(!mFileHierarchy->updateDirEntry(indx,dir_name,most_recent_time,dir_modtime,subdirs_hashes,subfiles_array))
|
|
{
|
|
std::cerr << "(EE) Cannot update dir entry with index " << indx << ": entry does not exist." << std::endl;
|
|
return false ;
|
|
}
|
|
|
|
mChanged = true ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
int RemoteDirectoryStorage::searchHash(const RsFileHash& hash, EntryIndex& result) const
|
|
{
|
|
RS_STACK_MUTEX(mDirStorageMtx) ;
|
|
|
|
return mFileHierarchy->searchHash(hash,result);
|
|
}
|
|
|
|
|
|
|