/******************************************************************************* * libretroshare/src/util: folderiterator.cc * * * * libretroshare: retroshare core library * * * * Copyright (C) 2017 Retroshare Team * * * * 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 . * * * *******************************************************************************/ #include #include #include #include #include #include "util/rstime.h" #include "util/folderiterator.h" #include "util/rsstring.h" #include "util/largefile_retrocompat.hpp" #ifdef WINDOWS_SYS #include "util/rswin.h" #endif //#define DEBUG_FOLDER_ITERATOR 1 namespace librs { namespace util { FolderIterator::FolderIterator( const std::string& folderName, bool allow_symlinks, bool allow_files_from_the_future ): mFolderName(folderName), mAllowSymLinks(allow_symlinks), mAllowFilesFromTheFuture(allow_files_from_the_future) { is_open = false ; validity = false ; mFileModTime = 0; mFolderModTime = 0; mFileSize = 0; mType = TYPE_UNKNOWN; // Grab the last modification time for the directory struct stat64 buf ; #ifdef WINDOWS_SYS std::wstring wfullname; librs::util::ConvertUtf8ToUtf16(folderName, wfullname); if ( 0 == _wstati64(wfullname.c_str(), &buf)) #else if ( 0 == stat64(folderName.c_str(), &buf)) #endif { mFolderModTime = buf.st_mtime ; } // Now open directory content and read the first entry #ifdef WINDOWS_SYS std::wstring utf16Name; if(! ConvertUtf8ToUtf16(folderName, utf16Name)) { validity = false; return; } utf16Name += L"/*.*"; // FindFirstFileW does the "next" operation, so after calling it, we need to read the information // for the current entry. handle = FindFirstFileW(utf16Name.c_str(), &fileInfo); is_open = validity = handle != INVALID_HANDLE_VALUE; bool should_skip ; validity = validity && updateFileInfo(should_skip); if(validity && should_skip) next(); #else // On linux, we need to call "next()" once the dir is openned. next() will call updateFileInfo() itself. handle = opendir(folderName.c_str()); is_open = validity = handle != NULL; next(); #endif } FolderIterator::~FolderIterator() { closedir(); } void FolderIterator::next() { bool should_skip = false ; while(readdir()) if(updateFileInfo(should_skip) && !should_skip) return ; #ifdef DEBUG_FOLDER_ITERATOR std::cerr << "End of directory." << std::endl; #endif mType = TYPE_UNKNOWN ; mFileSize = 0 ; mFileModTime = 0; validity = false ; } bool FolderIterator::updateFileInfo(bool& should_skip) { should_skip = false; #ifdef WINDOWS_SYS ConvertUtf16ToUtf8(fileInfo.cFileName, mFileName) ; #else mFileName = ent->d_name ; #endif if(mFileName == "." || mFileName == "..") { should_skip = true ; return true ; } mFullPath = mFolderName + "/" + mFileName ; #warning cyril soler: should we take care of symbolic links on windows? #ifndef WINDOWS_SYS if( ent->d_type == DT_LNK) { if(!mAllowSymLinks) { std::cerr << "(II) Skipping symbolic link " << mFullPath << std::endl; should_skip = true ; return true ; } } else if( ent->d_type != DT_DIR && ent->d_type != DT_REG && ent->d_type != DT_UNKNOWN) // DT_UNKNOWN is reported by mounted dirs, by services such as sshfs. { std::cerr << "(II) Skipping file of unknown type " << std::dec << (int)ent->d_type << std::dec << ": " << mFullPath << std::endl; should_skip = true ; return true ; } #endif struct stat64 buf ; #ifdef DEBUG_FOLDER_ITERATOR std::cerr << "FolderIterator: next. Looking into file " << mFileName ; #endif #ifdef WINDOWS_SYS std::wstring wfullname; librs::util::ConvertUtf8ToUtf16(mFullPath, wfullname); if ( 0 == _wstati64(wfullname.c_str(), &buf)) #else if ( 0 == stat64(mFullPath.c_str(), &buf)) #endif { mFileModTime = buf.st_mtime ; if(buf.st_mtime > time(NULL) && !mAllowFilesFromTheFuture) { std::cerr << "(II) skipping file with modification time in the future: " << mFullPath << std::endl; should_skip = true ; return true ; } if (S_ISDIR(buf.st_mode)) { #ifdef DEBUG_FOLDER_ITERATOR std::cerr << ": is a directory" << std::endl; #endif mType = TYPE_DIR ; mFileSize = 0 ; mFileModTime = buf.st_mtime; return true; } if (S_ISREG(buf.st_mode)) { #ifdef DEBUG_FOLDER_ITERATOR std::cerr << ": is a file" << std::endl; #endif mType = TYPE_FILE ; mFileSize = buf.st_size; mFileModTime = buf.st_mtime; return true; } } #ifdef DEBUG_FOLDER_ITERATOR std::cerr << ": is unknown skipping" << std::endl; #endif mType = TYPE_UNKNOWN ; mFileSize = 0 ; mFileModTime = 0; return false; } bool FolderIterator::readdir() { if(!validity) return false; #ifdef WINDOWS_SYS return FindNextFileW(handle, &fileInfo) != 0; #else ent = ::readdir(handle); return ent != NULL; #endif } rstime_t FolderIterator::dir_modtime() const { return mFolderModTime ; } const std::string& FolderIterator::file_fullpath() { return mFullPath ; } const std::string& FolderIterator::file_name() { return mFileName ; } uint64_t FolderIterator::file_size() { return mFileSize ; } rstime_t FolderIterator::file_modtime() { return mFileModTime ; } uint8_t FolderIterator::file_type() { return mType ; } bool FolderIterator::closedir() { validity = false; if(!is_open) return true ; is_open = false ; #ifdef WINDOWS_SYS return FindClose(handle) != 0; #else return ::closedir(handle) == 0; #endif } } } // librs::util