diff options
Diffstat (limited to '')
-rw-r--r-- | src/fstat_vtab.cc | 368 |
1 files changed, 368 insertions, 0 deletions
diff --git a/src/fstat_vtab.cc b/src/fstat_vtab.cc new file mode 100644 index 0000000..09567c7 --- /dev/null +++ b/src/fstat_vtab.cc @@ -0,0 +1,368 @@ +/** + * Copyright (c) 2017, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fstat_vtab.hh" + +#include <glob.h> +#include <grp.h> +#include <pwd.h> +#include <string.h> +#include <sys/stat.h> + +#include "base/auto_mem.hh" +#include "base/injector.hh" +#include "base/lnav_log.hh" +#include "bound_tags.hh" +#include "config.h" +#include "sql_util.hh" +#include "vtab_module.hh" + +enum { + FSTAT_COL_PARENT, + FSTAT_COL_NAME, + FSTAT_COL_DEV, + FSTAT_COL_INO, + FSTAT_COL_TYPE, + FSTAT_COL_MODE, + FSTAT_COL_NLINK, + FSTAT_COL_UID, + FSTAT_COL_USER, + FSTAT_COL_GID, + FSTAT_COL_GROUP, + FSTAT_COL_RDEV, + FSTAT_COL_SIZE, + FSTAT_COL_BLKSIZE, + FSTAT_COL_BLOCKS, + FSTAT_COL_ATIME, + FSTAT_COL_MTIME, + FSTAT_COL_CTIME, + FSTAT_COL_PATTERN, +}; + +/** + * @feature f0:sql.tables.fstat + */ +struct fstat_table { + static constexpr const char* NAME = "fstat"; + static constexpr const char* CREATE_STMT = R"( +CREATE TABLE fstat ( + st_parent TEXT, + st_name TEXT, + st_dev INTEGER, + st_ino INTEGER, + st_type TEXT, + st_mode INTEGER, + st_nlink INTEGER, + st_uid TEXT, + st_user TEXT, + st_gid TEXT, + st_group TEXT, + st_rdev INTEGER, + st_size INTEGER, + st_blksize INTEGER, + st_blocks INTEGER, + st_atime DATETIME, + st_mtime DATETIME, + st_ctime DATETIME, + pattern TEXT HIDDEN +); +)"; + + struct cursor { + sqlite3_vtab_cursor base; + std::string c_pattern; + static_root_mem<glob_t, globfree> c_glob; + size_t c_path_index{0}; + struct stat c_stat; + + cursor(sqlite3_vtab* vt) : base({vt}) + { + memset(&this->c_stat, 0, sizeof(this->c_stat)); + } + + void load_stat() + { + while ((this->c_path_index < this->c_glob->gl_pathc) + && lstat(this->c_glob->gl_pathv[this->c_path_index], + &this->c_stat) + == -1) + { + this->c_path_index += 1; + } + } + + int next() + { + if (this->c_path_index < this->c_glob->gl_pathc) { + this->c_path_index += 1; + this->load_stat(); + } + + return SQLITE_OK; + } + + int reset() { return SQLITE_OK; } + + int eof() { return this->c_path_index >= this->c_glob->gl_pathc; } + + int get_rowid(sqlite3_int64& rowid_out) + { + rowid_out = this->c_path_index; + + return SQLITE_OK; + } + }; + + int get_column(const cursor& vc, sqlite3_context* ctx, int col) + { + const char* path = vc.c_glob->gl_pathv[vc.c_path_index]; + char time_buf[32]; + + switch (col) { + case FSTAT_COL_PARENT: { + const char* slash = strrchr(path, '/'); + + if (slash == nullptr) { + sqlite3_result_text(ctx, ".", 1, SQLITE_STATIC); + } else if (path[1] == '\0') { + sqlite3_result_text(ctx, "", 0, SQLITE_STATIC); + } else { + sqlite3_result_text( + ctx, path, slash - path + 1, SQLITE_TRANSIENT); + } + break; + } + case FSTAT_COL_NAME: { + const char* slash = strrchr(path, '/'); + + if (slash == nullptr) { + sqlite3_result_text(ctx, path, -1, SQLITE_TRANSIENT); + } else { + sqlite3_result_text(ctx, slash + 1, -1, SQLITE_TRANSIENT); + } + break; + } + case FSTAT_COL_DEV: + sqlite3_result_int(ctx, vc.c_stat.st_dev); + break; + case FSTAT_COL_INO: + sqlite3_result_int64(ctx, vc.c_stat.st_ino); + break; + case FSTAT_COL_TYPE: + if (S_ISREG(vc.c_stat.st_mode)) { + sqlite3_result_text(ctx, "reg", 3, SQLITE_STATIC); + } else if (S_ISBLK(vc.c_stat.st_mode)) { + sqlite3_result_text(ctx, "blk", 3, SQLITE_STATIC); + } else if (S_ISCHR(vc.c_stat.st_mode)) { + sqlite3_result_text(ctx, "chr", 3, SQLITE_STATIC); + } else if (S_ISDIR(vc.c_stat.st_mode)) { + sqlite3_result_text(ctx, "dir", 3, SQLITE_STATIC); + } else if (S_ISFIFO(vc.c_stat.st_mode)) { + sqlite3_result_text(ctx, "fifo", 4, SQLITE_STATIC); + } else if (S_ISLNK(vc.c_stat.st_mode)) { + sqlite3_result_text(ctx, "lnk", 3, SQLITE_STATIC); + } else if (S_ISSOCK(vc.c_stat.st_mode)) { + sqlite3_result_text(ctx, "sock", 3, SQLITE_STATIC); + } + break; + case FSTAT_COL_MODE: + sqlite3_result_int(ctx, vc.c_stat.st_mode & 0777); + break; + case FSTAT_COL_NLINK: + sqlite3_result_int(ctx, vc.c_stat.st_nlink); + break; + case FSTAT_COL_UID: + sqlite3_result_int(ctx, vc.c_stat.st_uid); + break; + case FSTAT_COL_USER: { + struct passwd* pw = getpwuid(vc.c_stat.st_uid); + + if (pw != nullptr) { + sqlite3_result_text(ctx, pw->pw_name, -1, SQLITE_TRANSIENT); + } else { + sqlite3_result_int(ctx, vc.c_stat.st_uid); + } + break; + } + case FSTAT_COL_GID: + sqlite3_result_int(ctx, vc.c_stat.st_gid); + break; + case FSTAT_COL_GROUP: { + struct group* gr = getgrgid(vc.c_stat.st_gid); + + if (gr != nullptr) { + sqlite3_result_text(ctx, gr->gr_name, -1, SQLITE_TRANSIENT); + } else { + sqlite3_result_int(ctx, vc.c_stat.st_gid); + } + break; + } + case FSTAT_COL_RDEV: + sqlite3_result_int(ctx, vc.c_stat.st_rdev); + break; + case FSTAT_COL_SIZE: + sqlite3_result_int64(ctx, vc.c_stat.st_size); + break; + case FSTAT_COL_BLKSIZE: + sqlite3_result_int(ctx, vc.c_stat.st_blksize); + break; + case FSTAT_COL_BLOCKS: + sqlite3_result_int(ctx, vc.c_stat.st_blocks); + break; + case FSTAT_COL_ATIME: + sql_strftime(time_buf, sizeof(time_buf), vc.c_stat.st_atime, 0); + sqlite3_result_text(ctx, time_buf, -1, SQLITE_TRANSIENT); + break; + case FSTAT_COL_MTIME: + sql_strftime(time_buf, sizeof(time_buf), vc.c_stat.st_mtime, 0); + sqlite3_result_text(ctx, time_buf, -1, SQLITE_TRANSIENT); + break; + case FSTAT_COL_CTIME: + sql_strftime(time_buf, sizeof(time_buf), vc.c_stat.st_ctime, 0); + sqlite3_result_text(ctx, time_buf, -1, SQLITE_TRANSIENT); + break; + case FSTAT_COL_PATTERN: + sqlite3_result_text(ctx, + vc.c_pattern.c_str(), + vc.c_pattern.length(), + SQLITE_TRANSIENT); + break; + } + + return SQLITE_OK; + } + +#if 0 + int update_row(sqlite3_vtab *tab, + sqlite3_int64 &index, + const char *st_parent, + const char *st_name, + int64_t st_dev, + int64_t st_ino, + const char *st_type, + int64_t st_mode, + int64_t st_nlink, + int64_t st_uid, + const char *st_user, + int64_t st_gid, + const char *st_group, + int64_t st_rdev, + int64_t st_size, + int64_t st_blksize, + int64_t st_blocks, + const char *atime, + const char *mtime, + const char *ctime, + const char *pattern) { + + }; +#endif +}; + +static int +rcBestIndex(sqlite3_vtab* tab, sqlite3_index_info* pIdxInfo) +{ + vtab_index_constraints vic(pIdxInfo); + vtab_index_usage viu(pIdxInfo); + + for (auto iter = vic.begin(); iter != vic.end(); ++iter) { + if (iter->op != SQLITE_INDEX_CONSTRAINT_EQ) { + continue; + } + + switch (iter->iColumn) { + case FSTAT_COL_PATTERN: + viu.column_used(iter); + break; + } + } + + viu.allocate_args(FSTAT_COL_PATTERN, FSTAT_COL_PATTERN, 1); + return SQLITE_OK; +} + +static int +rcFilter(sqlite3_vtab_cursor* pVtabCursor, + int idxNum, + const char* idxStr, + int argc, + sqlite3_value** argv) +{ + fstat_table::cursor* pCur = (fstat_table::cursor*) pVtabCursor; + + if (argc != 1) { + pCur->c_pattern.clear(); + return SQLITE_OK; + } + + const char* pattern = (const char*) sqlite3_value_text(argv[0]); + pCur->c_pattern = pattern; + switch (glob(pattern, +#ifdef GLOB_TILDE + GLOB_TILDE | +#endif + GLOB_ERR, + nullptr, + pCur->c_glob.inout())) + { + case GLOB_NOSPACE: + pVtabCursor->pVtab->zErrMsg + = sqlite3_mprintf("No space to perform glob()"); + return SQLITE_ERROR; + case GLOB_NOMATCH: + return SQLITE_OK; + } + + pCur->load_stat(); + + return SQLITE_OK; +} + +int +register_fstat_vtab(sqlite3* db) +{ + static vtab_module<tvt_no_update<fstat_table>> FSTAT_MODULE; + + int rc; + + FSTAT_MODULE.vm_module.xBestIndex = rcBestIndex; + FSTAT_MODULE.vm_module.xFilter = rcFilter; + + static auto& lnav_flags = injector::get<unsigned long&, lnav_flags_tag>(); + + if (lnav_flags & LNF_SECURE_MODE) { + return SQLITE_OK; + } + + rc = FSTAT_MODULE.create(db, "fstat"); + + ensure(rc == SQLITE_OK); + + return rc; +} |