├── tools ├── test_po.sh ├── clean_build.sh ├── test_build.sh └── update_po.sh ├── kernel-patches ├── ext4-fix-defrag-for-running-executable.patch ├── delalloc-debug-ioctl ├── ext4_sort_and_merge_inode_pa.patch ├── ext4_add_ext4_ioc_get_pa.patch └── ext4_add_ext4_ioc_control_pa.patch ├── src ├── cmake │ ├── Findaudit.cmake │ ├── Findblkid.cmake │ ├── Findext2fs.cmake │ └── Findauparse.cmake ├── intl.hh ├── eventcatcher.hh ├── config.h ├── parsefilelist.hh ├── common.hh ├── buddycache.hh ├── defrag.hh ├── fileptr.hh ├── singleton.hh ├── balloc.h ├── logging.hh ├── signals.hh ├── buddycache.cc ├── listener.hh ├── device.hh ├── logging.cc ├── fiemap.hh ├── fileptr.cc ├── e4rat-offsets.cc ├── config.c ├── eventcatcher.cc ├── fiemap.cc ├── CMakeLists.txt ├── e4rat-realloc.cc ├── e4rat-preload.c ├── common.cc ├── device.cc └── e4rat-collect.cc ├── e4rat-lite.conf ├── po └── CMakeLists.txt ├── CMakeLists.txt ├── doc ├── en_US │ ├── e4rat-lite-preload.pod │ ├── e4rat-lite.conf.pod │ ├── e4rat-lite-realloc.pod │ └── e4rat-lite-collect.pod ├── pt_BR │ ├── e4rat-lite-preload.pod │ ├── e4rat-lite.conf.pod │ ├── e4rat-lite-realloc.pod │ └── e4rat-lite-collect.pod └── CMakeLists.txt └── README.md /tools/test_po.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd .. 3 | if [ `id -u` != 0 ]; then exit 1; fi 4 | if [ "$1" == "" ]; then exit 1; fi 5 | msgfmt --check po/${1}.po 6 | msgfmt --statistics po/${1}.po 7 | msgfmt -o /usr/share/locale/${1}/LC_MESSAGES/e4rat-lite.mo po/${1}.po 8 | exit 0 9 | -------------------------------------------------------------------------------- /tools/clean_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd .. 3 | rm -fv *.a 4 | rm -fv e4rat-lite-* 5 | rm -fv libe4rat-lite-* 6 | rm -rfv build 7 | potd=(`find doc/ -type d -exec basename {} \;`) 8 | for ((i=1;i<${#potd[@]};i++)); do 9 | rm -fv doc/${potd[$i]}/*.8 10 | rm -fv doc/${potd[$i]}/*.5 11 | done 12 | exit 0 13 | -------------------------------------------------------------------------------- /kernel-patches/ext4-fix-defrag-for-running-executable.patch: -------------------------------------------------------------------------------- 1 | diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c 2 | index 808c554..4afb59c 100644 3 | --- a/fs/ext4/ioctl.c 4 | +++ b/fs/ext4/ioctl.c 5 | @@ -230,8 +230,7 @@ setversion_out: 6 | struct file *donor_filp; 7 | int err; 8 | 9 | - if (!(filp->f_mode & FMODE_READ) || 10 | - !(filp->f_mode & FMODE_WRITE)) 11 | + if (inode_permission(inode, MAY_READ | MAY_WRITE) 12 | return -EBADF; 13 | 14 | if (copy_from_user(&me, 15 | -------------------------------------------------------------------------------- /tools/test_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd .. 3 | if [ "$1" == "" ]; then type=debug; else type=$1; fi 4 | if [ "$2" == "global" ] && [ `id -u` != 0 ]; then exit 1; fi 5 | if [ -e build ]; then rm -rfv build; fi 6 | mkdir build && cd build 7 | echo "---~---" 8 | cmake .. -DCMAKE_BUILD_TYPE=$type -DCMAKE_INSTALL_PREFIX="/usr" 9 | echo "---~---" 10 | make -j5 11 | echo "---~---" 12 | if [ "$2" != "global" ]; then 13 | mkdir test 14 | make DESTDIR="test" install 15 | echo "---~---" 16 | ls test/ 17 | else 18 | make install 19 | mandb 20 | fi 21 | echo "---~---" 22 | ls 23 | echo "---~---" 24 | 25 | exit 0 26 | -------------------------------------------------------------------------------- /src/cmake/Findaudit.cmake: -------------------------------------------------------------------------------- 1 | FIND_PATH(AUDIT_INCLUDE_DIR libaudit.h /usr/include 2 | /usr/local/include) 3 | 4 | FIND_LIBRARY(AUDIT_LIBRARY NAMES audit PATH /usr/lib /usr/local/lib) 5 | 6 | IF (AUDIT_INCLUDE_DIR AND AUDIT_LIBRARY) 7 | SET(AUDIT_FOUND TRUE) 8 | ENDIF (AUDIT_INCLUDE_DIR AND AUDIT_LIBRARY) 9 | 10 | 11 | IF (AUDIT_FOUND) 12 | IF (NOT audit_FIND_QUIETLY) 13 | MESSAGE(STATUS "Found audit: ${AUDIT_LIBRARY}") 14 | ENDIF (NOT audit_FIND_QUIETLY) 15 | ELSE (AUDIT_FOUND) 16 | IF (audit_FIND_REQUIRED) 17 | MESSAGE(FATAL_ERROR "Could not find audit") 18 | ENDIF (audit_FIND_REQUIRED) 19 | ENDIF (AUDIT_FOUND) 20 | -------------------------------------------------------------------------------- /src/cmake/Findblkid.cmake: -------------------------------------------------------------------------------- 1 | FIND_PATH(BLKID_INCLUDE_DIR blkid.h /usr/include/blkid 2 | /usr/local/include/blkid) 3 | 4 | FIND_LIBRARY(BLKID_LIBRARY NAMES blkid PATH /usr/lib /usr/local/lib) 5 | 6 | IF (BLKID_INCLUDE_DIR AND BLKID_LIBRARY) 7 | SET(BLKID_FOUND TRUE) 8 | ENDIF (BLKID_INCLUDE_DIR AND BLKID_LIBRARY) 9 | 10 | 11 | IF (BLKID_FOUND) 12 | IF (NOT blkid_FIND_QUIETLY) 13 | MESSAGE(STATUS "Found blkid: ${BLKID_LIBRARY}") 14 | ENDIF (NOT blkid_FIND_QUIETLY) 15 | ELSE (BLKID_FOUND) 16 | IF (blkid_FIND_REQUIRED) 17 | MESSAGE(FATAL_ERROR "Could not find blkid") 18 | ENDIF (blkid_FIND_REQUIRED) 19 | ENDIF (BLKID_FOUND) 20 | -------------------------------------------------------------------------------- /src/cmake/Findext2fs.cmake: -------------------------------------------------------------------------------- 1 | FIND_PATH(EXT2FS_INCLUDE_DIR ext2fs.h /usr/include/ext2fs 2 | /usr/local/include/ext2fs) 3 | 4 | FIND_LIBRARY(EXT2FS_LIBRARY NAMES ext2fs PATH /usr/lib /usr/local/lib) 5 | 6 | IF (EXT2FS_INCLUDE_DIR AND EXT2FS_LIBRARY) 7 | SET(EXT2FS_FOUND TRUE) 8 | ENDIF (EXT2FS_INCLUDE_DIR AND EXT2FS_LIBRARY) 9 | 10 | 11 | IF (EXT2FS_FOUND) 12 | IF (NOT ext2fs_FIND_QUIETLY) 13 | MESSAGE(STATUS "Found ext2fs: ${EXT2FS_LIBRARY}") 14 | ENDIF (NOT ext2fs_FIND_QUIETLY) 15 | ELSE (EXT2FS_FOUND) 16 | IF (ext2fs_FIND_REQUIRED) 17 | MESSAGE(FATAL_ERROR "Could not find ext2fs") 18 | ENDIF (ext2fs_FIND_REQUIRED) 19 | ENDIF (EXT2FS_FOUND) 20 | -------------------------------------------------------------------------------- /src/cmake/Findauparse.cmake: -------------------------------------------------------------------------------- 1 | FIND_PATH(AUPARSE_INCLUDE_DIR auparse.h /usr/include 2 | /usr/local/include) 3 | 4 | FIND_LIBRARY(AUPARSE_LIBRARY NAMES auparse PATH /usr/lib /usr/local/lib) 5 | 6 | IF (AUPARSE_INCLUDE_DIR AND AUPARSE_LIBRARY) 7 | SET(AUPARSE_FOUND TRUE) 8 | ENDIF (AUPARSE_INCLUDE_DIR AND AUPARSE_LIBRARY) 9 | 10 | 11 | IF (AUPARSE_FOUND) 12 | IF (NOT auparse_FIND_QUIETLY) 13 | MESSAGE(STATUS "Found auparse: ${AUPARSE_LIBRARY}") 14 | ENDIF (NOT auparse_FIND_QUIETLY) 15 | ELSE (AUPARSE_FOUND) 16 | IF (auparse_FIND_REQUIRED) 17 | MESSAGE(FATAL_ERROR "Could not find auparse") 18 | ENDIF (auparse_FIND_REQUIRED) 19 | ENDIF (AUPARSE_FOUND) 20 | -------------------------------------------------------------------------------- /e4rat-lite.conf: -------------------------------------------------------------------------------- 1 | ; e4rat-lite configuration file 2 | 3 | [Global] 4 | 5 | ; Path to the main initialization process 6 | init_file=/usr/lib/systemd/systemd 7 | 8 | ; Default location for the boot log 9 | startup_log_file=/var/lib/e4rat-lite/startup.log 10 | 11 | ; ------------------ 12 | 13 | [Collect] 14 | 15 | ; Collect files only on ext4 devices [true/false] 16 | ext4_only=true 17 | 18 | ; Ignore opened files (already running processes) [true/false] 19 | exclude_open_files=true 20 | 21 | ; Time (in seconds) to wait before finalizing the collect 22 | timeout=120 23 | 24 | ; ------------------ 25 | 26 | [Realloc] 27 | 28 | ; Defragmentation method [auto/pa/tld/locality_group] 29 | defrag_mode=auto 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/intl.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * e4rat-collect.cc - Generate file list of relevant files by monitoring programs 3 | * 4 | * Copyright (C) 2015 by Lara Maia 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #ifndef INTL_H 21 | #define INTL_H 22 | 23 | #include 24 | #include 25 | 26 | #ifndef _ 27 | #ifdef DEBUG_ENABLED 28 | #define _(string) string 29 | #else 30 | #define _(string) gettext(string) 31 | #endif 32 | #endif 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /po/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | FIND_PACKAGE(Gettext REQUIRED) 3 | 4 | IF(NOT GETTEXT_MSGFMT_EXECUTABLE) 5 | MESSAGE(FATAL_ERROR "Please, install gettext") 6 | ENDIF (NOT GETTEXT_MSGFMT_EXECUTABLE) 7 | 8 | FILE(GLOB _po_files *.po) 9 | 10 | SET(_gmoFiles) 11 | SET(_gmoPackFiles) 12 | 13 | FOREACH(_current_PO_FILE ${_po_files}) 14 | GET_FILENAME_COMPONENT(_lang ${_current_PO_FILE} NAME_WE) 15 | SET(_gmoFile ${CMAKE_CURRENT_BINARY_DIR}/${_lang}.gmo) 16 | add_custom_command(OUTPUT ${_gmoFile} 17 | COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} -o ${_gmoFile} ${_current_PO_FILE} 18 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 19 | DEPENDS ${_current_PO_FILE} 20 | ) 21 | 22 | INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${_lang}.gmo 23 | DESTINATION ${LOCALE_INSTALL_DIR}/${_lang}/LC_MESSAGES/ 24 | RENAME e4rat-lite.mo 25 | ) 26 | 27 | LIST(APPEND _gmoFiles ${_gmoFile}) 28 | ENDFOREACH(_current_PO_FILE) 29 | ADD_CUSTOM_TARGET(pofiles ALL DEPENDS ${_gmoFiles}) 30 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | 3 | PROJECT(e4rat-lite) 4 | set(E4RAT-LITE_VERSION "2.7-git") 5 | 6 | IF(CMAKE_BUILD_TYPE STREQUAL "release") 7 | set(CMAKE_VERBOSE_MAKEFILE off) 8 | else(CMAKE_BUILD_TYPE STREQUAL "release") 9 | set(CMAKE_VERBOSE_MAKEFILE on) 10 | endif(CMAKE_BUILD_TYPE STREQUAL "release") 11 | 12 | set(LOCALE_INSTALL_DIR "/usr/share/locale") 13 | 14 | IF( NOT CMAKE_BUILD_TYPE ) 15 | SET( CMAKE_BUILD_TYPE "debug" ) 16 | ENDIF() 17 | 18 | IF(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 19 | set(CMAKE_INSTALL_PREFIX "/" CACHE PATH "e4rat install prefix" FORCE) 20 | ENDIF(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 21 | 22 | set(Boost_USE_STATIC_LIBS OFF) 23 | set(Boost_USE_MULTITHREADED OFF) 24 | find_package(Boost 1.41 COMPONENTS system filesystem regex REQUIRED) 25 | set(${PROJECT_NAME}_LIBRARIES ${${PROJECT_NAME}_LIBRARIES} 26 | ${Boost_LIBRARIES}) 27 | set(Boost_VERSION_STR "${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}") 28 | 29 | add_subdirectory( ${CMAKE_CURRENT_SOURCE_DIR}/src) 30 | add_subdirectory( ${CMAKE_CURRENT_SOURCE_DIR}/doc) 31 | add_subdirectory( ${CMAKE_CURRENT_SOURCE_DIR}/po) 32 | 33 | add_custom_target(src 34 | COMMAND test -e ${PROJECT_NAME}-${E4RAT-LITE_VERSION} || mkdir ${PROJECT_NAME}-${E4RAT-LITE_VERSION} 35 | COMMAND rsync -p --relative `git ls-files` ${PROJECT_NAME}-${E4RAT-LITE_VERSION} 36 | COMMAND tar pczf ${PROJECT_NAME}_${E4RAT-LITE_VERSION}_src.tar.gz ${PROJECT_NAME}-${E4RAT-LITE_VERSION} 37 | ) 38 | 39 | INSTALL(FILES e4rat-lite.conf 40 | DESTINATION "/etc/" 41 | ) 42 | -------------------------------------------------------------------------------- /tools/update_po.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd .. 3 | if [ "$1" == "" ]; then 4 | pofile=e4rat-lite 5 | sufix=pot 6 | if [ -f ${pofile}.${sufix} ]; then 7 | rm po/${pofile}.${sufix} 8 | fi 9 | else 10 | pofile=${1} 11 | sufix=po 12 | fi 13 | 14 | _basearray=($(find src/ -type f \ 15 | \( -name "*.cc" -o -name "*.c" \) \ 16 | -exec basename {} \;)) 17 | 18 | if [ -e po/${pofile}.${sufix}.bkp ]; then 19 | echo "Apagando backup antigo de po/${pofile}.${sufix}" 20 | rm -f po/${pofile}.${sufix}.bkp 21 | fi 22 | 23 | if [ -f po/${pofile}.${sufix} ]; then 24 | echo "Criando backup de po/${pofile}.${sufix} para po/${pofile}.${sufix}.bkp" 25 | cp po/${pofile}.${sufix} po/${pofile}.${sufix}.bkp 26 | fi 27 | 28 | function create() { 29 | echo "Criando po/${pofile}.${sufix} a partir de src/$1" 30 | xgettext -d e4rat-lite -o po/${pofile}.${sufix} -k_ -s src/$1 31 | sed --in-place po/${pofile}.${sufix} --expression=s/CHARSET/UTF-8/ 32 | } 33 | 34 | function update() { 35 | echo "Atualizando po/${pofile}.${sufix} a partir de src/$1" 36 | xgettext -d e4rat-lite -o po/${pofile}.${sufix} -k_ -j -s src/$1 37 | } 38 | 39 | for file in ${_basearray[@]}; do 40 | if [ "$2" == "--new" ]; then 41 | if [ ! -f "po/${pofile}.${sufix}" ]; then 42 | unset nofirst 43 | fi 44 | if [ ! -n "$nofirst" ]; then 45 | sufix=pot 46 | create $file 47 | nofirst=true 48 | else 49 | update $file 50 | fi 51 | else 52 | if [ -f "po/${pofile}.${sufix}" ]; then 53 | update $file 54 | else 55 | echo "Arquivo não encontrado." 56 | create $file 57 | fi 58 | fi 59 | done 60 | 61 | -------------------------------------------------------------------------------- /src/eventcatcher.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * eventcatcher.hh - Handle audit event from Listener 3 | * 4 | * Copyright (C) 2011 by Andreas Rid 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #ifndef EVENT_CATCHER_HH 21 | #define EVENT_CATCHER_HH 22 | 23 | #include "fileptr.hh" 24 | #include "common.hh" 25 | #include "signals.hh" 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | class AuditEvent; 32 | 33 | class EventCatcher 34 | { 35 | public: 36 | virtual void handleAuditEvent(boost::shared_ptr event) = 0; 37 | }; 38 | 39 | /* 40 | * Scan all filesystem accessed files 41 | * collect all files in one list to retain access order 42 | * 43 | * ScanFsAccess resolve file links 44 | */ 45 | class ScanFsAccess : public EventCatcher 46 | { 47 | public: 48 | void observeApp(std::string); 49 | std::deque getFileList(); 50 | protected: 51 | virtual void handleAuditEvent(boost::shared_ptr); 52 | private: 53 | void insert(FilePtr&); 54 | fs::path readLink(fs::path& path); 55 | fs::path getPath2RegularFile(fs::path& path); 56 | 57 | std::set observe_apps; 58 | std::set observe_pids; 59 | std::deque list; 60 | }; 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | /* inih -- simple .INI file parser 2 | * Copyright (c) 2009, Brush Technology 3 | * http://code.google.com/p/inih/ 4 | */ 5 | 6 | #ifndef __INI_H__ 7 | #define __INI_H__ 8 | 9 | /* Make this header file easier to include in C++ code */ 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | #include 15 | 16 | /* Parse given INI-style file. May have [section]s, name=value pairs 17 | (whitespace stripped), and comments starting with ';' (semicolon). Section 18 | is "" if name=value pair parsed before any section heading. name:value 19 | pairs are also supported as a concession to Python's ConfigParser. 20 | 21 | For each name=value pair parsed, call handler function with given user 22 | pointer as well as section, name, and value (data only valid for duration 23 | of handler call). Handler should return nonzero on success, zero on error. 24 | 25 | Returns 0 on success, line number of first error on parse error (doesn't 26 | stop on first error), or -1 on file open error. 27 | */ 28 | int ini_parse(const char* filename, 29 | int (*handler)(void* user, const char* section, 30 | const char* name, const char* value), 31 | void* user); 32 | 33 | /* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't 34 | close the file when it's finished -- the caller must do that. */ 35 | int ini_parse_file(FILE* file, 36 | int (*handler)(void* user, const char* section, 37 | const char* name, const char* value), 38 | void* user); 39 | 40 | /* Nonzero to allow multi-line value parsing, in the style of Python's 41 | ConfigParser. If allowed, ini_parse() will call the handler with the same 42 | name for each subsequent line parsed. */ 43 | #ifndef INI_ALLOW_MULTILINE 44 | #define INI_ALLOW_MULTILINE 1 45 | #endif 46 | 47 | #ifdef __cplusplus 48 | } 49 | #endif 50 | 51 | #endif /* __INI_H__ */ 52 | -------------------------------------------------------------------------------- /doc/en_US/e4rat-lite-preload.pod: -------------------------------------------------------------------------------- 1 | =encoding utf8 2 | =pod 3 | 4 | =head1 NAME 5 | 6 | e4rat-lite-preload - Preload files into memory 7 | 8 | =head1 SYNOPSIS 9 | 10 | B B<[> option(s) B<]> file(s) 11 | 12 | =head1 DESCRIPTION 13 | 14 | e4rat-lite-preload transfers files into memory to gain a high cache hit rate. 15 | 16 | In order to prevent disk seeks the preloading process is divided into two steps: 17 | 18 | First, it reads the files' I-Node information, after it reads the files' content. This can be done in parallel of executing the command specified with --execute. If e4rat-lite-preload is executed as the init process by adding it to the kernel parameters, /usr/lib/systemd/systemd is called. 19 | 20 | =head2 Observations 21 | 22 | The list I must be in a format generated by e4rat-lite-collect. 23 | 24 | It assumes that all files got reallocated by e4rat-lite-realloc at first. 25 | 26 | =head1 OPTIONS 27 | 28 | =over 29 | 30 | =item -V --version 31 | 32 | show version information and exit. 33 | 34 | =item -h --help 35 | 36 | display usage message and exit. 37 | 38 | =item -i --initfile 39 | 40 | Alternate init file. 41 | 42 | =item -s --startuplog 43 | 44 | Alternate startup log file 45 | 46 | =back 47 | 48 | =head1 EXAMPLES 49 | 50 | =head2 Accelerate the boot process: 51 | 52 | Run e4rat-lite-preload as early as possible. It is recommended to run it as the init process. To do so, append the following line to the Kernel parameters in grub(8) or lilo(8): 53 | init=/usr/bin/e4rat-lite-preload 54 | 55 | =head1 FILES 56 | 57 | F 58 | E4rat-lite configuration file. 59 | 60 | =head1 AUTHOR 61 | 62 | e4rat written by Andreas Rid and Gundolf Kiefer. 63 | e4rat-preload-lite written by John Lindgren 64 | e4rat-lite written by Lara Maia. 65 | 66 | =head1 REPORTING BUGS 67 | 68 | Report bugs to Lara Maia 69 | 70 | =head1 SEE ALSO 71 | 72 | e4rat-lite-collect(8), e4rat-lite-realloc(8), e4rat-lite.conf(8) 73 | -------------------------------------------------------------------------------- /doc/pt_BR/e4rat-lite-preload.pod: -------------------------------------------------------------------------------- 1 | =encoding utf8 2 | =pod 3 | 4 | =head1 NOME 5 | 6 | e4rat-lite-preload - Pré carrega arquivos na memória. 7 | 8 | =head1 SINOPSE 9 | 10 | B B<[> opções B<]> arquivo(s) 11 | 12 | =head1 DESCRIÇÃO 13 | 14 | O e4rat-lite-preload transfere os arquivos para a memória a fim de obter um alta taxa de acertos no cache. 15 | 16 | A fim de evitar a busca no disco, o processo de pré carregamento é dividido em duas etapas: 17 | 18 | Primeiro, ele lê as informações de I-Node dos arquivos, depois ele lê o conteúdo dos arquivos. Isso pode ser feito paralelamente com a execução do arquivo especificado em --execute. Se o e4rat-lite-preload for executado como um processo de inicialização pela adição dele aos parametros do kernel, o /usr/lib/systemd/systemd (ou o processo indicado na configuração) é chamado. 19 | 20 | =head2 Observações 21 | 22 | A lista de l precisa estar no formato gerado pelo e4rat-lite-collect. 23 | 24 | Assume-se que todos os arquivos tenham sido realocados antes pelo e4rat-lite-realloc. 25 | 26 | =head1 OPÇÕES 27 | 28 | =over 29 | 30 | =item -V --version 31 | 32 | mostra informações sobre a versão e sai. 33 | 34 | =item -h --help 35 | 36 | mostra a ajuda e sai. 37 | 38 | =item -i --initfile 39 | 40 | Arquivo de inicialização alternativo. 41 | 42 | =item -s --startuplog 43 | 44 | Arquivo alternativo para o log de inicialização 45 | 46 | =back 47 | 48 | =head1 EXEMPLOS 49 | 50 | =head2 Acelerando o processo de boot 51 | 52 | Execute o e4rat-lite-preload o mais cedo possível. É recomendado executa-lo como um processo de inicialização, para isso insira a seguinte linha aos parametros do kernel em grub(8) ou lilo(8). 53 | init=/usr/bin/e4rat-lite-preload 54 | 55 | =head1 ARQUIVOS 56 | 57 | F 58 | Arquivo de configuração do e4rat-lite 59 | 60 | =head1 AUTOR 61 | 62 | e4rat foi escrito por Andreas Rid e Gunfolf Kiefer. 63 | e4rat-preload-lite foi escrito por John Lindgren 64 | e4rat-lite escrito por Lara Maia. 65 | 66 | =head1 REPORTANDO BUGS 67 | 68 | Reporte bugs para Lara Maia 69 | 70 | =head1 LEIA TAMBÉM 71 | 72 | e4rat-lite-collect(8), e4rat-lite-realloc(8), e4rat-lite.conf(8) 73 | -------------------------------------------------------------------------------- /src/parsefilelist.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * parselist.cc - Parse file list 3 | * 4 | * Copyright (C) 2011 by Andreas Rid 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | /* 21 | * Parse file list from file stream 22 | */ 23 | 24 | #include "logging.hh" 25 | #include 26 | 27 | int peek(FILE* in) 28 | { 29 | int c = fgetc(in); 30 | ungetc(c, in); 31 | 32 | return c; 33 | } 34 | 35 | template 36 | void parseInputStream(FILE* in, std::vector& filelist) 37 | { 38 | bool detailed = true; 39 | int ret; 40 | 41 | int lineno = 0; 42 | int dev = 0; 43 | __u64 ino; 44 | char path[PATH_MAX]; 45 | 46 | int c = peek(in); 47 | if(c == EOF) 48 | return; 49 | else if(c == '/') 50 | detailed = false; 51 | 52 | while(1) 53 | { 54 | lineno++; 55 | 56 | if(detailed) 57 | ret = fscanf(in, "%d %llu %[^\n]s", &dev, &ino, path); 58 | else 59 | ret = fscanf(in, "%[^\n]s", path); 60 | 61 | if(ret == EOF || ret == 0) 62 | break; 63 | 64 | if((detailed && ret != 3) 65 | || (!detailed && ret != 1)) 66 | { 67 | std::stringstream ss; 68 | ss << _("Error while parsing ") << getPathFromFd(fileno(in)) << ".\n" 69 | << _("Syntax error at line ") << lineno << _(" argument ") << ret+1; 70 | throw std::runtime_error(ss.str()); 71 | } 72 | if(!detailed) 73 | filelist.push_back(T(path)); 74 | else 75 | filelist.push_back(T(dev,ino,path)); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/common.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * common.hh - Collection of common functions and classes 3 | * 4 | * Copyright (C) 2011 by Andreas Rid 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #ifndef COMMON_HH 21 | #define COMMON_HH 22 | 23 | #include "intl.hh" 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | namespace fs = boost::filesystem; 30 | 31 | void setStdIn2NonBlocking(); 32 | 33 | /* 34 | * file operations 35 | */ 36 | const boost::regex path2regex(std::string path); 37 | std::vector matchPath( const std::string & filesearch ); 38 | fs::path realpath(fs::path _path, fs::path _cwd = ""); 39 | std::string getPathFromFd(int fd); 40 | 41 | /* 42 | * pid file operations 43 | */ 44 | pid_t readPidFile(const char* path); 45 | bool createPidFile(const char* path); 46 | 47 | /* 48 | * UserInterrupt is an exception thrown by non-multi-threaded applications. 49 | * The idea behind exceptions is to clean up memory and other system resources. 50 | */ 51 | class UserInterrupt : public std::exception 52 | { 53 | public: 54 | UserInterrupt() 55 | : msg("User interrupt") 56 | {} 57 | virtual const char* what() const throw() 58 | 59 | { 60 | return msg; 61 | } 62 | private: 63 | const char* msg; 64 | }; 65 | 66 | /* 67 | * Declare class interruptible 68 | */ 69 | class Interruptible 70 | { 71 | public: 72 | static void interrupt(); 73 | protected: 74 | void interruptionPoint(); 75 | private: 76 | static bool interrupted; 77 | }; 78 | 79 | void signalHandler(int signum); 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /doc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | 3 | set( _MAN_LANGS pt_BR en_US ) 4 | 5 | # test weather variables from top level directories are missing 6 | IF( NOT E4RAT-LITE_VERSION ) 7 | MESSAGE(FATAL_ERROR "Do not run cmake on subdirectories") 8 | ENDIF() 9 | 10 | ADD_CUSTOM_TARGET(ManPages ALL) 11 | 12 | foreach( _langs ${_MAN_LANGS} ) 13 | foreach( _man e4rat-lite-collect e4rat-lite-realloc e4rat-lite-preload ) 14 | ADD_CUSTOM_COMMAND( 15 | TARGET ManPages 16 | COMMAND pod2man ARGS -u 17 | ${CMAKE_CURRENT_SOURCE_DIR}/${_langs}/${_man}.pod 18 | ${CMAKE_CURRENT_SOURCE_DIR}/${_langs}/${_man}.8 19 | --section 8 20 | --center "User Manuals" 21 | --release "e4rat-lite ${E4RAT-LITE_VERSION}" 22 | OUTPUTS ${CMAKE_CURRENT_SOURCE_DIR}/${_langs}/${_man}.8) 23 | endforeach( _man ) 24 | 25 | ADD_CUSTOM_COMMAND( 26 | TARGET ManPages 27 | COMMAND pod2man ARGS -u 28 | ${CMAKE_CURRENT_SOURCE_DIR}/${_langs}/e4rat-lite.conf.pod 29 | ${CMAKE_CURRENT_SOURCE_DIR}/${_langs}/e4rat-lite.conf.5 30 | --section 5 31 | --center "User Manuals" 32 | --release "e4rat-lite ${E4RAT-LITE_VERSION}" 33 | OUTPUTS ${CMAKE_CURRENT_SOURCE_DIR}/${_langs}/e4rat-lite.conf.5) 34 | 35 | ADD_CUSTOM_COMMAND( 36 | TARGET ManPages 37 | DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${_langs}/e4rat-lite-collect.8 38 | ${CMAKE_CURRENT_SOURCE_DIR}/${_langs}/e4rat-lite-realloc.8 39 | ${CMAKE_CURRENT_SOURCE_DIR}/${_langs}/e4rat-lite-preload.8 40 | ${CMAKE_CURRENT_SOURCE_DIR}/${_langs}/e4rat-lite.conf.5) 41 | 42 | INSTALL(FILES 43 | ${CMAKE_CURRENT_SOURCE_DIR}/${_langs}/e4rat-lite-collect.8 44 | ${CMAKE_CURRENT_SOURCE_DIR}/${_langs}/e4rat-lite-realloc.8 45 | ${CMAKE_CURRENT_SOURCE_DIR}/${_langs}/e4rat-lite-preload.8 46 | DESTINATION /usr/share/man/${_langs}/man8/) 47 | 48 | INSTALL(FILES 49 | ${CMAKE_CURRENT_SOURCE_DIR}/${_langs}/e4rat-lite.conf.5 50 | DESTINATION /usr/share/man/${_langs}/man5/) 51 | endforeach( _langs ) 52 | -------------------------------------------------------------------------------- /src/buddycache.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * buddycache.hh - Linux kernel ext4 buddy cache 3 | * 4 | * Copyright (C) 2011 by Andreas Rid 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #ifndef BUDDY_CACHE_HH 21 | #define BUDDY_CACHE_HH 22 | 23 | #include 24 | #include "device.hh" 25 | 26 | /* 27 | * Store free block ranges per block group 28 | */ 29 | struct BuddyGroup 30 | { 31 | unsigned short free; 32 | unsigned short frags; 33 | unsigned short first; 34 | unsigned char s0; 35 | unsigned char s1; 36 | unsigned char s2; 37 | unsigned char s3; 38 | unsigned char s4; 39 | unsigned char s5; 40 | unsigned char s6; 41 | unsigned char s7; 42 | unsigned char s8; 43 | unsigned char s9; 44 | unsigned char s10; 45 | unsigned char s11; 46 | unsigned char s12; 47 | unsigned char s13; 48 | }; 49 | 50 | /* 51 | * BuddyCache holds a snapshot of free block ranges on disk 52 | * by parsing the file /proc/fs/ext4//mb_groups 53 | * 54 | * Unlike block bitmaps, buddy cache also marked pre-allocated space as used. 55 | * Therefore it reveals actual available free block ranges. 56 | * 57 | * The refresh()-method updates the snapshot. 58 | */ 59 | class BuddyCache 60 | { 61 | public: 62 | BuddyCache(Device); 63 | 64 | //updates snapshot 65 | void refresh(); 66 | 67 | //Return a reference to BuddyGroup of group id 68 | BuddyGroup& at(int group); 69 | 70 | //Return flex number of an empty flex 71 | int findEmptyFlex(); 72 | 73 | //Return block group number of first found empty block group 74 | int findEmptyGroup(); 75 | public: 76 | bool isFlexEmpty(__u32); 77 | bool isGroupEmpty(__u32); 78 | void scanGroup(unsigned int); 79 | Device device; 80 | std::vector data; 81 | }; 82 | 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /src/defrag.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * defrag.hh - Operations on relevant file defragmentation 3 | * 4 | * Copyright (C) 2011 by Andreas Rid 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #ifndef DEFRAG_HH 21 | #define DEFRAG_HH 22 | 23 | #include "common.hh" 24 | #include "fileptr.hh" 25 | #include "device.hh" 26 | 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | 33 | struct OrigDonorPair 34 | { 35 | OrigDonorPair() : blocks(0), isSparseFile(false) {} 36 | OrigDonorPair(fs::path p) : origPath(p), blocks(0), isSparseFile(false) {} 37 | 38 | fs::path origPath; 39 | fs::path donorPath; 40 | __u64 blocks : 63; 41 | __u64 isSparseFile : 1; 42 | }; 43 | 44 | class Defrag : public Interruptible 45 | { 46 | typedef std::vector filelist_t; 47 | protected: 48 | Defrag(); 49 | bool doesKernelSupportPA(const char*); 50 | bool isPAenabled(fs::path& mountPoint, char* device_name); 51 | void createDonorFiles_PA( 52 | Device& device, 53 | std::vector& files); 54 | void fillUpLocalityGroup(Device& device); 55 | void createDonorFiles_LocalityGroup( 56 | Device& device, 57 | std::vector& files); 58 | void createDonorFiles_TLD( 59 | Device& device, 60 | std::vector& files); 61 | void createDonorFiles( 62 | Device& device, 63 | std::vector& defragPair ); 64 | 65 | void checkFilesAttributes(Device device, std::vector&); 66 | 67 | void defragRelatedFiles(Device device, std::vector& files); 68 | 69 | int invalid_file_type; 70 | int not_writable; 71 | int not_extent_based; 72 | int empty_files; 73 | int sparse_files; 74 | }; 75 | 76 | class Optimizer : public Defrag 77 | { 78 | public: 79 | Optimizer(); 80 | void relatedFiles(std::vector&); 81 | }; 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /src/fileptr.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * fileptr.hh - Pointer to an unique file object 3 | * 4 | * Copyright (C) 2011 by Andreas Rid 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #ifndef FILEPTR_HH 21 | #define FILEPTR_HH 22 | 23 | #include "singleton.hh" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | namespace fs = boost::filesystem; 32 | 33 | 34 | struct FilePtrPrivate 35 | { 36 | friend class FilePtr; 37 | private: 38 | FilePtrPrivate(); 39 | ino_t ino; 40 | dev_t dev; 41 | unsigned int flags; 42 | fs::path path; 43 | bool valid; 44 | }; 45 | 46 | typedef boost::weak_ptr WeakFilePtr; 47 | 48 | class FilePtr : public boost::shared_ptr 49 | { 50 | typedef std::map map_t; 51 | 52 | public: 53 | FilePtr(dev_t, ino_t, fs::path, bool = true); 54 | FilePtr(fs::path, bool = true); 55 | public: 56 | FilePtr(); 57 | ~FilePtr(); 58 | FilePtr(boost::shared_ptr& other) 59 | : boost::shared_ptr(other) 60 | { 61 | 62 | } 63 | void setInvalid(); 64 | bool isValid() const; 65 | ino_t getInode() const; 66 | dev_t getDevice() const; 67 | const fs::path& getPath() const; 68 | private: 69 | }; 70 | 71 | class FileDepot 72 | { 73 | DECLARE_SINGLETON(FileDepot); 74 | friend class Collector; 75 | friend class FilePtr; 76 | public: 77 | struct key_t 78 | { 79 | dev_t dev; 80 | ino_t ino; 81 | bool operator<(const key_t&) const; 82 | }; 83 | 84 | typedef std::pair files_pair_t; 85 | typedef std::map files_t; 86 | typedef files_t::iterator files_it_t; 87 | 88 | protected: 89 | void insert(dev_t, ino_t, FilePtr&); 90 | void insert(FilePtr&); 91 | void remove(FilePtr); 92 | 93 | private: 94 | files_t files; 95 | inline void genKey(key_t*, dev_t, ino_t); 96 | void insert(key_t, FilePtr&); 97 | }; 98 | 99 | #endif 100 | -------------------------------------------------------------------------------- /src/singleton.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * singleton.hh - Macros declare a class to be a heap based singleton 3 | * 4 | * Copyright (C) 2011 by Andreas Rid 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | * 19 | * Exemplary usage: 20 | * 21 | * class MyClass 22 | * { 23 | * DECLARE_SINGLETON(MyClass); 24 | * public: 25 | * void foo(); 26 | * }; 27 | * 28 | * DEFINE_SINGLETON(MyClass); 29 | */ 30 | 31 | #ifndef SINGLETON_HH 32 | #define SINGLETON_HH 33 | 34 | #include 35 | 36 | #define DECLARE_SINGLETON(ClassName) \ 37 | public: \ 38 | static ClassName* instance(); \ 39 | private: \ 40 | static ClassName *me; \ 41 | static pthread_mutex_t lock_singleton; \ 42 | ClassName(); \ 43 | ClassName( const ClassName& ); \ 44 | ~ClassName(); \ 45 | \ 46 | /* Guarder is responsible to cleanup */ \ 47 | class Guarder { \ 48 | public: ~Guarder() { \ 49 | if( ClassName::me != 0 ) \ 50 | delete ClassName::me; \ 51 | } \ 52 | }; \ 53 | friend class Waechter; 54 | 55 | #define DEFINE_SINGLETON(ClassName) \ 56 | ClassName* ClassName::me = NULL; \ 57 | pthread_mutex_t ClassName::lock_singleton = PTHREAD_MUTEX_INITIALIZER; \ 58 | ClassName* ClassName::instance() \ 59 | { \ 60 | static Guarder g; \ 61 | if( me == NULL ) \ 62 | if(0 == pthread_mutex_lock(&lock_singleton)) { \ 63 | me = new ClassName(); \ 64 | pthread_mutex_unlock(&lock_singleton); \ 65 | } \ 66 | \ 67 | return me; \ 68 | } 69 | 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /doc/en_US/e4rat-lite.conf.pod: -------------------------------------------------------------------------------- 1 | =encoding utf8 2 | =pod 3 | 4 | =head1 NAME 5 | 6 | /etc/e4rat-lite.conf - configuration file for the e4rat-lite toolset 7 | 8 | =head1 DESCRIPTION 9 | 10 | B is the configuration file all e4rat-lite binaries. It allows to replace default parameters of the tools. 11 | 12 | =head1 FILE FORMAT 13 | 14 | The file is based on Python's ConfigParser style of .INI files, including RFC 822-style multi-line syntax and name: value entries. For more information see I 15 | 16 | An option is declared like this: 17 | option1=value1 18 | 19 | Options can either be declared below a section. Derived from its executable suffix name, each binary owns its own section. Values in section Global applies to all binaries. 20 | 21 | Example: 22 | 23 | [Collect] 24 | ; comment 25 | option=value 26 | 27 | =head1 OPTIONS 28 | 29 | e4rat-lite has a simple logging structure. Each event has a priority bit. The following priorities exist: 30 | 1 errors 31 | 2 warnings 32 | 4 task statistics 33 | 8 information about a single step 34 | 16 debug message 35 | 36 | =head2 Global Section (For all binaries) 37 | 38 | =over 39 | 40 | =item B 41 | 42 | (not yet implemented on config file) 43 | set verbose bit mask of messages to be displayed. [Default: 7] 44 | 45 | =item B 46 | 47 | (not yet implemented on config file) 48 | set bit mask of messages send to L. [Default: 3] 49 | 50 | =item B 51 | 52 | (not yet implemented on config file) 53 | set target path where log messages should be written to. Default value is /dev/kmsg. 54 | It sends the message to Kernel's log ring buffer. (See also dmesg(1)) 55 | Set to 'syslog' to send the message to the syslog daemon. 56 | 57 | =item B 58 | 59 | Set an alternative init process instead of /usr/lib/systemd/systemd 60 | 61 | =item B 62 | 63 | set path to startup log file. [Default: /var/lib/e4ra-litet/startup.log] 64 | 65 | =back 66 | 67 | =head2 Specific for I 68 | 69 | =over 70 | 71 | =item B 72 | 73 | Restrict file watches to ext4 filesystem types only. [Default: true] 74 | 75 | =item B 76 | 77 | exclude files which are already opened (running). [Default: true] 78 | 79 | =item B (in seconds) 80 | 81 | After the expiration of this value, the e4rat-lite-collect automatically quits collecting. Timeout takes only into account when e4rat-lite-collect was executed as init process. [Default: 120] 82 | 83 | =back 84 | 85 | =head2 Specific for e4rat-lite-realloc 86 | 87 | =over 88 | 89 | =item B 90 | 91 | set default rearrangement mode of e4rat-lite-realloc. [Default: auto] 92 | auto choose mode automatically 93 | pa use user-space pre-allocate ioctl 94 | locality-group create files in locality group 95 | tld create files in top level directory 96 | 97 | =back 98 | 99 | =head1 AUTHOR 100 | 101 | e4rat has been written by Andreas Rid and Gundolf Kiefer. 102 | e4rat-lite writen by Lara Maia. 103 | 104 | =head1 REPORTING BUGS 105 | 106 | Report bugs to Lara Maia 107 | 108 | =head1 SEE ALSO 109 | 110 | e4rat-lite-collect(8), e4rat-lite-realloc(8), e4rat-lite-preload(8) 111 | -------------------------------------------------------------------------------- /src/balloc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * balloc.h - Declarations concerning ext4 filesystem 3 | * 4 | * Copyright (C) 2009 NEC Software Tohoku, Ltd. 5 | * Copyright (C) 2011 by Andreas Rid 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | 22 | #ifndef BALLOC_H 23 | #define BALLOC_H 24 | 25 | #ifdef __cplusplus 26 | extern "C" 27 | { 28 | #endif 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | #include 36 | 37 | #ifndef EXT4_IOC_MOVE_EXT 38 | #define EXT4_IOC_MOVE_EXT _IOWR('f', 15, struct move_extent) 39 | #endif 40 | 41 | #ifndef EXT4_IOC_CONTROL_PA 42 | #define EXT4_IOC_CONTROL_PA _IOWR('f', 16, struct ext4_prealloc_info) 43 | #endif 44 | 45 | /* Macros for EXT4_IOC_CONTROL_PA */ 46 | #define EXT4_MB_MANDATORY 0x0001 47 | #define EXT4_MB_ADVISORY 0x0002 48 | #define EXT4_MB_DISCARD_PA 0x0004 49 | #define EXT4_IOC_GET_PA _IOWR('f', 17, struct ext4_prealloc_list) 50 | 51 | /* The maximum number of inode PAs that EXT4_IOC_CONTROL_PA can set */ 52 | #define EXT4_MAX_PREALLOC 1024 53 | 54 | /* The upper limit of length of prealloc which EXT4_IOC_CONTROL_PA can set */ 55 | //#define PREALLOC_MAX_BLK (blocks_per_group - 10) 56 | 57 | /* Data type for filesystem-wide blocks number */ 58 | typedef unsigned long long ext4_fsblk_t; 59 | 60 | struct fiemap_extent_data { 61 | __u64 len; /* blocks count */ 62 | __u64 logical; /* start logical block number */ 63 | ext4_fsblk_t physical; /* start physical block number */ 64 | }; 65 | 66 | struct move_extent { 67 | __s32 reserved; /* original file descriptor */ 68 | __u32 donor_fd; /* donor file descriptor */ 69 | __u64 orig_start; /* logical start offset in block for orig */ 70 | __u64 donor_start; /* logical start offset in block for donor */ 71 | __u64 len; /* block length to be moved */ 72 | __u64 moved_len; /* moved block length */ 73 | }; 74 | 75 | struct ext4_prealloc_info { 76 | __u64 pi_pstart; /* physical offset for the start of the PA from 77 | * the beginning of the file (in/out) */ 78 | __u32 pi_lstart; /* logical offset for the start of the PA from 79 | * the beginning of the disk (in/out) */ 80 | __u32 pi_len; /* length for this PA (in/out) */ 81 | __u32 pi_free; /* the number of free blocks in this PA (out) */ 82 | __u16 pi_flags; /* flags for the inode PA setting ioctl (in) */ 83 | }; 84 | struct ext4_prealloc_list { 85 | __u32 pl_count; /* size of pl_space array (in) */ 86 | __u32 pl_mapped; /* number of PAs that were mapped (out) */ 87 | __u32 pl_entries; /* number of PAs the inode has (out) */ 88 | struct ext4_prealloc_info pl_space[0]; /* array of mapped PAs (out) */ 89 | }; 90 | 91 | #ifdef __cplusplus 92 | } 93 | #endif 94 | #endif 95 | -------------------------------------------------------------------------------- /doc/en_US/e4rat-lite-realloc.pod: -------------------------------------------------------------------------------- 1 | =encoding utf8 2 | =pod 3 | 4 | =head1 NAME 5 | 6 | e4rat-lite-realloc - Reallocation and defragmentation of relevant files 7 | 8 | =head1 SYNOPSIS 9 | 10 | B B<[> option(s) B<]> B<[> mode B<]> file(s) 11 | 12 | =head1 DESCRIPTION 13 | 14 | e4rat-lite-realloc is a relevant file defrag program based on EXT4_IOC_MOVE_EXT ioctl from ext4 filesystems. It physically places files into a sequence of neighbouring blocks on disk. The reallocation process can be done when the filesystem is still mounted. For security reasons write access to each file is needed. 15 | 16 | If you want to process files from a program it is recommended to kill them at first. Linux does not allow to open files writable while it is still executed. So it is not possible to process the program executable binary. Those files get excluded automatically. 17 | 18 | Add list files to be proceeded either as parameters and/or over stdin. The list I must be in a format generated by e4rat-lite-collect. 19 | 20 | =head1 OPTIONS 21 | 22 | =over 23 | 24 | =item -V --version 25 | 26 | show version information and exit. 27 | 28 | =item -h --help 29 | 30 | display usage message and exit. 31 | 32 | =item -v --verbose 33 | 34 | increment verbosity level. 35 | 36 | =item -q --quiet 37 | 38 | set verbosity level to 0. This means that no messages will be displayed. 39 | 40 | =item -l --loglevel 41 | 42 | set loglevel to . All log messages are sent either to the Kernel log (see dmesg(1) or to syslog(3)). 43 | 44 | =back 45 | 46 | =head1 DEFRAG MODES 47 | 48 | Use the configuration file to change the defragmentation modes 49 | 50 | =over 51 | 52 | =item Preallocation (pa) 53 | 54 | use the pre-allocation ioctl to rearrange files on disk. Furthermore this mode supports to eliminate gaps of unallocated blocks in sparse files. Unfortunately, this ioctl is presently under heavy development by Kazuya Mio and has not yet entered the Linux Kernel. Therefore you have to apply this Kernel patch fist: 55 | http://kt1.osuosl.org/mailarchive/linux-fsdevel/2010/12/1/6887724 56 | Do this at your own risk. 57 | 58 | =item locality group 59 | 60 | The ext4 filesystem allocates small files in so-called locality groups. Each CPU manages its own group. 61 | The "locality-group" method makes use of this feature in order to rearrange the files on disk. In contrast to the "prealloc" method, no kernel patch is required, however, the placement may me suboptimal. 62 | While creating donor files is in progress, it is necessary to increase the limit which indicates small files. 63 | As a result other allocation requests occurring at the same time will be also satisfied with blocks from the locality group as well. It is the best if you just avoid heavy write access to disk. 64 | 65 | =item Top level directory (tld) 66 | 67 | With this method, all donor files are created in a new top level directory. 68 | The so-called Orlov algorithm put top level directory into an empty block group. As a result, donor files will be allocated near this directory. But there is no guarantee that the donor files are allocated in their access order. 69 | 70 | Also, ext4's block allocator normalizes every block request to a power of 2. This typically leads to small holes between the files. 71 | 72 | =back 73 | 74 | =head1 FILES 75 | 76 | F 77 | E4rat-lite configuration file. 78 | 79 | =head1 AUTHOR 80 | 81 | e4rat has been written by Andreas Rid and Gundolf Kiefer. 82 | e4rat-lite writen by Lara Maia. 83 | 84 | =head1 REPORTING BUGS 85 | 86 | Report bugs to Lara Maia 87 | 88 | =head1 SEE ALSO 89 | 90 | e4rat-lite-collect(8), e4rat-lite-preload(8), e4rat-lite.conf(8) 91 | -------------------------------------------------------------------------------- /doc/pt_BR/e4rat-lite.conf.pod: -------------------------------------------------------------------------------- 1 | =encoding utf8 2 | =pod 3 | 4 | =head1 NOME 5 | 6 | /etc/e4rat-lite.conf - Arquivo de configuração para o conjunto de ferramentas do e4rat-lite 7 | 8 | =head1 DESCRIÇÃO 9 | 10 | B é o arquivo configurador de todos os binários do e4rat-lite. Ele permite substituir as configurações padrões das ferramentas. 11 | 12 | =head1 FORMATO DE ARQUIVO 13 | 14 | O arquivo é baseado no estilo Python's ConfigParser de arquivos .INI, incluindo a sintaxe RFC 822-style multi-linha e entradas de nome:valores. Para mais informações leia I 15 | 16 | Uma opção pode ser declarada assim: 17 | opcao1=valor1 18 | 19 | As opções devem ser declaradas a baixo de uma seção. 20 | Derivado do nome sufixo do executável, cada binário tem sua própria seção. Valores da seção Global se aplica a todos os binários. 21 | 22 | Exemplo: 23 | 24 | [Collect] 25 | ; comentário 26 | opcao=valor 27 | 28 | =head1 OPÇÕES 29 | 30 | O e4rat-lite possui uma estrutura de log simples. Cada evento possui uma prioridade. As seguintes prioridades existem: 31 | 1 erros 32 | 2 avisos 33 | 4 estatísticas da tarefa 34 | 8 informação sobre uma única etapa 35 | 16 mensagens de debug 36 | 37 | =head2 Seção Global (Para todos os binários) 38 | 39 | =over 40 | 41 | =item B 42 | 43 | (ainda não implementado no arquivo de configuração) 44 | define o nível de verbose das mensagens [Padrão: 7] 45 | 46 | =item B 47 | 48 | (ainda não implementado no arquivo de configuração) 49 | Define o nível das mensagens para enviar ao L. [Padrão: 3] 50 | 51 | =item B 52 | 53 | (ainda não implementado no arquivo de configuração) 54 | define o diretório alvo onde as mensagens de log devem ser gravadas. O valor padrão é /dev/kmsg. Isto envia as mensagens para o log buffer do kernel. (veja também dmesg(1)) 55 | Defina o para 'syslog' caso queira enviar as mensagens para o daemon do syslog. 56 | 57 | =item B 58 | 59 | Define um processo de inicialização alternativo, substituindo o /usr/lib/systemd/systemd 60 | 61 | =item B 62 | 63 | Define o caminho para o arquivo de log de inicialização. [Padrão: /var/lib/e4rat-lite/startup.log] 64 | 65 | =back 66 | 67 | =head2 Específico para o I 68 | 69 | =over 70 | 71 | =item B 72 | 73 | Restringe a vigia para sistemas de arquivo ext4 somente. [Padrão: true] 74 | 75 | =item B 76 | 77 | Ignora arquivos abertos (em execução). [Padrão: true] 78 | 79 | =item B (em segundos) 80 | 81 | Depois da expiração desse valor, o e4rat-lite-collect vai automaticamente terminar a coleta. O Timeout apenas é funcional quando o e4rat-lite-collect é executado como um processo de inicialização (init). [Padrão: 120] 82 | 83 | =back 84 | 85 | =head2 Específico para o l 86 | 87 | =over 88 | 89 | =item B 90 | 91 | Define o modo padrão de realocação do e4rat-lite-realloc. [Padrão: auto] 92 | auto seleciona o modo automaticamente 93 | pa usa a pré alocação ioctl no espaço do usuário 94 | locality-group cria arquivos em grupos de localidades 95 | tld cria arquivo no diretório de nível superior 96 | 97 | =back 98 | 99 | =head1 AUTOR 100 | 101 | e4rat foi escrito por Andreas Rid e Gunfolf Kiefer. 102 | e4rat-lite escrito por Lara Maia. 103 | 104 | =head1 REPORTANDO BUGS 105 | 106 | Reporte bugs para Lara Maia 107 | 108 | =head1 Leia também 109 | 110 | e4rat-lite-collect(8), e4rat-lite-realloc(8), e4rat-lite-preload(8) 111 | -------------------------------------------------------------------------------- /src/logging.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * logging.hh - Display screen and or send event to klog or syslog 3 | * 4 | * Copyright (C) 2011 by Andreas Rid 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | * 19 | * 20 | * To log events use the following macros: 21 | * error (format, ...) Error Condition. Cannot continue. 22 | * warn (format, ...) Something stupid happen. Continue executing. 23 | * notice(format, ...) Results of a succeeded task. 24 | * info (format, ...) Messages during a task. 25 | * debug (format, ...) Debug messages. 26 | * 27 | * According to loglevel and verbose messages will be send 28 | * to syslog or will shown on screen. 29 | * 30 | * Queue Messages in Logging::queue if syslog daemon is not running. 31 | * This helps to read logging events at early startup process. To 32 | * indicate syslogd is running test for existence of /dev/log. 33 | * Usually syslogd does not remove /dev/log when they terminated. 34 | * It's your own fault if you stops syslogd manually and wonder of 35 | * lacking log events. 36 | * 37 | * Error and Warning messages will be send to stderr, 38 | * others to stdout. 39 | */ 40 | 41 | #ifndef LOGGING_HH 42 | #define LOGGING_HH 43 | 44 | #include "intl.hh" 45 | 46 | #include 47 | #include 48 | 49 | enum LogLevel 50 | { 51 | Error = 1, 52 | Warn = 2, 53 | Notice = 4, 54 | Info = 8, 55 | Debug = 16 56 | }; 57 | 58 | struct QueuedEvent 59 | { 60 | QueuedEvent(LogLevel l, std::string m) 61 | : level(l), msg(m) {} 62 | LogLevel level; 63 | std::string msg; 64 | }; 65 | 66 | class Logging 67 | { 68 | public: 69 | Logging(); 70 | ~Logging(); 71 | void setLogLevel(int l); 72 | void setVerboseLevel(int v); 73 | void write(LogLevel level, const char* format, ...); 74 | void errStream(FILE*); 75 | void outStream(FILE*); 76 | void redirectStdout2Stderr(bool); 77 | private: 78 | void dumpQueue(); 79 | bool targetAvailable(); 80 | void log2target(LogLevel level, const char* msg); 81 | bool redirectOut2Err; 82 | bool displayToolName; 83 | int loglevel; 84 | int verboselevel; 85 | /* 86 | * Because the heap based Config object is destroyed a little earlier 87 | * cache the target path to be able to dump queued messages in destructor 88 | */ 89 | std::string target; 90 | std::deque queue; 91 | }; 92 | 93 | extern Logging logger; 94 | 95 | #define dump_log(...) logger.write(__VA_ARGS__) 96 | 97 | #ifdef DEBUG_ENABLED 98 | #define debug(format,args...) dump_log(Debug, "%s:%d in %s(): "format, __FILE__, __LINE__, __FUNCTION__, ## args) 99 | #else 100 | #define debug(format,args...) 101 | #endif 102 | 103 | #define error(...) dump_log(Error, __VA_ARGS__) 104 | #define warn(...) dump_log(Warn, __VA_ARGS__) 105 | #define info(...) dump_log(Info, __VA_ARGS__) 106 | #define notice(...) dump_log(Notice, __VA_ARGS__) 107 | 108 | #endif 109 | -------------------------------------------------------------------------------- /doc/pt_BR/e4rat-lite-realloc.pod: -------------------------------------------------------------------------------- 1 | =encoding utf8 2 | =pod 3 | 4 | =head1 NOME 5 | 6 | e4rat-lite-realloc - Realocação e desfragmentação de arquivos relevantes. 7 | 8 | =head1 SINOPSE 9 | 10 | B B<[> opções B<]> B<[> modo B<]> arquivo(s) 11 | 12 | =head1 DESCRIÇÃO 13 | 14 | e4rat-lite-realloc é um relevante programa de desfragmentação de arquivos baseado no ioctl EXT4_IOC_MOVE_EXT de sistemas de arquivos ext4. Ele coloca fisicamente os arquivos em uma sequencia de blocos vizinhos no disco. O processo de realocação pode ser feito quando o sistema de arquivos ainda está montado. Por razões de segurança o acesso de gravação para cada arquivo é necessário. 15 | 16 | Se você quiser processar arquivos de um programa, é recomendado matar o processo primeiro. O Linux não permite que se escreva em arquivos abertos ou em execução. Portanto, não é possível processar o executável binário do programa, estes arquivos são excluidos automaticamente. 17 | 18 | Você pode usar uma lista de arquivos para serem processados. A lista de l precisa estar no formato gerado pelo e4rat-lite-collect. 19 | 20 | =head1 OPÇÕES 21 | 22 | =over 23 | 24 | =item -V --version 25 | 26 | mostra informações sobre versão e sai. 27 | 28 | =item -h --help 29 | 30 | mostra informações de uso e sai. 31 | 32 | =item -v --verbose 33 | 34 | incrementa o level verboso. 35 | 36 | =item -q --quiet 37 | 38 | define o level verboso para 0. Isso faz com que nenhuma mensagem seja exibida. 39 | 40 | =item -l --loglevel 41 | 42 | define o loglevel para . Todas as mensagens de log são enviadas para o log do Kernel (leia dmesh(1) ou syslog(3)). 43 | 44 | =back 45 | 46 | =head1 MODOS DE DESFRAGMENTAÇÃO 47 | 48 | Use o arquivo de configuração para alterar os modos de desfragmentação. 49 | 50 | =over 51 | 52 | =item Pré Alocação (pa) 53 | 54 | Use a pré alocação ioctl para reorganizar os arquivos no disco. Este modo também remove lacunas de blocos não alocados em arquivos esparsos. Infelizmente, esse ioctl está atualmente sobre pesado desenvolvimento por Kazuya Mio e não está inserido no Kernel Linux. Por isso você tem que aplicar este patch ao kernel primeiro: http://kt1.osuosl.org/mailarchive/linux-fsdevel/2010/12/1/6887724 55 | Faça isso por sua própria conta e risco. 56 | 57 | =item Grupos de localidades (locality-group) 58 | 59 | O sistema de arquivos ext4 aloca pequenos arquivos em grupos chamados de localidade. Cada CPU gerencia o seu próprio grupo. O método de "grupo de localidades" faz uso deste recurso a fim de organizar os arquivos no disco. Em contraste com o método de pré alocação, não é necessário patchear o kernel, no entanto, a atribuição deste é ideal. 60 | Enquanto a criação de arquivos doadores está em progresso, é necessário aumentar o limite de arquivos pequenos. Como resultado de outros pedidos de alocação que ocorrem ao mesmo tempo, também estará de acordo com blocos dos grupos de localidades. Esse é o melhor se você quiser evitar a escrita pesada no disco. 61 | 62 | =item Diretório de nível superior (tld) 63 | 64 | Com este método, todos os arquivos doadores são criados em uma novo diretório de nível superior. 65 | O chamado algorítimo de Orlov coloca o diretório de nível superior em um grupo de blocos vazio. Como resultado, todos os arquivos doadores serão alocados nesse diretório. Mas não há garantia de que os arquivos doadores serão alocados em sua ordem de acesso. 66 | 67 | Além disso, o alocador de blocos ext4's normaliza todas as requisições de blocos para uma potencia de 2. Isso normalmente leva a pequenos buracos entre os arquivos. 68 | 69 | =back 70 | 71 | =head1 ARQUIVOS 72 | 73 | F 74 | Arquivo de configuração do e4rat-lite 75 | 76 | =head1 AUTOR 77 | 78 | e4rat foi escrito por Andreas Rid e Gunfolf Kiefer. 79 | e4rat-lite escrito por Lara Maia. 80 | 81 | =head1 REPORTANDO BUGS 82 | 83 | Reporte bugs para Lara Maia 84 | 85 | =head1 LEIA TAMBÉM 86 | 87 | e4rat-lite-collect(8), e4rat-lite-preload(8), e4rat-lite.conf(8) 88 | -------------------------------------------------------------------------------- /src/signals.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * signals.hh - Macros allow easy signal/slot handling 3 | * 4 | * Copyright (C) 2011 by Andreas Rid 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | * 19 | * 20 | * Following macros are based on boost::signals2 which is thread-safe 21 | * 22 | * Declare a signal witch SIGNAL() only in c++ class statement. 23 | * Use CONNECT() and DISCONNECT() to link or unlink a signal with a specific slot 24 | * 25 | * Exemplary usage: 26 | * 27 | * Class Foo { 28 | * SIGNAL(mysig, int); 29 | * public: 30 | * } 31 | * Class Bar { 32 | * public: 33 | * void mylot(int); 34 | * }; 35 | * void main() 36 | * { 37 | * Foo foo; 38 | * Bar bar; 39 | * CONNECT(&foo, mysig, boost::bind(&Bar::myslot, &bar, _1)); 40 | * } 41 | */ 42 | #include 43 | 44 | //get the number of arguments 45 | #define BIND1ST _1 46 | #define BIND2ST _1,_2 47 | #define BIND3ST _1,_2,_3 48 | #define BIND4ST _1,_2,_3,_4 49 | #define BIND5ST _1,_2,_3,_4,_5 50 | 51 | #define VA_NUM_ARGS(...) VA_NUM_ARGS_IMPL(__VA_ARGS__, \ 52 | BIND5ST, \ 53 | BIND4ST, \ 54 | BIND3ST, \ 55 | BIND2ST, \ 56 | BIND1ST) 57 | #define VA_NUM_ARGS_IMPL(_1,_2,_3,_4,_5,N,...)N 58 | 59 | #define DECLARE_SIGNAL(signame, args...) \ 60 | boost::signals2::signal signame 61 | 62 | #define DECLARE_CONNECT_FUNCTION(signame, args...) \ 63 | boost::signals2::connection doOn##signame( \ 64 | const boost::signals2::signal::slot_type &subscriber)\ 65 | { \ 66 | return signame.connect(subscriber); \ 67 | } 68 | 69 | 70 | #define DECLARE_DISCONNECT_FUNCTION(signame, args...) \ 71 | template \ 72 | void undoOn##signame(void (T::*func)(args), T* obj) \ 73 | { \ 74 | if(sizeof(#args) == sizeof("")) \ 75 | signame.disconnect(boost::bind(func, obj)); \ 76 | else \ 77 | signame.disconnect(boost::bind(func, obj, VA_NUM_ARGS(args))); \ 78 | } 79 | 80 | 81 | #define SIGNAL(name, args...) \ 82 | public: \ 83 | DECLARE_CONNECT_FUNCTION(name,args) \ 84 | DECLARE_DISCONNECT_FUNCTION(name,args) \ 85 | protected: \ 86 | DECLARE_SIGNAL(name,args) 87 | 88 | 89 | 90 | #define CONNECT(sender, signal, slot) (sender)->doOn##signal(slot) 91 | #define DISCONNECT(sender, signal, slot, me) (sender)->undoOn##signal(slot, me) 92 | 93 | 94 | -------------------------------------------------------------------------------- /src/buddycache.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * buddycache.cc - Linux Kernel ext4 buddy cache 3 | * 4 | * Copyright (C) 2011 by Andreas Rid 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include "buddycache.hh" 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | BuddyCache::BuddyCache(Device _device) 27 | : device(_device) 28 | { 29 | refresh(); 30 | } 31 | 32 | void BuddyCache::refresh() 33 | { 34 | if(data.size()) 35 | data.clear(); 36 | 37 | std::filebuf fb; 38 | if (NULL == fb.open ( (std::string("/proc/fs/ext4/") 39 | + device.getDeviceName() 40 | + "/mb_groups").c_str(), std::ios::in)) 41 | 42 | throw std::runtime_error(std::string( 43 | _("cannot open buddy cache on device ") 44 | + device.getDeviceName())); 45 | 46 | 47 | std::istream buddy_stream(&fb); 48 | std::string line; 49 | int group; 50 | //skip first line 51 | getline (buddy_stream,line); 52 | 53 | for(unsigned int i = 0; i< device.getGroupCount(); i++) 54 | { 55 | getline (buddy_stream,line); 56 | struct BuddyGroup p; 57 | sscanf(line.c_str(), "#%d : %hu %hu %hu [ %d %d %d %d %d %d %d %d %d %d %d %d %d %d", 58 | &group,&p.free, &p.frags, &p.first, 59 | (int*)&p.s0, (int*)&p.s1,(int*)&p.s2,(int*)&p.s3,(int*)&p.s4,(int*)&p.s5, 60 | (int*)&p.s6,(int*)&p.s7,(int*)&p.s8, (int*)&p.s9,(int*)&p.s10,(int*)&p.s11, 61 | (int*)&p.s12,(int*)&p.s13); 62 | data.push_back(p); 63 | } 64 | } 65 | 66 | BuddyGroup& BuddyCache::at(int i) 67 | { 68 | return data.at(i); 69 | } 70 | 71 | /* 72 | * check weather flex group is empty 73 | */ 74 | bool BuddyCache::isFlexEmpty(__u32 flex) 75 | { 76 | int bg_start = flex << device.getLogGroupsPerFlex(); 77 | __u64 total_free = 0; 78 | 79 | for (__u32 i = bg_start; 80 | i < (flex << device.getLogGroupsPerFlex()) 81 | && i < device.getGroupCount(); 82 | i++) 83 | { 84 | total_free += data.at(i).free; 85 | } 86 | 87 | if(total_free == device.freeBlocksPerFlex()) 88 | return true; 89 | 90 | return true; 91 | } 92 | 93 | int BuddyCache::findEmptyFlex() 94 | { 95 | int total_flex_cnt = device.getGroupCount()<< device.getLogGroupsPerFlex(); 96 | 97 | for(int i = 0; i < total_flex_cnt; i++) 98 | { 99 | if(isFlexEmpty(i)) 100 | return i; 101 | } 102 | return -1; 103 | } 104 | 105 | bool BuddyCache::isGroupEmpty(__u32 group) 106 | { 107 | int free_blocks = device.getBlocksPerGroup(); 108 | if(0 == group % (1< free_blocks) 116 | throw std::logic_error(_("more blocks marked free than expected")); 117 | 118 | return false; 119 | } 120 | 121 | int BuddyCache::findEmptyGroup() 122 | { 123 | for(__u32 i = 0; i < device.getGroupCount(); i++) 124 | if(isGroupEmpty(i)) 125 | return i; 126 | return -1; 127 | } 128 | 129 | -------------------------------------------------------------------------------- /src/listener.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * listener.hh - Listen to the Linux audit socket 3 | * 4 | * Copyright (C) 2011 by Andreas Rid 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #ifndef LISTENER_HH 21 | #define LISTENER_HH 22 | 23 | #include "common.hh" 24 | #include "signals.hh" 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | typedef struct opaque auparse_state_t; 31 | 32 | // All known events emitted by AuditListener 33 | enum AuditEventType 34 | { 35 | Unknown, 36 | Open, 37 | OpenAt, 38 | Execve, 39 | Truncate, 40 | Creat, 41 | Fork, 42 | }; 43 | 44 | // AuditEvent is an structure emitted by eventParsed() signal. 45 | class AuditEvent 46 | { 47 | public: 48 | AuditEvent(); 49 | AuditEventType type; 50 | pid_t pid; 51 | pid_t ppid; 52 | std::string comm; 53 | fs::path exe; 54 | fs::path path; 55 | fs::path cwd; 56 | ino_t ino; 57 | dev_t dev; 58 | pid_t exit; 59 | bool readOnly; 60 | bool successful; 61 | }; 62 | 63 | 64 | /* 65 | * Linux Audit Listener Class 66 | * It connects directly to the Linux audit socket. 67 | * 68 | * Signal eventParsed() is emitted on every successfully parsed event. 69 | */ 70 | class AuditListener : public Interruptible 71 | { 72 | SIGNAL(eventParsed, boost::shared_ptr); 73 | 74 | public: 75 | AuditListener(); 76 | ~AuditListener(); 77 | void excludePath(std::string); 78 | void watchPath(std::string); 79 | void watchFileSystemType(long); 80 | void excludeDevice(std::string); 81 | void watchDevice(std::string); 82 | void watchExt4Only(bool = true); 83 | protected: 84 | virtual void exec(); 85 | void insertAuditRules(); 86 | void removeAuditRules(); 87 | void activateAuditSocket(); 88 | void closeAuditSocket(); 89 | std::string parseField(auparse_state_t*, const char*); 90 | std::string parsePathField(auparse_state_t*, const char*); 91 | private: 92 | void activateRules(int machine); 93 | void waitForEvent(struct audit_reply* reply); 94 | auparse_state_t* initAuParse(struct audit_reply*); 95 | void parseCwdEvent(auparse_state_t*, boost::shared_ptr); 96 | void parsePathEvent(auparse_state_t*, boost::shared_ptr); 97 | void parseSyscallEvent(auparse_state_t*, boost::shared_ptr); 98 | bool ignorePath(fs::path&); 99 | bool ignoreDevice(dev_t dev); 100 | bool checkFileSystemType(fs::path& p); 101 | 102 | std::vector rule_vec; 103 | int auditFlags; 104 | int auditAction; 105 | int audit_fd; 106 | std::vector exclude_paths; 107 | std::vector watch_paths; 108 | std::set watch_devices; 109 | std::set exclude_devices; 110 | std::set watch_fs_types; 111 | bool ext4_only; 112 | std::setext4_devices_cache; 113 | }; 114 | 115 | /* 116 | * Single threaded Linux Audit Listener 117 | */ 118 | class Listener : public AuditListener 119 | { 120 | public: 121 | virtual ~Listener(); 122 | void connect(); 123 | bool start(); 124 | void stop(); 125 | }; 126 | 127 | #endif 128 | -------------------------------------------------------------------------------- /src/device.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * device.hh - get/set paramters for a block device 3 | * 4 | * Copyright (C) 2011 by Andreas Rid 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #ifndef DEVICE_HH 21 | #define DEVICE_HH 22 | 23 | #include "common.hh" 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | 30 | fs::path __getMountPoint(fs::path path); 31 | 32 | 33 | /* 34 | * Physical extent of free blocks. On disk start block and length. 35 | * Unlike ext4 extents this class does not store logical file offset 36 | */ 37 | class Extent 38 | { 39 | public: 40 | Extent(__u64 s, __u32 l) : start(s), len(l) {} 41 | Extent() : start(0), len(0) {} 42 | unsigned long long start; 43 | size_t len; 44 | }; 45 | 46 | 47 | /* 48 | * Private Data of Device Object. 49 | */ 50 | class DevicePrivate 51 | { 52 | friend class Device; 53 | public: 54 | ~DevicePrivate(); 55 | private: 56 | DevicePrivate(); 57 | 58 | ext2_filsys fs; 59 | dev_t devno; 60 | std::string deviceName; // Linux block device name. Example: "sda1" 61 | std::string devicePath; 62 | fs::path mount_point; 63 | std::string fs_name; // name of the filesystem. Example: "ext4" 64 | }; 65 | 66 | /* 67 | * Device is the source of information of a specific filesystem. 68 | * To create this class a file of the desired filesystem is needed. 69 | * 70 | * Device is derived from shared_ptr. So all created copies have the 71 | * same Private Data. Shared pointer is responsible for destructing of the 72 | * ext2_filsys object. 73 | */ 74 | class Device: private boost::shared_ptr 75 | { 76 | public: 77 | //file is a path belongs to device/filesystem 78 | //it is not the path to the device themself 79 | Device(fs::path file); 80 | Device(dev_t); 81 | bool open(); 82 | std::string getDeviceName(); 83 | std::string getDevicePath(); 84 | fs::path getMountPoint(); 85 | std::string getFileSystem(); 86 | void setTuningParameter(std::string, unsigned int val); 87 | __u32 getTuningParameter(std::string); 88 | bool hasExtentFeature(); 89 | __u32 getBlockSize(); 90 | __u64 freeBlocksPerFlex(); 91 | __u64 freeBlocksPerGroup(); 92 | __u32 getBlocksPerGroup(); 93 | __u32 getGroupCount(); 94 | __u32 getLogGroupsPerFlex(); 95 | 96 | void preallocate(int fd, 97 | __u64 physical, 98 | __u32 logical, 99 | __u32 len, 100 | __u16 flags); 101 | 102 | void moveExtent( int orig_fd, 103 | int donor_fd, 104 | __u64 logical, //logical block offset 105 | __u64 len); //block count to be moved 106 | bool operator<(const Device&) const; 107 | private: 108 | int getDevNameFromDevfs(); 109 | int getDevNameFromMajorMinor(); 110 | void parseMtab(); 111 | void parseMtabFile(const char* path); 112 | void openSysFsExt4File( 113 | std::filebuf* fb, 114 | std::string filename, 115 | std::ios_base::openmode mode); 116 | }; 117 | 118 | #endif 119 | -------------------------------------------------------------------------------- /src/logging.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * logging.cc - Display screen and or send event to klog or syslog 3 | * 4 | * Copyright (C) 2011 by Andreas Rid 5 | * Copyright (C) 2012 by Lara Maia 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #include "logging.hh" 22 | #include "common.hh" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | Logging logger; 34 | 35 | void Logging::log2target(LogLevel level, const char* msg) 36 | { 37 | if(target == "syslog") 38 | { 39 | if(access(_PATH_LOG, F_OK)) 40 | throw std::runtime_error(_("syslog daemon is not running")); 41 | syslog((level/2)+2, msg, NULL); 42 | } 43 | else 44 | { 45 | if(target == "/dev/kmsg") 46 | if(access(target.c_str(), W_OK)) 47 | throw std::runtime_error(strerror(errno)); 48 | 49 | FILE* file = fopen(target.c_str(), "a"); 50 | if(!file) 51 | throw std::runtime_error(strerror(errno)); 52 | 53 | fprintf(file, _("[Logging] %s\n"), msg); 54 | fclose(file); 55 | } 56 | } 57 | 58 | Logging::Logging() 59 | { 60 | redirectOut2Err = false; 61 | verboselevel = Error; 62 | loglevel = Error; 63 | openlog(NULL, LOG_PID, 0); 64 | 65 | if(getpid() == 1) 66 | displayToolName = true; 67 | else 68 | displayToolName = false; 69 | 70 | target = "/dev/kmsg"; 71 | } 72 | 73 | Logging::~Logging() 74 | { 75 | try { 76 | dumpQueue(); 77 | } 78 | catch(std::exception& e) 79 | { 80 | fprintf(stderr, _("Cannot dump log messages: %s: %s\n"), 81 | target.c_str(), 82 | e.what()); 83 | } 84 | 85 | if(queue.size()) 86 | fprintf(stderr, _("Discard %zu unwritten log message(s).\n"), queue.size()); 87 | } 88 | 89 | void Logging::setLogLevel(int l) 90 | { 91 | loglevel = l; 92 | } 93 | 94 | void Logging::setVerboseLevel(int v) 95 | { 96 | verboselevel = v; 97 | } 98 | 99 | void Logging::dumpQueue() 100 | { 101 | while(queue.size()) 102 | { 103 | QueuedEvent& event = queue.front(); 104 | log2target(event.level, event.msg.c_str()); 105 | queue.pop_front(); 106 | } 107 | } 108 | void Logging::write(LogLevel level, const char* format, ...) 109 | { 110 | #define MSG_SIZE 8192 111 | char msg[MSG_SIZE]; 112 | va_list args; 113 | va_start(args, format); 114 | 115 | vsnprintf(msg, MSG_SIZE, format, args); 116 | 117 | if((level & verboselevel)) 118 | { 119 | FILE* out; 120 | if(redirectOut2Err || level == Error || level == Warn) 121 | out = stderr; 122 | else 123 | out = stdout; 124 | 125 | if(displayToolName) 126 | fprintf(out, _("[Logging] %s\n"), msg); 127 | else 128 | fprintf(out, _("%s\n"), msg); 129 | } 130 | 131 | if(!(level & loglevel)) 132 | goto out; 133 | 134 | try { 135 | target = "/dev/kmsg"; 136 | dumpQueue(); 137 | log2target(level, msg); 138 | } 139 | catch(...) 140 | { 141 | queue.push_back(QueuedEvent(level, msg)); 142 | } 143 | 144 | out: 145 | va_end(args); 146 | } 147 | 148 | void Logging::redirectStdout2Stderr(bool s) 149 | { 150 | redirectOut2Err = s; 151 | } 152 | -------------------------------------------------------------------------------- /src/fiemap.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * FS_IOC_FIEMAP ioctl infrastructure and ioctl functions 3 | * 4 | * Some portions copyright (C) 2007 Cluster File Systems, Inc 5 | * 6 | * Authors: Mark Fasheh 7 | * Kalpak Shah 8 | * Andreas Dilger 9 | * 10 | * Add some function declarations based on fiemap ioctl. 11 | * Andreas Rid 12 | */ 13 | 14 | #ifndef _LINUX_FIEMAP_H 15 | #define _LINUX_FIEMAP_H 16 | 17 | #include 18 | #include 19 | 20 | struct fiemap_extent { 21 | __u64 fe_logical; /* logical offset in bytes for the start of 22 | * the extent from the beginning of the file */ 23 | __u64 fe_physical; /* physical offset in bytes for the start 24 | * of the extent from the beginning of the disk */ 25 | __u64 fe_length; /* length in bytes for this extent */ 26 | __u64 fe_reserved64[2]; 27 | __u32 fe_flags; /* FIEMAP_EXTENT_* flags for this extent */ 28 | __u32 fe_reserved[3]; 29 | }; 30 | 31 | struct fiemap { 32 | __u64 fm_start; /* logical offset (inclusive) at 33 | * which to start mapping (in) */ 34 | __u64 fm_length; /* logical length of mapping which 35 | * userspace wants (in) */ 36 | __u32 fm_flags; /* FIEMAP_FLAG_* flags for request (in/out) */ 37 | __u32 fm_mapped_extents; /* number of extents that were mapped (out) */ 38 | __u32 fm_extent_count; /* size of fm_extents array (in) */ 39 | __u32 fm_reserved; 40 | struct fiemap_extent fm_extents[0]; /* array of mapped extents (out) */ 41 | }; 42 | 43 | #ifndef FS_IOC_FIEMAP 44 | #define FS_IOC_FIEMAP _IOWR('f', 11, struct fiemap) 45 | #endif 46 | 47 | #define FIEMAP_MAX_OFFSET (~0ULL) 48 | 49 | #define FIEMAP_FLAG_SYNC 0x00000001 /* sync file data before map */ 50 | #define FIEMAP_FLAG_XATTR 0x00000002 /* map extended attribute tree */ 51 | 52 | #define FIEMAP_FLAGS_COMPAT (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR) 53 | 54 | #define FIEMAP_EXTENT_LAST 0x00000001 /* Last extent in file. */ 55 | #define FIEMAP_EXTENT_UNKNOWN 0x00000002 /* Data location unknown. */ 56 | #define FIEMAP_EXTENT_DELALLOC 0x00000004 /* Location still pending. 57 | * Sets EXTENT_UNKNOWN. */ 58 | #define FIEMAP_EXTENT_ENCODED 0x00000008 /* Data can not be read 59 | * while fs is unmounted */ 60 | #define FIEMAP_EXTENT_DATA_ENCRYPTED 0x00000080 /* Data is encrypted by fs. 61 | * Sets EXTENT_NO_BYPASS. */ 62 | #define FIEMAP_EXTENT_NOT_ALIGNED 0x00000100 /* Extent offsets may not be 63 | * block aligned. */ 64 | #define FIEMAP_EXTENT_DATA_INLINE 0x00000200 /* Data mixed with metadata. 65 | * Sets EXTENT_NOT_ALIGNED.*/ 66 | #define FIEMAP_EXTENT_DATA_TAIL 0x00000400 /* Multiple files in block. 67 | * Sets EXTENT_NOT_ALIGNED.*/ 68 | #define FIEMAP_EXTENT_UNWRITTEN 0x00000800 /* Space allocated, but 69 | * no data (i.e. zero). */ 70 | #define FIEMAP_EXTENT_MERGED 0x00001000 /* File does not natively 71 | * support extents. Result 72 | * merged for efficiency. */ 73 | 74 | 75 | struct fiemap* ioctl_fiemap(int fd, unsigned int extent_count = 0); 76 | struct fiemap* get_fiemap(const char* file); 77 | bool is_sparse_file(struct fiemap* fmap); 78 | __u64 get_allocated_file_size(const char* file); 79 | __u64 get_allocated_file_size(struct fiemap* fmap); 80 | __u64 get_file_size(int fd); 81 | __u64 get_file_size(struct fiemap* fmap); 82 | __u32 get_frag_count(int fd); 83 | #endif /* _LINUX_FIEMAP_H */ 84 | -------------------------------------------------------------------------------- /doc/en_US/e4rat-lite-collect.pod: -------------------------------------------------------------------------------- 1 | =encoding utf8 2 | =pod 3 | 4 | =head1 NAME 5 | 6 | e4rat-lite-collect - Generate a list of relevant files by monitoring applications 7 | 8 | =head1 SYNOPSIS 9 | 10 | B B<[> option(s) B<]> B<[> application name(s) B<]> 11 | 12 | =head1 DESCRIPTION 13 | 14 | e4rat-lite-collect listens to the Linux audit socket to monitor filesystem activities and generates a list of relevant files retaining their access order. 15 | Temporary files and/or files opened (running) like log-files, are excluded automatically. 16 | 17 | You can either monitor applications or the entire operating system. If an I is supplied, the processes to monitor are selected according to their process name, which is usually the name of the running executable and shown by the ps(1) command. Paths are removed from the process names. The application you specify are just monitored and does not get executed. see I<--execute>. 18 | In addition, the collector follows child processes, which allows to monitor shell scripts correctly. 19 | 20 | To stop scanning process press CTRL-C or run `e4rat-lite-collect -k'. Unless otherwise stated, the generated file list is written to '/var/lib/e4rat-lite/startup.log' or the file specified in the configuration. 21 | 22 | =head1 OPTIONS 23 | 24 | Some options require a path to a file, directory or device. Feel free to use relative paths and or paths containing wildcard characters like '*' or '?'. 25 | 26 | =over 27 | 28 | =item -V --version 29 | 30 | show version information and exit. 31 | 32 | =item -h --help 33 | 34 | display usage message and exit. 35 | 36 | =item -v --verbose 37 | 38 | increment verbosity level. 39 | 40 | =item -q --quiet 41 | 42 | set verbose level to 0. This means that no messages will be displayed. 43 | 44 | =item -l --loglevel 45 | 46 | set loglevel mask to . All log messages are sent either to the Kernel log (see dmesg(1)) or to syslog(3). 47 | 48 | =item -k --stop 49 | 50 | kill already running e4rat-lite-collect process. 51 | 52 | =item -x --execute 53 | 54 | collect while executing command. e4rat-lite-collect stops running after terminates. Be aware that gets executed with root privileges if no username is specified. See l<--user>. 55 | 56 | =item -u --user 57 | 58 | specify username the command gets executed. See l<--execute>. 59 | 60 | =item -o --output [file] 61 | 62 | set the output file in which the generated file list is written to. If no path is specified, the list is written to stdout. 63 | 64 | =item -d --device 65 | 66 | =item -D --exclude-device 67 | 68 | limit monitoring to a special device. is a path to device normally in /dev. 69 | [example: /dev/sda1] 70 | 71 | =item -p --path 72 | 73 | =item -P --exclude-path 74 | 75 | limit monitoring to a special path. 76 | [example: '*/bin/*'] 77 | 78 | =item -L --exclude-list 79 | 80 | The contains a list of files, which e4rat-lite-collect should exclude. 81 | 82 | =back 83 | 84 | =head1 EXAMPLES 85 | 86 | =head2 Scan Thunderbird and a locally running Imap daemon 87 | 88 | =head4 Run collector as root: 89 | 90 | ~# sudo e4rat-lite-collect thunderbird imapd 91 | 92 | =head4 Once collector is running execute thunderbird as a normal user: 93 | 94 | ~# thunderbird 95 | 96 | =head4 Alternatively combine it to one call 97 | 98 | ~# sudo e4rat-lite-collect thunderbird imapd --execute 'thunderbird' --user `whoami` 99 | 100 | =head2 Scan boot process: 101 | 102 | Run e4rat-lite-collect as early as possible to scan the whole startup process. It is recommended to run e4rat-lite-collect as init process. To do so, add the following lines to the Kernel parameters in grub(8) or lilo(8). 103 | init=/usr/bin/e4rat-lite-collect 104 | 105 | =head1 FILES 106 | 107 | F 108 | E4rat-lite configuration file. 109 | 110 | =head1 AUTHOR 111 | 112 | e4rat has been written by Andreas Rid and Gundolf Kiefer. 113 | e4rat-lite writen by Lara Maia. 114 | 115 | =head1 REPORTING BUGS 116 | 117 | Report bugs to Lara Maia 118 | 119 | =head1 SEE ALSO 120 | 121 | e4rat-lite-realloc(8), e4rat-lite-preload(8), e4rat-lite.conf(8) 122 | -------------------------------------------------------------------------------- /kernel-patches/delalloc-debug-ioctl: -------------------------------------------------------------------------------- 1 | ext4: add a delayed allocation debugging ioctl 2 | 3 | The debugging code must be triggered as root, using the following 4 | program: 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #if (!defined(EXT4_IOC_DEBUG_DELALLOC) && defined(__linux__)) 13 | #define EXT4_IOC_DEBUG_DELALLOC _IO('f', 42) 14 | #endif 15 | 16 | int main(int argc, char **argv) 17 | { 18 | int fd; 19 | 20 | if (argc != 2) { 21 | fprintf(stderr, "Usage: %s disk\n", argv[0]); 22 | exit(1); 23 | } 24 | fd = open(argv[1], O_RDONLY, 0); 25 | if (fd < 0) { 26 | perror("open"); 27 | exit(1); 28 | } 29 | if (ioctl(fd, EXT4_IOC_DEBUG_DELALLOC, 0) < 0) { 30 | perror("ioctl EXT4_IOC_DEBUG_DELALLOC"); 31 | exit(1); 32 | } 33 | return 0; 34 | } 35 | 36 | Signed-off-by: "Theodore Ts'o" 37 | --- 38 | fs/ext4/ext4.h | 2 + 39 | fs/ext4/ioctl.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 40 | 2 files changed, 68 insertions(+), 0 deletions(-) 41 | 42 | diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h 43 | index e9ddbe2..f015eaa 100644 44 | --- a/fs/ext4/ext4.h 45 | +++ b/fs/ext4/ext4.h 46 | @@ -533,6 +533,8 @@ struct ext4_new_group_data { 47 | #endif 48 | 49 | 50 | +#define EXT4_IOC_DEBUG_DELALLOC _IO('f', 42) 51 | + 52 | /* 53 | * Mount options 54 | */ 55 | diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c 56 | index 9fb2d01..9673a40 100644 57 | --- a/fs/ext4/ioctl.c 58 | +++ b/fs/ext4/ioctl.c 59 | @@ -15,9 +15,21 @@ 60 | #include 61 | #include 62 | #include 63 | +#include 64 | #include "ext4_jbd2.h" 65 | #include "ext4.h" 66 | 67 | +static void print_inode_dealloc_info(struct inode *inode) 68 | +{ 69 | + if (!EXT4_I(inode)->i_reserved_data_blocks || 70 | + !EXT4_I(inode)->i_reserved_meta_blocks) 71 | + return; 72 | + 73 | + printk(KERN_DEBUG "ino %lu: %u %u\n", inode->i_ino, 74 | + EXT4_I(inode)->i_reserved_data_blocks, 75 | + EXT4_I(inode)->i_reserved_meta_blocks); 76 | +} 77 | + 78 | long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 79 | { 80 | struct inode *inode = filp->f_dentry->d_inode; 81 | @@ -331,6 +343,59 @@ mext_out: 82 | return err; 83 | } 84 | 85 | + case EXT4_IOC_DEBUG_DELALLOC: 86 | + { 87 | +#ifndef MODULE 88 | + extern spinlock_t inode_lock; 89 | +#endif 90 | + struct super_block *sb = inode->i_sb; 91 | + struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); 92 | + struct inode *inode; 93 | + 94 | + if (!capable(CAP_SYS_ADMIN)) 95 | + return -EPERM; 96 | + 97 | + printk(KERN_DEBUG "EXT4-fs debug delalloc of %s\n", sb->s_id); 98 | + printk(KERN_DEBUG "EXT4-fs: dirty blocks %lld free blocks %lld\n", 99 | + percpu_counter_sum(&sbi->s_dirtyblocks_counter), 100 | + percpu_counter_sum(&sbi->s_freeblocks_counter)); 101 | +#ifdef MODULE 102 | + /* Yuck; but the inode_lock spinlock is not exported */ 103 | + lock_kernel(); 104 | +#else 105 | + spin_lock(&inode_lock); 106 | +#endif 107 | + if (!list_empty(&sb->s_bdi->wb.b_dirty)) { 108 | + printk(KERN_DEBUG "s_bdi->wb.b_dirty list:\n"); 109 | + list_for_each_entry(inode, &sb->s_bdi->wb.b_dirty, 110 | + i_list) { 111 | + print_inode_dealloc_info(inode); 112 | + } 113 | + } 114 | + if (!list_empty(&sb->s_bdi->wb.b_io)) { 115 | + printk(KERN_DEBUG "s_bdi->wb.b_io list:\n"); 116 | + list_for_each_entry(inode, &sb->s_bdi->wb.b_io, 117 | + i_list) { 118 | + print_inode_dealloc_info(inode); 119 | + } 120 | + } 121 | + if (!list_empty(&sb->s_bdi->wb.b_more_io)) { 122 | + printk(KERN_DEBUG "s_bdi->wb.b_more_io list:\n"); 123 | + list_for_each_entry(inode, &sb->s_bdi->wb.b_more_io, 124 | + i_list) { 125 | + print_inode_dealloc_info(inode); 126 | + } 127 | + } 128 | +#ifdef MODULE 129 | + lock_kernel(); 130 | +#else 131 | + spin_unlock(&inode_lock); 132 | +#endif 133 | + printk(KERN_DEBUG "ext4 debug delalloc done\n"); 134 | + return 0; 135 | + } 136 | + 137 | + 138 | default: 139 | return -ENOTTY; 140 | } 141 | @@ -397,6 +462,7 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 142 | return err; 143 | } 144 | case EXT4_IOC_MOVE_EXT: 145 | + case EXT4_IOC_DEBUG_DELALLOC: 146 | break; 147 | default: 148 | return -ENOIOCTLCMD; 149 | -------------------------------------------------------------------------------- /doc/pt_BR/e4rat-lite-collect.pod: -------------------------------------------------------------------------------- 1 | =encoding utf8 2 | =pod 3 | 4 | =head1 NOME 5 | 6 | e4rat-lite-collect - Gera uma lista de arquivos relevantes pelo monitoramento de aplicativos 7 | 8 | =head1 SINOPSE 9 | 10 | B B<[> opções B<]> B<[> nome do(s) aplicativo(s) B<]> 11 | 12 | =head1 DESCRIÇÃO 13 | 14 | O e4rat-lite-collect escuta o socket de auditoria do Linux para monitorar as atividades do sistema de arquivos e gerar uma lista de arquivos revelantes, retendo pela ordem de acesso. 15 | Arquivos temporários e/ou arquivos abertos (em execução) como arquivos de log, são ignorados automaticamente. 16 | 17 | Você pode monitorar aplicativos ou o sistema operacional inteiro. Se um l for fornecido, os processos monitorados serão selecionados de acordo com o nome do processo, que usualmente é o nome do executável em execução e é mostrado pelo comando ps(1). Os caminhos são removidos do nome do processo. O aplicativo que você especificar é somente monitorado e não executado. leia l<--execute> 18 | Além disso, o coletor segue processos filhos, para permitir um monitoramento correto de shell scripts. 19 | 20 | Para parar o escaneamento do processo, precione CTRL-C ou rode `e4rat-lite-collect -k'. A lista de arquivos gerada será escrita em '/var/lib/e4rat-lite/startup.log' ou o arquivo specificado na configuração. 21 | 22 | =head1 OPÇÕES 23 | 24 | Algumas opções requerem o caminho para o arquivo, pasta ou dispositivo. Sinta-se livre para usar caminhos relativos e/ou caminhos contendo caracteres coringas como '*' ou '?'. 25 | 26 | =over 27 | 28 | =item -V --version 29 | 30 | mostra a versão e sai 31 | 32 | =item -h --help 33 | 34 | mostra a ajuda e sai 35 | 36 | =item -v --verbose 37 | 38 | incrementa o level verboso 39 | 40 | =item -q --quiet 41 | 42 | define o level verboso para 0. Isso faz com que nenhuma mensagem seja exibida. 43 | 44 | =item -l --loglevel 45 | 46 | define a mascara do loglevel para . Todas as mensagens são enviadas para o log do Kernel (veja dmesg(1) ou syslog(3)). 47 | 48 | =item -k --stop 49 | 50 | Mata o processo do e4rat-lite-collect em execução. 51 | 52 | =item -x --execute 53 | 54 | Coleta durante a execução do comando. O e4rat-lite-collect para após o terminar. Esteja ciente de que o é executado com privilégios de root se nenhum nome de usuário for especificado. Veja l<--user>. 55 | 56 | =item -u --user 57 | 58 | Especifica um nome de usuário para o comando ser executado. Veja l<--execute>. 59 | 60 | =item -o --output [arquivo] 61 | 62 | Define o arquivo de saída em que a lista de arquivos gerados serão gravados. Se nenhum caminho for especificado, a lista é escrita para o stdout. 63 | 64 | =item -d --device 65 | 66 | =item -D --exclude-device 67 | 68 | Limita o monitoramento a um dispositivo em especial. é o caminho para o dispositivo, normalmente em /dev. 69 | [exemplo: /dev/sda1] 70 | 71 | =item -p --path 72 | 73 | =item -P --exclude-path 74 | 75 | Limita o monitoramento a uma pasta específica: 76 | [exemplo: '*/bin/*'] 77 | 78 | =item -L --exclude-list 79 | 80 | O contém uma lista de arquivos a serem ignorados pelo e4rat-lite-collect 81 | 82 | =back 83 | 84 | =head1 EXEMPLOS 85 | 86 | =head2 Escanear o Thunderbird e localmente rodar o Imap daemon 87 | 88 | =head4 Rode o coletor como ROOT 89 | 90 | ~# sudo e4rat-lite-collect thunderbird imapd 91 | 92 | =head4 Uma vez que esteja rodando o coletor, executar o Thunderbird como um usuário normal: 93 | 94 | ~# thunderbird 95 | 96 | =head4 Alternativamente combine-os com uma chamada: 97 | 98 | ~# sudo e4rat-lite-collect thunderbird imapd --execute 'thunderbird' --user `whoami` 99 | 100 | =head2 Escanear processos do boot 101 | 102 | Rode o e4rat-lite-collect o mais antes possível para escanear todo o processo de inicialização. É recomendado executar o e4rat-lite-collect como um processo de inicialização. Para isso, adicione a seguinte linha aos parâmetros do kernel em grub(8) ou lilo(8). 103 | init=/usr/bin/e4rat-lite-collect 104 | 105 | =head1 ARQUIVOS 106 | 107 | F 108 | Arquivo de configuração do e4rat-lite. 109 | 110 | =head1 AUTOR 111 | 112 | e4rat foi escrito por Andreas Rid e Gunfolf Kiefer. 113 | e4rat-lite escrito por Lara Maia. 114 | 115 | =head1 REPORTANDO BUGS 116 | 117 | Reporte bugs para Lara Maia 118 | 119 | =head1 LEIA TAMBÉM 120 | 121 | e4rat-lite-realloc(8), e4rat-lite-preload(8), e4rat-lite.conf(8) 122 | -------------------------------------------------------------------------------- /kernel-patches/ext4_sort_and_merge_inode_pa.patch: -------------------------------------------------------------------------------- 1 | fs/ext4/mballoc.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 2 | 1 file changed, 97 insertions(+), 7 deletions(-) 3 | 4 | diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c 5 | index 67809b5..9a33c33 100644 6 | --- a/fs/ext4/mballoc.c 7 | +++ b/fs/ext4/mballoc.c 8 | @@ -3363,6 +3363,43 @@ static void ext4_mb_put_pa(struct ext4_allocation_context *ac, 9 | call_rcu(&(pa)->u.pa_rcu, ext4_mb_pa_callback); 10 | } 11 | 12 | +/** 13 | + * ext4_mb_merge_preallocations - Merge two preallocations 14 | + * 15 | + * @pa1: a preallocation 16 | + * @pa2: a preallocation that is absorbed by @pa1 17 | + * 18 | + * Merge PAs into @pa1 if possible. 19 | + * If success to merge, return 1. Otherwise return 0. 20 | + */ 21 | +static noinline_for_stack int 22 | +ext4_mb_merge_preallocations(struct ext4_prealloc_space *pa1, 23 | + struct ext4_prealloc_space *pa2) 24 | +{ 25 | + struct super_block *sb = pa1->pa_inode->i_sb; 26 | + ext4_group_t pa1_grp, pa2_grp; 27 | + 28 | + ext4_get_group_no_and_offset(sb, pa1->pa_pstart, &pa1_grp, NULL); 29 | + ext4_get_group_no_and_offset(sb, pa2->pa_pstart, &pa2_grp, NULL); 30 | + 31 | + if (pa1_grp != pa2_grp) 32 | + return 0; 33 | + if (pa1->pa_lstart + pa1->pa_len != pa2->pa_lstart && 34 | + pa2->pa_lstart + pa2->pa_len != pa1->pa_lstart) 35 | + return 0; 36 | + if (pa1->pa_pstart + pa1->pa_len != pa2->pa_pstart && 37 | + pa2->pa_pstart + pa2->pa_len != pa1->pa_pstart) 38 | + return 0; 39 | + 40 | + pa1->pa_pstart = min(pa1->pa_pstart, pa2->pa_pstart); 41 | + pa1->pa_lstart = min(pa1->pa_lstart, pa2->pa_lstart); 42 | + 43 | + pa1->pa_len += pa2->pa_len; 44 | + pa1->pa_free += pa2->pa_free; 45 | + 46 | + return 1; 47 | +} 48 | + 49 | /* 50 | * creates new preallocated space for given inode 51 | */ 52 | @@ -3370,9 +3407,10 @@ static noinline_for_stack int 53 | ext4_mb_new_inode_pa(struct ext4_allocation_context *ac) 54 | { 55 | struct super_block *sb = ac->ac_sb; 56 | - struct ext4_prealloc_space *pa; 57 | + struct ext4_prealloc_space *pa, *tmp_pa, *prev = NULL, *next = NULL; 58 | struct ext4_group_info *grp; 59 | struct ext4_inode_info *ei; 60 | + int merged = 0; 61 | 62 | if (ac->ac_flags & EXT4_MB_HINT_PA_ONLY) { 63 | /* EXT4_MB_HINT_PA_ONLY makes all found space preallocated */ 64 | @@ -3455,13 +3493,65 @@ ext4_mb_new_inode_pa(struct ext4_allocation_context *ac) 65 | pa->pa_obj_lock = &ei->i_prealloc_lock; 66 | pa->pa_inode = ac->ac_inode; 67 | 68 | - ext4_lock_group(sb, ac->ac_b_ex.fe_group); 69 | - list_add(&pa->pa_group_list, &grp->bb_prealloc_list); 70 | - ext4_unlock_group(sb, ac->ac_b_ex.fe_group); 71 | + spin_lock(&ei->i_prealloc_lock); 72 | + rcu_read_lock(); 73 | + list_for_each_entry_rcu(tmp_pa, &ei->i_prealloc_list, pa_inode_list) { 74 | + if (tmp_pa->pa_deleted) 75 | + continue; 76 | + if (tmp_pa->pa_lstart > pa->pa_lstart) { 77 | + next = tmp_pa; 78 | + break; 79 | + } 80 | + prev = tmp_pa; 81 | + } 82 | + rcu_read_unlock(); 83 | 84 | - spin_lock(pa->pa_obj_lock); 85 | - list_add_rcu(&pa->pa_inode_list, &ei->i_prealloc_list); 86 | - spin_unlock(pa->pa_obj_lock); 87 | + if (next) { 88 | + BUG_ON(pa->pa_lstart + pa->pa_len > next->pa_lstart); 89 | + spin_lock(&next->pa_lock); 90 | + merged += ext4_mb_merge_preallocations(next, pa); 91 | + } 92 | + 93 | + if (prev) { 94 | + BUG_ON(prev->pa_lstart + prev->pa_len > pa->pa_lstart); 95 | + spin_lock_nested(&prev->pa_lock, SINGLE_DEPTH_NESTING); 96 | + 97 | + if (merged) { 98 | + merged += ext4_mb_merge_preallocations(prev, next); 99 | + 100 | + if (merged == 2) { 101 | + /* Prepare to discard next */ 102 | + atomic_inc(&next->pa_count); 103 | + next->pa_free = 0; 104 | + } 105 | + } else { 106 | + merged += ext4_mb_merge_preallocations(prev, pa); 107 | + } 108 | + spin_unlock(&prev->pa_lock); 109 | + } 110 | + 111 | + if (next) 112 | + spin_unlock(&next->pa_lock); 113 | + 114 | + if (!merged) { 115 | + if (prev) 116 | + list_add_rcu(&pa->pa_inode_list, &prev->pa_inode_list); 117 | + else 118 | + list_add_rcu(&pa->pa_inode_list, &ei->i_prealloc_list); 119 | + 120 | + spin_unlock(&ei->i_prealloc_lock); 121 | + ext4_lock_group(sb, ac->ac_b_ex.fe_group); 122 | + list_add(&pa->pa_group_list, &grp->bb_prealloc_list); 123 | + ext4_unlock_group(sb, ac->ac_b_ex.fe_group); 124 | + } else { 125 | + spin_unlock(&ei->i_prealloc_lock); 126 | + ac->ac_pa = NULL; 127 | + kmem_cache_free(ext4_pspace_cachep, pa); 128 | + 129 | + /* If prev and next are merged, discard next */ 130 | + if (merged == 2) 131 | + ext4_mb_put_pa(NULL, sb, next); 132 | + } 133 | 134 | return 0; 135 | } 136 | -------------------------------------------------------------------------------- /src/fileptr.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * fileptr.cc - Pointer to an unique file object 3 | * 4 | * Copyright (C) 2011 by Andreas Rid 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include "fileptr.hh" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | bool isFileUnique(const char* path) 29 | { 30 | struct stat st; 31 | if(stat(path, &st) < 0) 32 | { 33 | std::string err_msg = path; 34 | err_msg += ": Cannot receive file stat: "; 35 | err_msg += strerror(errno); 36 | throw std::runtime_error(err_msg); 37 | } 38 | 39 | FilePtr file(st.st_dev, st.st_ino, path); 40 | return file.unique(); 41 | } 42 | 43 | DEFINE_SINGLETON(FileDepot); 44 | 45 | FileDepot::FileDepot() 46 | {} 47 | FileDepot::~FileDepot() 48 | {} 49 | 50 | bool FileDepot::key_t::operator<(const FileDepot::key_t& other) const 51 | { 52 | if(this->dev < other.dev) 53 | return true; 54 | else if(this->dev == other.dev) 55 | if(this->ino < other.ino) 56 | return true; 57 | 58 | return false; 59 | } 60 | 61 | void FileDepot::insert(key_t key, FilePtr& file) 62 | { 63 | std::pair ret = files.insert(files_pair_t(key, file)); 64 | if(ret.second == false) 65 | { 66 | boost::shared_ptr a = ret.first->second.lock(); 67 | file = a; 68 | } 69 | } 70 | 71 | void FileDepot::insert(dev_t dev, ino_t ino, FilePtr& file) 72 | { 73 | key_t key; 74 | genKey(&key, dev, ino); 75 | insert(key, file); 76 | } 77 | 78 | void FileDepot::insert(FilePtr& file) 79 | { 80 | key_t key; 81 | struct stat st; 82 | 83 | if(stat(file.getPath().string().c_str(), &st) == 0) 84 | { 85 | genKey(&key, st.st_dev, st.st_ino); 86 | insert(key, file); 87 | } 88 | } 89 | 90 | void FileDepot::remove(FilePtr file) 91 | { 92 | struct stat st; 93 | key_t key; 94 | 95 | if(!file) 96 | return; 97 | fs::path p = file.getPath(); 98 | 99 | if(stat(file.getPath().string().c_str(), &st) == 0) 100 | { 101 | genKey(&key, st.st_dev, st.st_ino); 102 | files.erase(key); 103 | } 104 | } 105 | 106 | 107 | void FileDepot::genKey(key_t* key, dev_t dev, ino_t ino) 108 | { 109 | key->dev = dev; 110 | key->ino = ino; 111 | } 112 | 113 | FilePtrPrivate::FilePtrPrivate() 114 | { 115 | ino = dev = flags = 0; 116 | valid = true; 117 | } 118 | 119 | FilePtr::FilePtr(dev_t dev, ino_t ino, fs::path path, bool valid) 120 | : boost::shared_ptr(new FilePtrPrivate) 121 | { 122 | get()->path = path; 123 | get()->dev = dev; 124 | get()->ino = ino; 125 | FileDepot::instance()->insert(dev, ino, *this); 126 | 127 | if(false == valid) 128 | get()->valid = false; 129 | } 130 | 131 | FilePtr::FilePtr(fs::path path, bool valid) 132 | : boost::shared_ptr(new FilePtrPrivate) 133 | { 134 | struct stat st; 135 | if(stat(path.string().c_str(), &st) == 0) 136 | { 137 | get()->path = path; 138 | get()->dev = st.st_dev; 139 | get()->ino = st.st_ino; 140 | FileDepot::instance()->insert(st.st_dev, st.st_ino, *this); 141 | if(false == valid) 142 | get()->valid = false; 143 | } 144 | } 145 | 146 | FilePtr::FilePtr() 147 | {} 148 | 149 | FilePtr::~FilePtr() 150 | { 151 | if(this->unique()) 152 | FileDepot::instance()->remove(*this); 153 | } 154 | 155 | const fs::path& FilePtr::getPath() const 156 | { 157 | return get()->path; 158 | } 159 | 160 | void FilePtr::setInvalid() 161 | { 162 | get()->valid = false; 163 | } 164 | bool FilePtr::isValid() const 165 | { 166 | return get()->valid; 167 | } 168 | ino_t FilePtr::getInode() const 169 | { 170 | return get()->ino; 171 | } 172 | 173 | dev_t FilePtr::getDevice() const 174 | { 175 | return get()->dev; 176 | } 177 | -------------------------------------------------------------------------------- /src/e4rat-offsets.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * e4rat-offsets.cc - display physical block allocation 3 | * and their offset of a list of files 4 | * 5 | * Copyright (C) 2011 by Andreas Rid 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #include "common.hh" 22 | #include "fiemap.hh" 23 | #include "parsefilelist.hh" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | /* 37 | * FileInfo is a wrapper class of fs::path 38 | * Constructor are necessary to use common file list parser 39 | */ 40 | class FileInfo : public boost::filesystem::path 41 | { 42 | public: 43 | FileInfo(dev_t, ino_t, fs::path p) 44 | : boost::filesystem::path(p) {} 45 | FileInfo(fs::path p) 46 | : boost::filesystem::path(p) {} 47 | }; 48 | 49 | void printUsage() 50 | { 51 | std::cout << "Usage: "PROGRAM_NAME"-offsets [file(s)]\n"; 52 | } 53 | 54 | int main(int argc, char* argv[]) 55 | { 56 | struct fiemap* fmap; 57 | int fd; 58 | int prev_block = 0; 59 | int l = 13; 60 | std::vector filelist; 61 | 62 | try { 63 | FILE* file; 64 | 65 | // parse file list given as arguments 66 | for(int i=optind; i < argc; i++) 67 | { 68 | file = fopen(argv[i], "r"); 69 | if(NULL == file) { 70 | fprintf(stderr, "File %s does not exists: %s\n", argv[i], strerror(errno)); 71 | } else { 72 | printf("Parsing file %s\n", argv[i]); 73 | 74 | parseInputStream(file, filelist); 75 | 76 | fclose(file); 77 | } 78 | } 79 | 80 | // parse file list on stdin 81 | setStdIn2NonBlocking(); 82 | if(EOF != peek(stdin)) 83 | { 84 | printf("Parsing from stdin\n"); 85 | parseInputStream(stdin, filelist); 86 | } 87 | } catch(std::exception&e ) { 88 | std::cerr << e.what() << std::endl; 89 | return 1; 90 | } 91 | 92 | if(filelist.empty()) 93 | goto out; 94 | 95 | printf("%*s%*s%*s%*s%*s %s\n", 3, "ext", l, "start",l, "end",l, "length", l, "offset", "file"); 96 | 97 | BOOST_FOREACH(fs::path& file, filelist) 98 | { 99 | fd = open(file.string().c_str(), O_RDONLY | O_NOFOLLOW); 100 | if(-1 == fd) 101 | { 102 | std::cerr << "Cannot open file: " 103 | << file.string() << ": " 104 | << strerror(errno) << std::endl; 105 | 106 | continue; 107 | } 108 | 109 | fmap = ioctl_fiemap(fd); 110 | if(NULL == fmap) 111 | { 112 | std::cerr << "Cannot receive file extents: " 113 | << file.string() << ": " 114 | << strerror(errno) << std::endl; 115 | close(fd); 116 | 117 | continue; 118 | } 119 | 120 | for(__u32 i = 0; i < fmap->fm_mapped_extents; i++) 121 | { 122 | int start = fmap->fm_extents[i].fe_physical>>12; 123 | int end = start + (fmap->fm_extents[i].fe_length>>12) - 1; 124 | 125 | if(1 == fmap->fm_mapped_extents) 126 | printf("%*s", 3, " "); 127 | else 128 | printf("%*d", 3, i+1); 129 | 130 | printf("%*d", l, start); 131 | printf("%*d", l, end); 132 | printf("%*d", l, end - start + 1); 133 | printf("%*d", l, start - prev_block -1); 134 | 135 | prev_block = end; 136 | 137 | if(0 == i) 138 | printf(" %s", file.string().c_str()); 139 | 140 | printf("\n"); 141 | } 142 | free(fmap); 143 | close(fd); 144 | } 145 | 146 | exit(EXIT_SUCCESS); 147 | 148 | out: 149 | printUsage(); 150 | exit(EXIT_FAILURE); 151 | } 152 | -------------------------------------------------------------------------------- /kernel-patches/ext4_add_ext4_ioc_get_pa.patch: -------------------------------------------------------------------------------- 1 | fs/ext4/ext4.h | 11 +++++++++++ 2 | fs/ext4/ioctl.c | 33 +++++++++++++++++++++++++++++++++ 3 | fs/ext4/mballoc.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 4 | 3 files changed, 88 insertions(+) 5 | 6 | diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h 7 | index 550c61b..a7cb560 100644 8 | --- a/fs/ext4/ext4.h 9 | +++ b/fs/ext4/ext4.h 10 | @@ -147,6 +147,14 @@ struct ext4_prealloc_info { 11 | __u16 pi_flags; /* flags for the inode PA setting ioctl (in) */ 12 | }; 13 | 14 | +/* inode PA list */ 15 | +struct ext4_prealloc_list { 16 | + __u32 pl_count; /* size of pl_space array (in) */ 17 | + __u32 pl_mapped; /* number of PAs that were mapped (out) */ 18 | + __u32 pl_entries; /* number of PAs the inode has (out) */ 19 | + struct ext4_prealloc_info pl_space[0]; /* array of mapped PAs (out) */ 20 | +}; 21 | + 22 | /* 23 | * Logical to physical block mapping, used by ext4_map_blocks() 24 | * 25 | @@ -534,6 +542,7 @@ struct ext4_new_group_data { 26 | #define EXT4_IOC_ALLOC_DA_BLKS _IO('f', 12) 27 | #define EXT4_IOC_MOVE_EXT _IOWR('f', 15, struct move_extent) 28 | #define EXT4_IOC_CONTROL_PA _IOWR('f', 16, struct ext4_prealloc_info) 29 | +#define EXT4_IOC_GET_PA _IOWR('f', 17, struct ext4_prealloc_list) 30 | 31 | #if defined(__KERNEL__) && defined(CONFIG_COMPAT) 32 | /* 33 | @@ -1654,6 +1663,8 @@ extern void ext4_mb_put_buddy_cache_lock(struct super_block *, 34 | ext4_group_t, int); 35 | extern int ext4_mb_control_pa(struct inode *inode, 36 | struct ext4_prealloc_info *pi); 37 | +extern int ext4_mb_get_pa(struct inode *inode, struct ext4_prealloc_list *pl, 38 | + struct ext4_prealloc_info *dest); 39 | 40 | /* inode.c */ 41 | struct buffer_head *ext4_getblk(handle_t *, struct inode *, 42 | diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c 43 | index 631ae01..352cb35 100644 44 | --- a/fs/ext4/ioctl.c 45 | +++ b/fs/ext4/ioctl.c 46 | @@ -424,6 +424,38 @@ mext_out: 47 | return err; 48 | } 49 | 50 | + case EXT4_IOC_GET_PA: 51 | + { 52 | + struct ext4_prealloc_list pl; 53 | + struct ext4_prealloc_info *dest; 54 | + int err; 55 | + 56 | + if (!S_ISREG(inode->i_mode)) 57 | + return -EINVAL; 58 | + if (!(filp->f_mode & FMODE_READ)) 59 | + return -EBADF; 60 | + 61 | + if (copy_from_user(&pl, 62 | + (struct ext4_prealloc_list __user *)arg, sizeof(pl))) 63 | + return -EFAULT; 64 | + 65 | + dest = (struct ext4_prealloc_info *)(arg + sizeof(pl)); 66 | + if (!access_ok(VERIFY_WRITE, 67 | + (struct ext4_prealloc_info __force __user *)dest, 68 | + pl.pl_count * sizeof(struct ext4_prealloc_info))) 69 | + return -EFAULT; 70 | + 71 | + err = ext4_mb_get_pa(inode, &pl, dest); 72 | + 73 | + if (err) 74 | + return err; 75 | + 76 | + if (copy_to_user((struct ext4_prealloc_list __user *)arg, 77 | + &pl, sizeof(pl))) 78 | + return -EFAULT; 79 | + return err; 80 | + } 81 | + 82 | default: 83 | return -ENOTTY; 84 | } 85 | @@ -492,6 +524,7 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 86 | case EXT4_IOC_MOVE_EXT: 87 | case EXT4_IOC_DEBUG_DELALLOC: 88 | case EXT4_IOC_CONTROL_PA: 89 | + case EXT4_IOC_GET_PA: 90 | break; 91 | default: 92 | return -ENOIOCTLCMD; 93 | diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c 94 | index 9a33c33..a412e72 100644 95 | --- a/fs/ext4/mballoc.c 96 | +++ b/fs/ext4/mballoc.c 97 | @@ -5089,3 +5089,47 @@ out: 98 | return err1; 99 | } 100 | 101 | +/** 102 | + * ext4_mb_get_pa - Pass inode PA information to user space 103 | + * 104 | + * @inode: target inode 105 | + * @pl: information of the inode PA list (in/out) 106 | + * @dest: user space address where PAs' information is written to 107 | + * 108 | + * Return 0 if success or error code on failure. 109 | + */ 110 | +int ext4_mb_get_pa(struct inode *inode, struct ext4_prealloc_list *pl, 111 | + struct ext4_prealloc_info *dest) 112 | +{ 113 | + struct ext4_inode_info *ei = EXT4_I(inode); 114 | + struct ext4_prealloc_space *pa; 115 | + struct ext4_prealloc_info pi; 116 | + 117 | + pl->pl_entries = 0; 118 | + pl->pl_mapped = 0; 119 | + 120 | + rcu_read_lock(); 121 | + list_for_each_entry_rcu(pa, &ei->i_prealloc_list, pa_inode_list) { 122 | + pl->pl_entries++; 123 | + if (pl->pl_mapped < pl->pl_count) { 124 | + memset(&pi, 0, sizeof(pi)); 125 | + pi.pi_pstart = pa->pa_pstart; 126 | + pi.pi_lstart = pa->pa_lstart; 127 | + pi.pi_len = pa->pa_len; 128 | + pi.pi_free = pa->pa_free; 129 | + 130 | + rcu_read_unlock(); 131 | + if (copy_to_user( 132 | + (struct ext4_prealloc_info __force __user *)dest, 133 | + &pi, sizeof(pi))) { 134 | + return -EFAULT; 135 | + } 136 | + dest++; 137 | + pl->pl_mapped++; 138 | + rcu_read_lock(); 139 | + } 140 | + } 141 | + rcu_read_unlock(); 142 | + 143 | + return 0; 144 | +} 145 | -------------------------------------------------------------------------------- /src/config.c: -------------------------------------------------------------------------------- 1 | /* inih -- simple .INI file parser 2 | * Copyright (c) 2009, Brush Technology 3 | * http://code.google.com/p/inih/ 4 | */ 5 | 6 | #include "config.h" 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #define MAX_LINE 200 13 | #define MAX_SECTION 50 14 | #define MAX_NAME 50 15 | 16 | /* Strip whitespace chars off end of given string, in place. Return s. */ 17 | static char* rstrip(char* s) 18 | { 19 | char* p = s + strlen(s); 20 | while (p > s && isspace(*--p)) 21 | *p = '\0'; 22 | return s; 23 | } 24 | 25 | /* Return pointer to first non-whitespace char in given string. */ 26 | static char* lskip(const char* s) 27 | { 28 | while (*s && isspace(*s)) 29 | s++; 30 | return (char*)s; 31 | } 32 | 33 | /* Return pointer to first char c or ';' comment in given string, or pointer to 34 | null at end of string if neither found. ';' must be prefixed by a whitespace 35 | character to register as a comment. */ 36 | static char* find_char_or_comment(const char* s, char c) 37 | { 38 | int was_whitespace = 0; 39 | while (*s && *s != c && !(was_whitespace && *s == ';')) { 40 | was_whitespace = isspace(*s); 41 | s++; 42 | } 43 | return (char*)s; 44 | } 45 | 46 | /* Version of strncpy that ensures dest (size bytes) is null-terminated. */ 47 | static char* strncpy0(char* dest, const char* src, size_t size) 48 | { 49 | strncpy(dest, src, size); 50 | dest[size - 1] = '\0'; 51 | return dest; 52 | } 53 | 54 | /* See documentation in header file. */ 55 | int ini_parse_file(FILE* file, 56 | int (*handler)(void*, const char*, const char*, 57 | const char*), 58 | void* user) 59 | { 60 | /* Uses a fair bit of stack (use heap instead if you need to) */ 61 | char line[MAX_LINE]; 62 | char section[MAX_SECTION] = ""; 63 | char prev_name[MAX_NAME] = ""; 64 | 65 | char* start; 66 | char* end; 67 | char* name; 68 | char* value; 69 | int lineno = 0; 70 | int error = 0; 71 | 72 | /* Scan through file line by line */ 73 | while (fgets(line, sizeof(line), file) != NULL) { 74 | lineno++; 75 | start = lskip(rstrip(line)); 76 | 77 | if (*start == ';' || *start == '#') { 78 | /* Per Python ConfigParser, allow '#' comments at start of line */ 79 | } 80 | #if INI_ALLOW_MULTILINE 81 | else if (*prev_name && *start && start > line) { 82 | /* Non-black line with leading whitespace, treat as continuation 83 | of previous name's value (as per Python ConfigParser). */ 84 | if (!handler(user, section, prev_name, start) && !error) 85 | error = lineno; 86 | } 87 | #endif 88 | else if (*start == '[') { 89 | /* A "[section]" line */ 90 | end = find_char_or_comment(start + 1, ']'); 91 | if (*end == ']') { 92 | *end = '\0'; 93 | strncpy0(section, start + 1, sizeof(section)); 94 | *prev_name = '\0'; 95 | } 96 | else if (!error) { 97 | /* No ']' found on section line */ 98 | error = lineno; 99 | } 100 | } 101 | else if (*start && *start != ';') { 102 | /* Not a comment, must be a name[=:]value pair */ 103 | end = find_char_or_comment(start, '='); 104 | if (*end != '=') { 105 | end = find_char_or_comment(start, ':'); 106 | } 107 | if (*end == '=' || *end == ':') { 108 | *end = '\0'; 109 | name = rstrip(start); 110 | value = lskip(end + 1); 111 | end = find_char_or_comment(value, '\0'); 112 | if (*end == ';') 113 | *end = '\0'; 114 | rstrip(value); 115 | 116 | /* Valid name[=:]value pair found, call handler */ 117 | strncpy0(prev_name, name, sizeof(prev_name)); 118 | if (!handler(user, section, name, value) && !error) 119 | error = lineno; 120 | } 121 | else if (!error) { 122 | /* No '=' or ':' found on name[=:]value line */ 123 | error = lineno; 124 | } 125 | } 126 | } 127 | 128 | return error; 129 | } 130 | 131 | /* See documentation in header file. */ 132 | int ini_parse(const char* filename, 133 | int (*handler)(void*, const char*, const char*, const char*), 134 | void* user) 135 | { 136 | FILE* file; 137 | int error; 138 | 139 | file = fopen(filename, "r"); 140 | if (!file) 141 | return -1; 142 | error = ini_parse_file(file, handler, user); 143 | fclose(file); 144 | return error; 145 | } 146 | -------------------------------------------------------------------------------- /src/eventcatcher.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * eventcatcher.cc - Handle audit event from Listener 3 | * 4 | * Copyright (C) 2011 by Andreas Rid 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include "eventcatcher.hh" 21 | #include "listener.hh" 22 | #include "logging.hh" 23 | 24 | #include 25 | 26 | void ScanFsAccess::insert(FilePtr& f) 27 | { 28 | list.push_back(f); 29 | } 30 | 31 | std::deque ScanFsAccess::getFileList() 32 | { 33 | std::deque ret; 34 | BOOST_FOREACH(FilePtr f, list) 35 | if(f.isValid()) 36 | ret.push_back(f); 37 | 38 | return ret; 39 | } 40 | 41 | void ScanFsAccess::observeApp(std::string comm) 42 | { 43 | // name of process is limited to 16 characters 44 | if(comm.size() > 15) 45 | comm.resize(15); 46 | observe_apps.insert(comm); 47 | } 48 | 49 | fs::path ScanFsAccess::readLink(fs::path& path) 50 | { 51 | char lnk_buf[PATH_MAX]; 52 | memset(&lnk_buf, '\0', PATH_MAX); 53 | if(0 > readlink(path.string().c_str(), (char*)&lnk_buf, PATH_MAX)) 54 | throw ""; 55 | return realpath(lnk_buf, path.parent_path()); 56 | } 57 | 58 | fs::path ScanFsAccess::getPath2RegularFile(fs::path& path) 59 | { 60 | fs::path linkTo = path; 61 | 62 | try { 63 | while(1) 64 | linkTo = readLink(linkTo); 65 | } 66 | catch(...) 67 | {} 68 | 69 | return linkTo; 70 | } 71 | 72 | void ScanFsAccess::handleAuditEvent(boost::shared_ptr event) 73 | { 74 | // Since Linux set audit filter AUDT_FILTER_ENTRY as deprecated, there is no way 75 | // to monitor exit() syscall events. 76 | // Cause we identify processes by its process id, we have to check whether 77 | // a process exists and another process got the same process id like the one 78 | // watched before. 79 | if(event->type == Fork) 80 | { 81 | std::set::iterator it; 82 | it = observe_pids.find(event->exit); 83 | if(it != observe_pids.end()) 84 | observe_pids.erase(it); 85 | } 86 | 87 | // does this event apply to a process name we are watching? 88 | if(!observe_apps.empty()) 89 | { 90 | if(observe_pids.find(event->pid) == observe_pids.end()) 91 | { 92 | if(observe_apps.find(event->comm) == observe_apps.end()) 93 | return; 94 | 95 | debug(_("Valid process name %. insert pid %d"), event->comm.c_str(), event->pid); 96 | observe_pids.insert(event->pid); 97 | } 98 | } 99 | debug(_("syscall: %d RO: %d"), event->type, event->readOnly); 100 | 101 | switch(event->type) 102 | { 103 | case Fork: 104 | observe_pids.insert(event->exit); 105 | break; 106 | case Creat: 107 | case Truncate: 108 | { 109 | FilePtr file = FilePtr(event->dev, event->ino, event->path); 110 | if(file.isValid()) 111 | { 112 | info(_("File was modified: \t%s"), event->path.string().c_str()); 113 | file.setInvalid(); 114 | } 115 | if(file.unique()) 116 | insert(file); 117 | } 118 | break; 119 | 120 | case Open: 121 | case OpenAt: 122 | if(!event->readOnly) 123 | { 124 | FilePtr file = FilePtr(event->dev, event->ino, event->path); 125 | if(file.isValid()) 126 | { 127 | info(_("Opened writable: \t%s"), 128 | event->path.string().c_str()); 129 | file.setInvalid(); 130 | } 131 | if(file.unique()) 132 | insert(file); 133 | 134 | break; 135 | } 136 | 137 | case Execve: 138 | if(event->type == Execve) 139 | { 140 | FilePtr file = FilePtr(event->exe); 141 | if(file.unique()) 142 | { 143 | info(_("Insert executable: \t%s"), event->exe.string().c_str()); 144 | insert(file); 145 | } 146 | } 147 | 148 | default: 149 | { 150 | FilePtr file; 151 | file = FilePtr(event->dev, event->ino, getPath2RegularFile(event->path)); 152 | if(file.unique()) 153 | { 154 | info(_("Insert regular file: \t%s"), file.getPath().string().c_str()); 155 | insert(file); 156 | } 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/fiemap.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * fiemap.cc - get physical block mapping 3 | * 4 | * Copyright (C) 2011 by Andreas Rid 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include "fiemap.hh" 21 | #include "logging.hh" 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | /* 33 | * Call fiemap ioctl on file descriptor fd. 34 | * Determine the size of struct fiemap by extent_count. 35 | * If extent_count is set to 0, it chooses the size by itself to receive all extents. 36 | * 37 | * Returns NULL on error 38 | */ 39 | struct fiemap* ioctl_fiemap(int fd, unsigned int extent_count) 40 | { 41 | if(!extent_count) 42 | extent_count = 10; 43 | 44 | struct fiemap* fmap = (struct fiemap*)calloc(1, 45 | sizeof(struct fiemap) + extent_count * sizeof(struct fiemap_extent)); 46 | 47 | fmap->fm_length = FIEMAP_MAX_OFFSET; 48 | fmap->fm_flags |= FIEMAP_FLAG_SYNC; 49 | fmap->fm_extent_count = extent_count; 50 | 51 | if(ioctl(fd, FS_IOC_FIEMAP, fmap) < 0) 52 | { 53 | char __filename[PATH_MAX]; 54 | char __path2fd[1024]; 55 | 56 | sprintf(__path2fd, "/proc/self/fd/%d", fd); 57 | int len; 58 | if((len = readlink(__path2fd, __filename, PATH_MAX)) != -1) 59 | { 60 | __filename[len] = '\0'; 61 | error(_("ioctl_fiemap: %s: %s"), __filename, strerror(errno)); 62 | } 63 | else 64 | error(_("ioctl_fiemap and readlink failed: %s"), strerror(errno)); 65 | 66 | free(fmap); 67 | return NULL; 68 | } 69 | 70 | if(fmap->fm_mapped_extents == fmap->fm_extent_count) 71 | return ioctl_fiemap(fd, extent_count<<1); 72 | 73 | if(fmap->fm_mapped_extents < fmap->fm_extent_count) 74 | { 75 | fmap = (struct fiemap*) realloc(fmap, sizeof(struct fiemap) 76 | + fmap->fm_mapped_extents * sizeof(struct fiemap_extent)); 77 | fmap->fm_extent_count = fmap->fm_mapped_extents; 78 | } 79 | 80 | return fmap; 81 | } 82 | 83 | /* 84 | * Return struct fiemap by calling ioctl_fiemap 85 | */ 86 | struct fiemap* get_fiemap(const char* file) 87 | { 88 | int fd; 89 | fd = open64(file, O_RDONLY); 90 | if (fd < 0) 91 | { 92 | error(_("open: %s: %s"), file, strerror(errno)); 93 | return NULL; 94 | } 95 | struct fiemap* fmap = ioctl_fiemap(fd, 0); 96 | close(fd); 97 | return fmap; 98 | } 99 | 100 | /* 101 | * Test whether file is a sparse file 102 | */ 103 | bool is_sparse_file(struct fiemap* fmap) 104 | { 105 | __u64 estimated = 0; 106 | for(unsigned int j=0; j < fmap->fm_mapped_extents; j++) 107 | { 108 | if(fmap->fm_extents[j].fe_logical != estimated) 109 | return true; 110 | estimated += fmap->fm_extents[j].fe_length; 111 | } 112 | return false; 113 | } 114 | 115 | /* 116 | * return size in bytes 117 | */ 118 | 119 | __u64 get_allocated_file_size(const char* file) 120 | { 121 | return get_allocated_file_size(get_fiemap(file)); 122 | 123 | } 124 | __u64 get_allocated_file_size(struct fiemap* fmap) 125 | { 126 | __u64 result = 0; 127 | 128 | if(NULL == fmap) 129 | return 0; 130 | 131 | for(unsigned int j=0; j < fmap->fm_mapped_extents; j++) 132 | result += fmap->fm_extents[j].fe_length; 133 | 134 | return result; 135 | } 136 | /* 137 | * Calculate the inode's logical size in Bytes. 138 | * Sparse files may have holes of unallocated blocks between its extents. 139 | * 140 | * The return value will inherit those blocks too. 141 | * So the returned number in Bytes can be used to create an appropriate donor file. 142 | */ 143 | __u64 get_file_size(int fd) 144 | { 145 | return get_file_size(ioctl_fiemap(fd)); 146 | } 147 | 148 | __u64 get_file_size(struct fiemap* fmap) 149 | { 150 | if(NULL == fmap) 151 | return 0; 152 | 153 | for(unsigned int j=0; j < fmap->fm_mapped_extents; j++) 154 | if(fmap->fm_extents[j].fe_flags & FIEMAP_EXTENT_LAST) 155 | return (fmap->fm_extents[j].fe_logical + fmap->fm_extents[j].fe_length); 156 | return 0; 157 | } 158 | 159 | /* 160 | * In case of sparse files ignore unallocated holes. 161 | */ 162 | __u32 get_frag_count(int fd) 163 | { 164 | __u32 result = 1; 165 | struct fiemap* fmap; 166 | 167 | fmap = ioctl_fiemap(fd, 0); 168 | if(NULL == fmap) 169 | return 0; 170 | 171 | for(unsigned int j=1; j < fmap->fm_mapped_extents; j++) 172 | if(fmap->fm_extents[j].fe_physical != fmap->fm_extents[j-1].fe_physical + fmap->fm_extents[j].fe_logical - fmap->fm_extents[j-1].fe_logical) 173 | result++; 174 | 175 | return result; 176 | } 177 | 178 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | 3 | # test weather variables from top level directories are missing 4 | IF( NOT CMAKE_BUILD_TYPE ) 5 | MESSAGE(FATAL_ERROR "Do not run cmake on subdirectories") 6 | ENDIF() 7 | 8 | if(NOT BUILD_CORE_LIBRARY_STATIC) 9 | if(CMAKE_SIZEOF_VOID_P MATCHES 8) 10 | SET(BUILD_CORE_LIBRARY_STATIC 1) 11 | endif() 12 | endif() 13 | 14 | #TODO permit link dependencies not static 15 | #set(LINK_DEPENDENIES "static") 16 | 17 | if(CMAKE_COMPILER_IS_GNUCXX) 18 | execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion 19 | OUTPUT_VARIABLE GCC_VERSION) 20 | 21 | if(GCC_VERSION VERSION_LESS 4.5) 22 | message(WARNING "GCC <4.5 does not support -static-libstdc++") 23 | execute_process(COMMAND stat -c %d / 24 | OUTPUT_VARIABLE DEV_ROOT_DIR) 25 | execute_process(COMMAND stat -c %d /usr 26 | OUTPUT_VARIABLE DEV_USR_DIR) 27 | if(NOT ${DEV_ROOT_DIR} STREQUAL ${DEV_USR_DIR}) 28 | message(FATAL_ERROR "Your /usr directory is not part of the root device. Upgrade to gcc >=4.5.") 29 | endif(NOT ${DEV_ROOT_DIR} STREQUAL ${DEV_USR_DIR}) 30 | endif(GCC_VERSION VERSION_LESS 4.5) 31 | endif(CMAKE_COMPILER_IS_GNUCXX) 32 | 33 | add_definitions(-Wall) 34 | add_definitions(-DPROGRAM_NAME=\"${PROJECT_NAME}\") 35 | add_definitions(-DVERSION=\"${E4RAT-LITE_VERSION}\") 36 | IF(CMAKE_BUILD_TYPE STREQUAL "release") 37 | add_definitions(-O2) 38 | add_definitions(-D_FORTIFY_SOURCE=2) 39 | add_definitions(-fstack-protector-all) 40 | else(CMAKE_BUILD_TYPE STREQUAL "release") 41 | add_definitions(-ggdb -DDEBUG_ENABLED) 42 | endif(CMAKE_BUILD_TYPE STREQUAL "release") 43 | if(MOVE_EXT_RDONLY_FLAG) 44 | add_definitions(-DMOVE_EXT_RDONLY_FLAG) 45 | endif(MOVE_EXT_RDONLY_FLAG) 46 | 47 | SET(CMAKE_C_FLAGS_RELEASE "-std=c99" ) 48 | SET(CMAKE_C_FLAGS_DEBUG ${CMAKE_C_FLAGS_RELEASE}) 49 | SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) 50 | SET(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/..) 51 | SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/..) 52 | 53 | ## 54 | # Set RPATH to find shared library 55 | ## 56 | # use, i.e. don't skip the full RPATH for the build tree 57 | SET(CMAKE_SKIP_BUILD_RPATH FALSE) 58 | 59 | # when building, don't use the install RPATH already 60 | # (but later on when installing) 61 | SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) 62 | 63 | # add the automatically determined parts of the RPATH 64 | # which point to directories outside the build tree to the install RPATH 65 | SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) 66 | 67 | # the RPATH to be used when installing, but only if it's not a system directory 68 | LIST(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/lib" isSystemDir) 69 | IF("${isSystemDir}" STREQUAL "-1") 70 | if(${CMAKE_INSTALL_PREFIX} STREQUAL "/") 71 | SET(RPATH "/lib") 72 | else(${CMAKE_INSTALL_PREFIX} STREQUAL "/") 73 | SET(RPATH "${CMAKE_INSTALL_PREFIX}/lib") 74 | endif(${CMAKE_INSTALL_PREFIX} STREQUAL "/") 75 | SET(CMAKE_INSTALL_RPATH "${RPATH}") 76 | ENDIF("${isSystemDir}" STREQUAL "-1") 77 | 78 | ## 79 | # dependencies 80 | ## 81 | FIND_PACKAGE(ext2fs REQUIRED) 82 | set(${PROJECT_NAME}_LIBRARIES ${${PROJECT_NAME}_LIBRARIES} 83 | ${EXT2FS_LIBRARY}) 84 | 85 | find_package(audit REQUIRED) 86 | set(${PROJECT_NAME}_LIBRARIES ${${PROJECT_NAME}_LIBRARIES} 87 | ${AUDIT_LIBRARY}) 88 | 89 | find_package(auparse REQUIRED) 90 | set(${PROJECT_NAME}_LIBRARIES ${${PROJECT_NAME}_LIBRARIES} 91 | ${AUPARSE_LIBRARY}) 92 | 93 | find_package(Threads REQUIRED) 94 | set(${PROJECT_NAME}_LIBRARIES ${${PROJECT_NAME}_LIBRARIES} 95 | ${CMAKE_THREAD_LIBS_INIT}) 96 | 97 | 98 | ### 99 | # Building source code 100 | ### 101 | set(${PROJECT_NAME}-core_SRC 102 | config.c 103 | logging.cc 104 | common.cc 105 | fiemap.cc 106 | device.cc 107 | ) 108 | 109 | ADD_EXECUTABLE(${PROJECT_NAME}-collect 110 | e4rat-collect.cc 111 | fileptr.cc 112 | listener.cc 113 | eventcatcher.cc 114 | ) 115 | 116 | ADD_EXECUTABLE(${PROJECT_NAME}-preload 117 | e4rat-preload.c 118 | ) 119 | 120 | ADD_EXECUTABLE(${PROJECT_NAME}-realloc 121 | e4rat-realloc.cc 122 | defrag.cc 123 | buddycache.cc 124 | ) 125 | 126 | 127 | IF(CMAKE_BUILD_TYPE STREQUAL "debug") 128 | ADD_EXECUTABLE(${PROJECT_NAME}-offsets 129 | e4rat-offsets.cc 130 | ) 131 | TARGET_LINK_LIBRARIES(${PROJECT_NAME}-offsets 132 | ${PROJECT_NAME}-core 133 | ) 134 | ENDIF(CMAKE_BUILD_TYPE STREQUAL "debug") 135 | 136 | add_library(${PROJECT_NAME}-core SHARED 137 | ${${PROJECT_NAME}-core_SRC} 138 | ) 139 | set_target_properties(${PROJECT_NAME}-core PROPERTIES 140 | SOVERSION 0 141 | ) 142 | 143 | target_link_libraries(${PROJECT_NAME}-core 144 | ${${PROJECT_NAME}_LIBRARIES} 145 | ${${PROJECT_NAME}_STATIC_LIBRARIES} 146 | ) 147 | 148 | foreach( EXE ${PROJECT_NAME}-collect 149 | ${PROJECT_NAME}-realloc 150 | ${PROJECT_NAME}-preload) 151 | TARGET_LINK_LIBRARIES(${EXE} 152 | ${PROJECT_NAME}-core 153 | ) 154 | endforeach( EXE ) 155 | 156 | ### 157 | # install project targets 158 | ### 159 | set(${PROJECT_NAME}_TARGETS 160 | ${PROJECT_NAME}-core 161 | ${PROJECT_NAME}-collect 162 | ${PROJECT_NAME}-preload 163 | ${PROJECT_NAME}-realloc 164 | ) 165 | 166 | if(NOT BUILD_CORE_LIBRARY_STATIC) 167 | set(${PROJECT_NAME}_TARGETS 168 | ${${PROJECT_NAME}_TARGETS} 169 | ${PROJECT_NAME}-core 170 | ) 171 | endif(NOT BUILD_CORE_LIBRARY_STATIC) 172 | 173 | INSTALL(TARGETS ${${PROJECT_NAME}_TARGETS} 174 | RUNTIME DESTINATION "bin" 175 | LIBRARY DESTINATION "${CMAKE_INSTALL_PREFIX}/lib" 176 | ) 177 | 178 | INSTALL(DIRECTORY DESTINATION "/var/lib/${PROJECT_NAME}") 179 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Informations 2 | ------------ 3 | 4 | The e4rat-lite reduces disk access times through physical file reallocation. 5 | It is based on the online defragmentation ioctl EXT4_IOC_MOVE_EXT from the ext4 6 | filesystem, which was introduced in Linux Kernel 2.6.31. Therefore, other 7 | filesystem types or earlier versions of extended filesystems are not supported. 8 | 9 | The e4rat-lite is created from a simply idea of implementating the e4rat-preload-lite 10 | on the main e4rat tree, but it ended up showing a number of features that could be 11 | improved, making e4rat-lite a more independent project, with several optimizations 12 | that causes your system to start even faster than standard e4rat. 13 | 14 | e4rat-lite binaries 15 | =================== 16 | 17 | The e4rat-lite consists of three binaries: 18 | 19 | **e4rat-lite-collect:** Gather relevant files by monitoring file accesses during an 20 | application startup. The generated file list is the fundament of the second step. 21 | 22 | **e4rat-lite-realloc:** Files are placed physically in a row on disk. The reallocation 23 | of the files' content yields a higher disk transfer rate which accelerates program 24 | start processes. 25 | 26 | **e4rat-lite-preload:** Transfers files into memory in parallel to program startup. 27 | Because a file consists of file content and its I-Node information the preloading 28 | process is divided into two steps. First, it reads the I-Nodes' information which 29 | are still spread over the entire filesystem. In the second step, the files' content 30 | is read without causing any disk seeks. 31 | 32 | For more information see: e4rat-lite-collect(8), e4rat-lite-realloc(8) 33 | e4rat-lite-preload and e4rat-lite.conf(5). 34 | 35 | 36 | How to use 37 | ---------- 38 | 39 | **1)** Run e4rat-lite-collect as init process through adding following line to Kernel 40 | parameters: 41 | 42 | init=/usr/bin/e4rat-lite-collect 43 | 44 | This can also be done by changing the configuration files of your boot 45 | manager (grub, lilo, syslinux, etc). 46 | 47 | **2)** Check the configuration file (/etc/e4rat-lite.conf) and change the variable 48 | init_file to the process used in your system. E.g.: 49 | 50 | init_file=/usr/lib/systemd/systemd 51 | 52 | or 53 | 54 | init_file=/sbin/init 55 | 56 | **3)** Restart your computer to complete the first data collection. 57 | 58 | **4)** After the e4rat-lite-collect end it will generate a list of files, which is 59 | written in /var/lib/e4rat-lite/startup.log (You can change the destination path on 60 | configuration file, in /etc/e4rat-lite.conf). 61 | 62 | After complete boot, you can end the e4rat-lite-collect running: 63 | 64 | # e4rat-lite-collect -k 65 | 66 | **5)** Now you must run the e4rat-lite-realloc to start the relocation process. Is 67 | recommended that you switch to runlevel 1, so you ensure write access to all binary 68 | processes (See e4rat-lite-realloc(8) for more information). 69 | 70 | If you are using a boot system like System V, you can use the following command to 71 | enter runlevel 1: 72 | 73 | # telinit 1 74 | 75 | If you are using a boot system like systemd, you can switch to rescue mode: 76 | 77 | # systemctl isolate rescue.target 78 | 79 | So do the relocation using the command: 80 | 81 | # e4rat-lite-realloc 82 | 83 | By default the e4rat-lite-realloc search for the boot log file in 84 | /var/lib/e4rat-lite/startup.log. If you use another location, you can 85 | indicate the path as a parameter. 86 | 87 | **6)** At end of this process, the kernel parameter should be changed again to load the 88 | e4rat-lite-preload, which will in fact speed up the boot of your system: 89 | 90 | init=/usr/bin/e4rat-lite-preload 91 | 92 | That's all 93 | 94 | You must make a new collection of files after much modification in programs installed, such 95 | as updates and/or boot related files such as libraries and like. The time for this varies 96 | depending on the frequency of changes that you performs on your disk. Keep this in mind. 97 | 98 | DEPENDENCIES 99 | ----------- 100 | 101 | The e4rat-lite toolset has the following external dependencies: 102 | 103 | - Linux Kernel (>= 2.6.31) 104 | - CMake (>= 2.6) 105 | - Boost Library (>=1.41) 106 | [You need the following components: system, filesytem, regex, signals2] 107 | - Linux Audit Library (libaudit >=0.1.7) 108 | - Ext2 File System Utilities (e2fsprogs) 109 | - Gettext (>=0.18) 110 | 111 | BUILDING 112 | -------- 113 | 114 | The build system is based on CMake, which will generate a Makefile. 115 | 116 | To build the release version of e4rat-lite run the following commands: 117 | 118 | $ mkdir build 119 | $ cd build 120 | $ cmake .. -DCMAKE_BUILD_TYPE=release 121 | $ make 122 | 123 | For install: 124 | 125 | # make install 126 | 127 | AUTHORS 128 | ------- 129 | 130 | e4rat has been developed by Andreas Rid under the guidance 131 | of Gundolf Kiefer at the University of Applied Sciences, Augsburg. 132 | 133 | e4rat-lite has been developed by Lara Maia . 134 | 135 | There can be external authors for some files. In this situation it will be indicated in the file head. 136 | 137 | e4rat-lite is free software: you can redistribute it and/or modify 138 | it under the terms of the GNU General Public License as published by 139 | the Free Software Foundation, either version 3 of the License, or 140 | (at your option) any later version. 141 | 142 | e4rat-lite is distributed in the hope that it will be useful, 143 | but WITHOUT ANY WARRANTY; without even the implied warranty of 144 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 145 | GNU General Public License for more details. 146 | 147 | You should have received a copy of the GNU General Public License 148 | along with this program. If not, see . 149 | 150 | Lara Maia 151 | -------------------------------------------------------------------------------- /src/e4rat-realloc.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * e4rat-realloc.cc - Relevant file defragmentation tool 3 | * 4 | * Copyright: 2011 Andreas Rid. 5 | * 2012 ~ 2015 Lara Maia 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #include "defrag.hh" 22 | #include "logging.hh" 23 | #include "common.hh" 24 | #include "parsefilelist.hh" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include 36 | 37 | #define PID_FILE "/var/run/e4rat-lite-realloc.pid" 38 | #define LOG_FILE "/var/lib/e4rat-lite/startup.log" 39 | 40 | /* 41 | * FileInfo is a wrapper class of fs::path 42 | * Constructor are necessary to use common file list parser 43 | */ 44 | class FileInfo : public boost::filesystem::path 45 | { 46 | public: 47 | FileInfo(dev_t, ino_t, fs::path p) 48 | : boost::filesystem::path(p) {} 49 | FileInfo(fs::path p) 50 | : boost::filesystem::path(p) {} 51 | }; 52 | 53 | void printUsage() 54 | { 55 | std::cout << 56 | _("Usage: e4rat-lite-realloc [ option(s) ] [ mode ] files(s)\n" 57 | "\n" 58 | " OPTIONS:\n" 59 | " -V --version print version and exit\n" 60 | " -h --help print help and exit\n" 61 | " -v --verbose increment verbosity level\n" 62 | " -q --quiet set verbose level to 0\n" 63 | " -l --loglevel set log level\n\n") 64 | ; 65 | } 66 | 67 | int main(int argc, char *argv[]) 68 | { 69 | Optimizer optimizer; 70 | std::vector filelist; 71 | 72 | setlocale(LC_ALL, ""); 73 | bindtextdomain("e4rat-lite", "/usr/share/locale"); 74 | textdomain("e4rat-lite"); 75 | 76 | int loglevel = 3; //FIXME 77 | int verbose = 7; //FIXME 78 | 79 | static struct option long_options[] = 80 | { 81 | {"verbose", no_argument, 0, 'v'}, 82 | {"version", no_argument, 0, 'V'}, 83 | {"quiet", no_argument, 0, 'q'}, 84 | {"help", no_argument, 0, 'h'}, 85 | {"loglevel", required_argument, 0, 'l'}, 86 | {0, 0, 0, 0} 87 | }; 88 | 89 | char c; 90 | int option_index = 0; 91 | while((c = getopt_long(argc, argv, "Vvhql", long_options, &option_index)) != EOF) 92 | { 93 | switch(c) 94 | { 95 | case 'h': 96 | goto out; 97 | case 'V': 98 | std::cout << PROGRAM_NAME << " " << VERSION << std::endl; 99 | return 0; 100 | case 'v': 101 | verbose <<= 1; 102 | verbose |= 1; 103 | break; 104 | case 'q': 105 | verbose = 0; 106 | break; 107 | case 'l': 108 | loglevel = atoi(optarg); 109 | break; 110 | default: 111 | std::cerr << _("Unrecognised option: ") << optopt << std::endl; 112 | goto out; 113 | } 114 | } 115 | 116 | logger.setVerboseLevel(verbose); 117 | logger.setLogLevel(loglevel); 118 | 119 | if(getuid() != 0) 120 | { 121 | std::cerr << _("You need root privileges to run this program.\n"); 122 | exit(1); 123 | } 124 | 125 | if(!createPidFile(PID_FILE)) 126 | { 127 | std::cerr << _("It seems that e4rat-lite-realloc is already running.\n"); 128 | std::cerr << _("Remove pid file ") << PID_FILE << _(" to unlock.\n"); 129 | exit(1); 130 | } 131 | 132 | // Register signal handler 133 | struct sigaction sa; 134 | memset(&sa, '\0', sizeof(struct sigaction)); 135 | sa.sa_handler = signalHandler; 136 | sigaction(SIGINT, &sa, NULL); 137 | sigaction(SIGTERM, &sa, NULL); 138 | 139 | try { 140 | FILE *file; 141 | 142 | // parse file list given as arguments 143 | for(int i=optind; i < argc; i++) 144 | { 145 | file = fopen(argv[i], "r"); 146 | if(NULL == file) 147 | warn(_("File %s does not exist."), argv[i]); 148 | else 149 | { 150 | notice(_("Parsing file %s"), argv[i]); 151 | parseInputStream(file, filelist); 152 | fclose(file); 153 | } 154 | } 155 | 156 | // parse file list on stdin 157 | setStdIn2NonBlocking(); 158 | if(EOF != peek(stdin)) 159 | { 160 | notice(_("Parsing from stdin")); 161 | parseInputStream(stdin, filelist); 162 | } 163 | 164 | if(filelist.empty() && optind == argc) 165 | { 166 | file = fopen(LOG_FILE, "r"); 167 | if(NULL != file) 168 | { 169 | notice(_("Parsing file %s"), LOG_FILE); 170 | parseInputStream(file, filelist); 171 | fclose(file); 172 | } 173 | else 174 | goto out; 175 | } 176 | // type casting necessary not to break strict-aliasing rules 177 | std::vector *fp = (std::vector*)&filelist; 178 | optimizer.relatedFiles(*fp); 179 | } catch(std::exception& e) { 180 | std::cerr << e.what() << std::endl; 181 | } 182 | 183 | unlink(PID_FILE); 184 | 185 | return 0; 186 | 187 | out: 188 | unlink(PID_FILE); 189 | printUsage(); 190 | 191 | exit(EXIT_FAILURE); 192 | } 193 | -------------------------------------------------------------------------------- /src/e4rat-preload.c: -------------------------------------------------------------------------------- 1 | /* 2 | * e4rat-preload.c - Preload files into page cache 3 | * 4 | * e4rat-preload.cc: 2011 Andreas Rid. 5 | * e4rat-preload-lite.c: 2011 John Lindgren 6 | * 7 | * e4rat-preload.c (e4rat-lite): 8 | * 2012 ~ 2015 Lara Maia 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program. If not, see . 22 | */ 23 | 24 | #include "config.h" 25 | #include "intl.hh" 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #define EARLY 200 38 | #define BLOCK 300 39 | #define BUF (1024*1024) 40 | #define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0 41 | 42 | #ifdef __STRICT_ANSI__ 43 | char *strdup(const char *str) { 44 | unsigned int n = strlen(str) + 1; 45 | char *dup = malloc(n); 46 | 47 | if(dup) strcpy(dup, str); 48 | return dup; 49 | } 50 | #endif 51 | 52 | typedef struct { 53 | int n, dev; 54 | uint64_t inode; 55 | char *path; 56 | } FileDesc; 57 | 58 | static FileDesc **list = 0; 59 | static FileDesc **sorted = 0; 60 | static int listlen = 0; 61 | 62 | static int sort_cb(const void *_a, const void *_b) { 63 | FileDesc *a = *(FileDesc**) _a; 64 | FileDesc *b = *(FileDesc**) _b; 65 | 66 | if(a->dev < b->dev) 67 | return -1; 68 | if(a->dev > b->dev) 69 | return 1; 70 | if(a->inode < b->inode) 71 | return -1; 72 | if(a->inode > b->inode) 73 | return 1; 74 | 75 | return 0; 76 | } 77 | 78 | static FileDesc *parse_line(int n, const char *line) { 79 | int dev = 0; 80 | uint64_t inode = 0; 81 | 82 | while(*line >= '0' && *line <= '9') 83 | dev = dev *10 +((*line ++) - '0'); 84 | 85 | if((*line ++) != ' ') 86 | return 0; 87 | 88 | while(*line >= '0' && *line <= '9') 89 | inode = inode *10 +((*line ++) - '0'); 90 | 91 | if((* line ++) != ' ') 92 | return 0; 93 | 94 | FileDesc *f = malloc(sizeof(FileDesc)); 95 | 96 | f->n = n; 97 | f->dev = dev; 98 | f->inode = inode; 99 | f->path = strdup(line); 100 | 101 | return f; 102 | } 103 | 104 | static void printUsage () { 105 | printf(_("Usage: e4rat-lite-preload [ option(s) ]\n" 106 | "\n" 107 | "-V --version print version and exit\n" 108 | "-h --help print help and exit\n" 109 | "\n" 110 | "-i --initfile alternate init file\n" 111 | "-s --startuplog alternate startup log file" 112 | "\n")); 113 | } 114 | 115 | static void load_list(const char *LIST) { 116 | int listsize = 0; 117 | 118 | printf(_("Loading %s.\n"), LIST); 119 | 120 | FILE *stream = fopen(LIST, "r"); 121 | if(!stream) { 122 | printf(_("Error: %s.\n"), strerror(errno)); 123 | exit(EXIT_FAILURE); 124 | } 125 | 126 | while(1) { 127 | char buf[512]; 128 | if(! fgets(buf, sizeof buf, stream)) 129 | break; 130 | if(buf[0] && buf[strlen(buf) - 1] == '\n') 131 | buf[strlen(buf) - 1] = 0; 132 | FileDesc *f = parse_line(listlen, buf); 133 | if(! f) 134 | continue; 135 | if(listlen >= listsize) { 136 | listsize = listsize ? listsize *2 : 256; 137 | list = realloc(list, sizeof(FileDesc*) *listsize); 138 | } 139 | list[listlen ++] = f; 140 | } 141 | fclose(stream); 142 | 143 | list = realloc(list, sizeof(FileDesc *) *listlen); 144 | sorted = malloc(sizeof(FileDesc *) *listlen); 145 | memcpy(sorted, list, sizeof(FileDesc *) *listlen); 146 | qsort(sorted, listlen, sizeof(FileDesc *), sort_cb); 147 | } 148 | 149 | static void load_inodes(int a, int b) { 150 | struct stat s; 151 | 152 | for(int i = 0; i < listlen; i ++) { 153 | if(sorted[i]->n >= a && sorted[i]->n < b) 154 | stat(sorted[i]->path, & s); 155 | } 156 | } 157 | 158 | static void exec_init(char **argv, const char *INIT) { 159 | printf(_("Running %s.\n"), INIT); 160 | 161 | switch(fork()) { 162 | case -1: 163 | printf(_("Error: %s.\n"), strerror(errno)); 164 | exit(EXIT_FAILURE); 165 | case 0: 166 | return; 167 | default: 168 | execv(INIT, argv); 169 | printf(_("Error: %s.\n"), strerror(errno)); 170 | exit(EXIT_FAILURE); 171 | } 172 | } 173 | 174 | static void load_files(int a, int b) { 175 | void *buf = malloc(BUF); 176 | 177 | for(int i = a; i < b && i < listlen; i ++) { 178 | int handle = open(list[i]->path, O_RDONLY); 179 | 180 | if(handle < 0) 181 | continue; 182 | 183 | while(read(handle, buf, BUF) > 0){} 184 | 185 | close(handle); 186 | } 187 | 188 | free(buf); 189 | } 190 | 191 | typedef struct 192 | { 193 | const char *init_file; 194 | const char *startup_log_file; 195 | } configuration; 196 | 197 | static int config_handler(void *user, const char *section, const char *name, 198 | const char *value) 199 | { 200 | configuration *pconfig = (configuration*)user; 201 | 202 | if(MATCH("Global", "startup_log_file")) { 203 | pconfig->startup_log_file = strdup(value); 204 | } else if(MATCH("Global", "init_file")) { 205 | pconfig->init_file = strdup(value); 206 | } else { 207 | return 0; // unknown section/name, error 208 | } 209 | 210 | return 1; 211 | } 212 | 213 | int main(int argc, char **argv) { 214 | configuration config; 215 | 216 | setlocale(LC_ALL, ""); 217 | bindtextdomain("e4rat-lite", "/usr/share/locale"); 218 | textdomain("e4rat-lite"); 219 | 220 | if(ini_parse("/etc/e4rat-lite.conf", config_handler, &config) < 0) { 221 | printf(_("Unable to load the configuration file: %s\n"), strerror(errno)); 222 | exit(EXIT_FAILURE); 223 | } 224 | 225 | static struct option long_options[] = 226 | { 227 | {"help", no_argument, 0, 'h'}, 228 | {"version", no_argument, 0, 'V'}, 229 | {"initfile", required_argument, 0, 'i'}, 230 | {"startuplog", required_argument, 0, 's'}, 231 | {0, 0, 0, 0} 232 | }; 233 | 234 | int c; 235 | int option_index = 0; 236 | const char *opt_init_file = 0; 237 | const char *opt_startup_log_file = 0; 238 | 239 | while ((c = getopt_long(argc, argv, "i:s:hV", long_options, &option_index)) != EOF) 240 | { 241 | switch(c) 242 | { 243 | case 'h': 244 | goto err1; 245 | case 'V': 246 | goto err2; 247 | case 'i': 248 | opt_init_file = optarg; 249 | break; 250 | case 's': 251 | opt_startup_log_file = optarg; 252 | break; 253 | } 254 | } 255 | 256 | if(opt_startup_log_file != 0) 257 | load_list(opt_startup_log_file); 258 | else 259 | load_list(config.startup_log_file); 260 | 261 | printf (_("Preloading %d files...\n"), listlen); 262 | 263 | load_inodes (0, EARLY); 264 | load_files (0, EARLY); 265 | 266 | if(opt_init_file != 0) 267 | exec_init(argv, opt_init_file); 268 | else 269 | exec_init(argv, config.init_file); 270 | 271 | for(int i = EARLY; i < listlen; i += BLOCK) { 272 | load_inodes(i, i + BLOCK); 273 | load_files(i, i + BLOCK); 274 | } 275 | 276 | exit(EXIT_SUCCESS); 277 | 278 | err1: 279 | printUsage(); 280 | exit(1); 281 | err2: 282 | printf("%s %s\n", PROGRAM_NAME, VERSION); 283 | exit(1); 284 | } 285 | -------------------------------------------------------------------------------- /src/common.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * common.hh - Collection of common functions and classes 3 | * 4 | * Copyright (C) 2011 by Andreas Rid 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include "common.hh" 21 | #include "logging.hh" 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | /* 37 | * Setup SIGABRT and SIGSEGV signal handler for backtracing 38 | */ 39 | static void __attribute__((constructor)) setup_kill_signals() 40 | { 41 | struct sigaction action; 42 | memset(&action, 0, sizeof(action)); 43 | action.sa_handler = signalHandler; 44 | action.sa_flags = SA_SIGINFO; 45 | 46 | if(sigaction(SIGSEGV, &action, NULL) < 0) 47 | perror("sigaction"); 48 | if(sigaction(SIGABRT, &action, NULL) < 0) 49 | perror("sigaction"); 50 | } 51 | 52 | /* 53 | * Print backtrace to stderr. 54 | */ 55 | void printBacktrace() 56 | { 57 | int j, nptrs; 58 | #define SIZE 100 59 | void *buffer[100]; 60 | char **strings; 61 | 62 | nptrs = backtrace(buffer, SIZE); 63 | std::cerr << _("backtrace() returned ") << nptrs << _(" addresses\n"); 64 | 65 | /* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO) 66 | would produce similar output to the following: */ 67 | 68 | strings = backtrace_symbols(buffer, nptrs); 69 | if (strings == NULL) { 70 | perror("backtrace_symbols"); 71 | exit(EXIT_FAILURE); 72 | } 73 | 74 | for (j = 0; j < nptrs; j++) 75 | std::cerr << strings[j] << std::endl; 76 | 77 | free(strings); 78 | } 79 | 80 | /* 81 | * This is the default signal handler funktion on UNIX signals. 82 | * 83 | * Print backtrace on SIGABRT or SIGSEGV signal. SIGABRT is send when an assert() failed. 84 | * Assert calls abort(3), which raise SIGABRT before forcing process termination. 85 | * It does this by restoring the default disposition for SIGABRT and then raising 86 | * the signal for a second time. 87 | * 88 | * On all other signals, stop execution when process reaches 89 | * an InterruptAble::interruptionPoint(). 90 | */ 91 | void signalHandler(int signum) 92 | { 93 | if(signum == SIGABRT || signum == SIGSEGV) 94 | { 95 | std::cerr << strsignal(signum) << std::endl; 96 | printBacktrace(); 97 | exit(1); 98 | } 99 | 100 | Interruptible::interrupt(); 101 | } 102 | 103 | /* 104 | * To support redirecting file-streams to stdin at startup 105 | * we have to set stdin to non-blocking. 106 | */ 107 | void setStdIn2NonBlocking() 108 | { 109 | int cinfd = fileno(stdin); 110 | 111 | const int fcflags = fcntl(cinfd,F_GETFL); 112 | 113 | if (fcflags<0) 114 | { 115 | std::cerr << _("cannot read stdin flags\n"); 116 | } 117 | 118 | if (fcntl(cinfd,F_SETFL,fcflags | O_NONBLOCK) <0) 119 | { 120 | std::cerr << _("cannot set stdin to non-blocking\n"); 121 | } 122 | 123 | std::cin.exceptions ( std::ifstream::eofbit 124 | | std::ifstream::failbit 125 | | std::ifstream::badbit ); 126 | } 127 | 128 | /* 129 | * Convert string containing wildcard characters to a valid boost regular expression. 130 | * 1: Replace . with \. (i.e. escape it) 131 | * 2: Replace any ? with . 132 | * 3: Replace any * with .* 133 | */ 134 | const boost::regex path2regex(std::string path) 135 | { 136 | return boost::regex( 137 | boost::regex_replace(path, 138 | boost::regex("(\\.)|(\\?)|(\\*)"), 139 | "(?1\\\\.)(?2.)(?3.*)", 140 | boost::match_default | boost::format_all )); 141 | } 142 | 143 | /* 144 | * return list of paths match filesearch filter 145 | * filesearch string may consist of simple wildcards: 146 | * VALID: /dev/?da* 147 | * INVALID: * /bin/ * (inserted spaces to avoid and of comment) 148 | */ 149 | std::vector matchPath( const std::string & filesearch ) 150 | { 151 | std::vector fileset; 152 | // Initialize path p (if necessary prepend with ./ so current directory is used) 153 | fs::path p(filesearch); 154 | if( !fs::path(filesearch).has_parent_path() ) 155 | { 156 | p = "./"; 157 | p /= filesearch; 158 | } 159 | // Initialize regex filter - use * as default if nothing is given in filesearch 160 | std::string f( p.has_filename() ? p.filename().string() : "*"); 161 | 162 | fs::path dir(system_complete(p).parent_path()); 163 | if( is_directory(dir) ) 164 | // Iterate through contents (files/directories) of directory... 165 | for( boost::filesystem::directory_iterator it(dir); 166 | it!=boost::filesystem::directory_iterator(); 167 | ++it ) 168 | { 169 | if( boost::regex_match( it->path().filename().string(), path2regex(f) ) ) 170 | fileset.push_back(it->path().string()); 171 | } 172 | return fileset; 173 | } 174 | 175 | /* 176 | * Determine full path. 177 | * If base path is empty use current_path() at the time of entry to main(). 178 | * Resolve "." and ".." and merge with working directory 179 | */ 180 | fs::path realpath(fs::path ph, fs::path base) 181 | { 182 | fs::path mypath; 183 | if(!base.has_root_directory()) 184 | base.clear(); 185 | /* 186 | * boost::filesystem2::compare() throw an assert on path names like: "//.esd.conf" 187 | * I don't know why but path::is_complete() returns false while path::has_root_name() 188 | * is true. 189 | * Fix this by removing leading double slash. 190 | */ 191 | while(ph.string().size() > 1 && !ph.string().compare(0,2, "//")) 192 | ph = ph.string().substr(1); 193 | 194 | while(base.string().size() > 1 && !base.string().compare(0,2, "//")) 195 | base = base.string().substr(1); 196 | 197 | /* 198 | * combine base and path to full path 199 | */ 200 | if(base.empty()) 201 | mypath = complete(ph); //base is current working directory 202 | else 203 | mypath = complete(ph, base); 204 | 205 | fs::path result; 206 | 207 | /* 208 | * Clean up full path by resolving ".." and "." directories. 209 | */ 210 | for(fs::path::iterator it = mypath.begin(); 211 | it != mypath.end(); 212 | it++) 213 | { 214 | if(*it == "..") 215 | result = result.parent_path(); 216 | else if(*it == ".") 217 | continue; 218 | else 219 | result /= (*it); 220 | } 221 | 222 | return result; 223 | } 224 | 225 | /* 226 | * Get path of file opened on file descriptor fd. 227 | * Call readlink() on /proc/self/fd/ 228 | */ 229 | std::string getPathFromFd(int fd) 230 | { 231 | char __filename[PATH_MAX]; 232 | char __path2fd[1024]; 233 | memset(__filename, '\0', PATH_MAX); 234 | sprintf(__path2fd, "/proc/self/fd/%d", fd); 235 | 236 | if(-1 == readlink(__path2fd, __filename, PATH_MAX)) 237 | { 238 | std::stringstream ss; 239 | ss << _("Cannot readlink: ") 240 | << fd 241 | << ": " << strerror(errno); 242 | throw std::runtime_error(ss.str()); 243 | } 244 | return __filename; 245 | } 246 | 247 | /* 248 | * Read pid number from pid file 249 | */ 250 | pid_t readPidFile(const char* path) 251 | { 252 | int tmp; 253 | 254 | FILE* file = fopen(path, "r"); 255 | if(NULL == file) 256 | return 0; 257 | 258 | if(EOF == fscanf(file, "%d", &tmp)) 259 | return 0; 260 | 261 | fclose(file); 262 | 263 | return tmp; 264 | } 265 | 266 | /* 267 | * Create pid file 268 | * Return true on success 269 | * false file already exists 270 | */ 271 | bool createPidFile(const char* path) 272 | { 273 | int fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0600); 274 | if(-1 == fd) 275 | { 276 | if(errno == EEXIST) 277 | return false; 278 | else if(errno == EROFS) 279 | { 280 | error(_("Cannot create pid file %s on read-only filesystem"), path); 281 | return true; 282 | } 283 | else 284 | { 285 | error(_("Cannot open pid file: %s"), strerror(errno)); 286 | return false; 287 | } 288 | } 289 | 290 | FILE* file = fdopen(fd, "w"); 291 | if(NULL == file) 292 | { 293 | error(_("Cannot open pid file %s: %s"), path, strerror(errno)); 294 | return false; 295 | } 296 | 297 | fprintf(file, "%d", getpid()); 298 | fclose(file); 299 | 300 | return true; 301 | } 302 | 303 | 304 | bool Interruptible::interrupted = false; 305 | 306 | void Interruptible::interrupt() 307 | { 308 | interrupted = true; 309 | } 310 | 311 | void Interruptible::interruptionPoint() 312 | { 313 | if(interrupted) 314 | throw UserInterrupt(); 315 | } 316 | -------------------------------------------------------------------------------- /src/device.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * device.cc - get/set paramters for a block device 3 | * 4 | * Copyright (C) 2011 by Andreas Rid 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include "device.hh" 21 | #include "balloc.h" 22 | #include "logging.hh" 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | 35 | #define BLOCKS_PER_GROUP(fs) (fs->super->s_blocks_per_group) 36 | #define BLOCKS_PER_FLEX(fs) (BLOCKS_PER_GROUP(fs) << fs->super->s_log_groups_per_flex) 37 | #define FREE_BLOCKS_PER_GROUP(fs) ( BLOCKS_PER_GROUP(fs) \ 38 | - 2 \ 39 | - fs->super->s_inode_size * fs->super->s_inodes_per_group / fs->blocksize \ 40 | ) 41 | 42 | #define FREE_BLOCKS_PER_FLEX(fs) (FREE_BLOCKS_PER_GROUP(fs) << fs->super->s_log_groups_per_flex) 43 | 44 | /* 45 | * get mount-point from arbitrary path 46 | * path should have a root directory '/' 47 | */ 48 | fs::path __getMountPoint(fs::path path) 49 | { 50 | struct stat st; 51 | dev_t dev; 52 | 53 | fs::path cur_dir = path.parent_path(); 54 | 55 | //test weather path is root directory '/' 56 | if(cur_dir.string().empty()) 57 | return path; 58 | 59 | if(0 > stat(cur_dir.string().c_str(), &st)) 60 | goto err; 61 | dev = st.st_dev; 62 | 63 | while(1) 64 | { 65 | if(0 > stat(cur_dir.string().c_str(), &st)) 66 | goto err; 67 | if(st.st_dev != dev) 68 | return path; 69 | 70 | cur_dir = cur_dir.parent_path(); 71 | 72 | if(cur_dir == "/" || cur_dir.empty()) 73 | return "/"; 74 | } 75 | 76 | err: 77 | throw std::runtime_error(std::string(_("Cannot get MountPoint of path: ")) 78 | +path.string()); 79 | } 80 | 81 | 82 | DevicePrivate::DevicePrivate() 83 | { 84 | fs = 0; 85 | } 86 | 87 | DevicePrivate::~DevicePrivate() 88 | { 89 | if(fs) 90 | ext2fs_close(fs); 91 | } 92 | 93 | 94 | Device::Device(fs::path file) 95 | : boost::shared_ptr(new DevicePrivate) 96 | { 97 | struct stat st; 98 | if(lstat(file.string().c_str(), &st)) 99 | { 100 | std::stringstream ss; 101 | ss << _("Cannot get devno from file ") << file.string() << _(" to create Device object"); 102 | throw std::runtime_error(ss.str()); 103 | } 104 | if(S_ISBLK(st.st_mode)) 105 | get()->devno = st.st_rdev; 106 | else 107 | get()->devno = st.st_dev; 108 | } 109 | 110 | Device::Device(dev_t dev) 111 | : boost::shared_ptr(new DevicePrivate) 112 | { 113 | get()->devno = dev; 114 | } 115 | 116 | void Device::parseMtabFile(const char* path) 117 | { 118 | FILE* fmtab; 119 | struct mntent *mnt = NULL; 120 | struct stat st; 121 | 122 | fmtab = setmntent(path, "r"); 123 | if(fmtab == NULL) 124 | throw std::runtime_error(std::string(_("Cannot access ")) + path + ": " + strerror(errno)); 125 | 126 | while((mnt = getmntent(fmtab)) != NULL) 127 | { 128 | if(0 == strcmp(mnt->mnt_type, "rootfs")) 129 | continue; 130 | if(stat(mnt->mnt_dir, &st)) 131 | continue; 132 | if(st.st_dev == get()->devno) 133 | { 134 | get()->mount_point = mnt->mnt_dir; 135 | get()->fs_name = mnt->mnt_type; 136 | break; 137 | } 138 | } 139 | 140 | endmntent(fmtab); 141 | } 142 | 143 | void Device::parseMtab() 144 | { 145 | if(0 ==access("/proc/mounts", R_OK)) 146 | { 147 | parseMtabFile("/proc/mounts"); 148 | if( get()->fs_name == "ext2") 149 | // maybe /proc/mounts is not up to date cause user forget to setup 150 | // rootfstype=ext4 to kernel parameters. 151 | parseMtabFile(MOUNTED); 152 | } 153 | else if(0 == access(MOUNTED, R_OK)) 154 | parseMtabFile(MOUNTED); 155 | else 156 | throw std::runtime_error(_("Neither /proc/mounts nor /etc/mtab is readable.")); 157 | } 158 | fs::path Device::getMountPoint() 159 | { 160 | if(get()->mount_point.empty()) 161 | parseMtab(); 162 | 163 | return get()->mount_point; 164 | } 165 | 166 | /* 167 | * Create ext2fs object which gave access to the ext2 superblock and other 168 | * filesystem information 169 | */ 170 | bool Device::open() 171 | { 172 | if( ext2fs_open(getDevicePath().c_str(), 173 | EXT2_FLAG_RW | EXT2_FLAG_JOURNAL_DEV_OK | EXT2_FLAG_SOFTSUPP_FEATURES, 174 | 0, 0, unix_io_manager, &get()->fs)) 175 | return false; 176 | return true; 177 | } 178 | 179 | std::string Device::getFileSystem() 180 | { 181 | if(get()->fs_name.empty()) 182 | parseMtab(); 183 | return get()->fs_name; 184 | } 185 | 186 | /* 187 | * convert dev_t to device string 188 | * Iterate over /dev directory 189 | * return 0 on success otherwise -1 190 | */ 191 | int Device::getDevNameFromDevfs() 192 | { 193 | struct stat st; 194 | fs::directory_iterator end_itr; // default construction yields past-the-end 195 | for ( fs::directory_iterator it("/dev"); 196 | it != end_itr; 197 | ++it ) 198 | { 199 | if(it->path().string() == "/dev/root") 200 | continue; 201 | if(lstat(it->path().c_str(), &st)) 202 | continue; 203 | if(st.st_rdev == get()->devno && S_ISBLK(st.st_mode)) 204 | { 205 | get()->devicePath = it->path().string(); 206 | get()->deviceName = it->path().filename().string(); 207 | return 0; 208 | } 209 | } 210 | return -1; 211 | } 212 | 213 | /* 214 | * return 0 success otherwise -1 215 | */ 216 | int Device::getDevNameFromMajorMinor() 217 | { 218 | std::stringstream ss; 219 | int major = major(get()->devno); 220 | int minor = minor(get()->devno); 221 | 222 | switch(major) 223 | { 224 | case 0: 225 | // the minor number of virtual filesystems are allocated dynamically in function set_anon_super() in fs/super.c 226 | // for convenience set deviceName and devicePath to a common name 227 | get()->deviceName = "virtual file system"; 228 | get()->devicePath = get()->mount_point.filename().string(); 229 | return 0; 230 | case 2: 231 | ss << "fd"; 232 | goto devicename_has_no_letter; 233 | case 3: 234 | ss << "hd"; break; 235 | case 8: 236 | ss << "sd"; break; 237 | case 254: 238 | ss << "dm-"; 239 | goto devicename_has_no_letter; 240 | default: 241 | return -1; 242 | } 243 | 244 | ss << (char)(0x61 + (minor >>4)); 245 | 246 | devicename_has_no_letter: 247 | ss << (minor & 0b1111); 248 | 249 | get()->deviceName = ss.str(); 250 | get()->devicePath = "/dev/" + get()->deviceName; 251 | return 0; 252 | } 253 | 254 | 255 | bool isMountPoint(fs::path p) 256 | { 257 | struct stat st1, st2; 258 | if(-1 == stat(p.string().c_str(), &st1) 259 | || -1 == stat(p.parent_path().string().c_str(), &st2)) 260 | return false; 261 | 262 | if(st1.st_dev == st2.st_dev) 263 | return false; 264 | else 265 | return true; 266 | } 267 | 268 | /* 269 | * Throw runtime_error on error 270 | */ 271 | std::string Device::getDeviceName() 272 | { 273 | if(-1 == getDevNameFromMajorMinor()) 274 | { 275 | if(!isMountPoint("/dev")) 276 | throw std::runtime_error(_("Unknown block device: devfs is not mounted")); 277 | 278 | if(-1 == getDevNameFromDevfs()) 279 | throw std::runtime_error(_("Unknown block device: no such device found in /dev")); 280 | } 281 | 282 | return get()->deviceName; 283 | } 284 | 285 | /* 286 | * return full path of device 287 | * example: /dev/sda1 288 | */ 289 | std::string Device::getDevicePath() 290 | { 291 | if(get()->devicePath.empty()) 292 | getDeviceName(); 293 | return get()->devicePath; 294 | } 295 | 296 | /* 297 | * this function is used by read and write ext4 tuning parameters 298 | */ 299 | void Device::openSysFsExt4File(std::filebuf* fb, std::string filename, std::ios_base::openmode mode) 300 | { 301 | std::string fullPath = std::string("/sys/fs/ext4/") 302 | + getDeviceName() 303 | + "/" + filename; 304 | 305 | if(NULL == fb->open(fullPath.c_str(), mode)) 306 | throw std::runtime_error(std::string(_("Cannot open file: ")) + fullPath); 307 | } 308 | 309 | /* 310 | * set tuning parameter to current ext4 filesystem 311 | * write value to /sys/fs/ext4//