FTL/src/files.c

206 lines
5.7 KiB
C

/* Pi-hole: A black hole for Internet advertisements
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
* Network-wide ad blocking via your own hardware.
*
* FTL Engine
* File operation routines
*
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
#include "FTL.h"
#include "files.h"
#include "config.h"
#include "setupVars.h"
#include "log.h"
// opendir(), readdir()
#include <dirent.h>
// getpwuid()
#include <pwd.h>
// getgrgid()
#include <grp.h>
// NAME_MAX
#include <limits.h>
// statvfs()
#include <sys/statvfs.h>
// dirname()
#include <libgen.h>
// chmod_file() changes the file mode bits of a given file (relative
// to the directory file descriptor) according to mode. mode is an
// octal number representing the bit pattern for the new mode bits
bool chmod_file(const char *filename, const mode_t mode)
{
if(chmod(filename, mode) < 0)
{
logg("WARNING: chmod(%s, %d): chmod() failed: %s (%d)", filename, mode, strerror(errno), errno);
return false;
}
struct stat st;
if(stat(filename, &st) < 0)
{
logg("WARNING: chmod(%s, %d): stat() failed: %s (%d)", filename, mode, strerror(errno), errno);
return false;
}
// We need to apply a bitmask on st.st_mode as the upper bits may contain random data
// 0x1FF = 0b111_111_111 corresponding to the three-digit octal mode number
if((st.st_mode & 0x1FF) != mode)
{
logg("WARNING: chmod(%s, %d): Verification failed, %d != %d", filename, mode, st.st_mode, mode);
return false;
}
return true;
}
bool file_exists(const char *filename)
{
struct stat st;
return stat(filename, &st) == 0;
}
unsigned long long get_FTL_db_filesize(void)
{
struct stat st;
if(stat(FTLfiles.FTL_db, &st) != 0)
{
// stat() failed (maybe the DB file does not exist?)
return 0;
}
return st.st_size;
}
void ls_dir(const char* path)
{
// Open directory stream
DIR* dirp = opendir(path);
if(dirp == NULL)
{
logg("opendir(\"%s\") failed with %s (%d)", path, strerror(errno), errno);
return;
}
// Stack space for full path (directory + "/" + filename + terminating \0)
char full_path[strlen(path)+NAME_MAX+2];
logg("------ Listing content of directory %s ------", path);
logg("File Mode User:Group Size Filename");
struct dirent *dircontent = NULL;
// Walk directory file by file
while((dircontent = readdir(dirp)) != NULL)
{
// Get filename
const char *filename = dircontent->d_name;
// Construct full path
snprintf(full_path, sizeof(full_path), "%s/%s", path, filename);
struct stat st;
// Use stat to get file size, permissions, and ownership
if(stat(full_path, &st) < 0)
{
logg("%s failed with %s (%d)", filename, strerror(errno), errno);
continue;
}
// Get owner's name
struct passwd *pwd;
char user[256];
if ((pwd = getpwuid(st.st_uid)) != NULL)
snprintf(user, sizeof(user), "%s", pwd->pw_name);
else
snprintf(user, sizeof(user), "%d", st.st_uid);
struct group *grp;
char usergroup[256];
// Get out group name
if ((grp = getgrgid(st.st_gid)) != NULL)
snprintf(usergroup, sizeof(usergroup), "%s:%s", user, grp->gr_name);
else
snprintf(usergroup, sizeof(usergroup), "%s:%d", user, st.st_gid);
char permissions[10];
// Get human-readable format of permissions as known from ls
snprintf(permissions, sizeof(permissions),
"%s%s%s%s%s%s%s%s%s",
st.st_mode & S_IRUSR ? "r":"-",
st.st_mode & S_IWUSR ? "w":"-",
st.st_mode & S_IXUSR ? "x":"-",
st.st_mode & S_IRGRP ? "r":"-",
st.st_mode & S_IWGRP ? "w":"-",
st.st_mode & S_IXGRP ? "x":"-",
st.st_mode & S_IROTH ? "r":"-",
st.st_mode & S_IWOTH ? "w":"-",
st.st_mode & S_IXOTH ? "x":"-");
char prefix[2] = { 0 };
double formatted = 0.0;
format_memory_size(prefix, (unsigned long long)st.st_size, &formatted);
// Log output for this file
logg("%s %-15s %3.0f%s %s", permissions, usergroup, formatted, prefix, filename);
}
logg("---------------------------------------------------");
// Close directory stream
closedir(dirp);
}
int get_path_usage(const char *path, char buffer[64])
{
// Get filesystem information about /dev/shm (typically a tmpfs)
struct statvfs f;
if(statvfs(path, &f) != 0)
{
// If statvfs() failed, we return the error instead
strncpy(buffer, strerror(errno), 64);
buffer[63] = '\0';
return 0;
}
// Explicitly cast the block counts to unsigned long long to avoid
// overflowing with drives larger than 4 GB on 32bit systems
const unsigned long long size = (unsigned long long)f.f_blocks * f.f_frsize;
const unsigned long long free = (unsigned long long)f.f_bavail * f.f_bsize;
const unsigned long long used = size - free;
// Create human-readable total size
char prefix_size[2] = { 0 };
double formatted_size = 0.0;
format_memory_size(prefix_size, size, &formatted_size);
// Generate human-readable "total used" size
char prefix_used[2] = { 0 };
double formatted_used = 0.0;
format_memory_size(prefix_used, used, &formatted_used);
// Print result into buffer passed to this subroutine
snprintf(buffer, 64, "%s: %.1f%sB used, %.1f%sB total", path,
formatted_used, prefix_used, formatted_size, prefix_size);
// Return percentage of used shared memory
// Adding 1 avoids FPE if the size turns out to be zero
return (used*100)/(size + 1);
}
int get_filepath_usage(const char *file, char buffer[64])
{
if(file == NULL || strlen(file) == 0)
return -1;
// Get path from file, we duplicate the string
// here as dirname() modifies the string inplace
char path[PATH_MAX] = { 0 };
strncpy(path, file, sizeof(path)-1);
path[sizeof(path)-1] = '\0';
dirname(path);
// Get percentage of disk usage at this path
return get_path_usage(path, buffer);
}