/** * 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 #include #include #include #include #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 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> FSTAT_MODULE; int rc; FSTAT_MODULE.vm_module.xBestIndex = rcBestIndex; FSTAT_MODULE.vm_module.xFilter = rcFilter; static auto& lnav_flags = injector::get(); if (lnav_flags & LNF_SECURE_MODE) { return SQLITE_OK; } rc = FSTAT_MODULE.create(db, "fstat"); ensure(rc == SQLITE_OK); return rc; }