From 25a988442e34c3ef76dc88659e65c86fef5c48a8 Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Tue, 29 Jan 2013 20:01:46 +0100 Subject: [PATCH] Support filtering paths in the mkinitrd program. This will allow initrds to omit certain files, such as other initrds, irrelevant files, iles for another platform, and so on. This will be useful when initrd contain entire system roots. --- Makefile | 3 +- compiler.mak | 2 + mkinitrd/Makefile | 6 +- mkinitrd/mkinitrd.cpp | 89 ++++++++++---- mkinitrd/rules.cpp | 265 ++++++++++++++++++++++++++++++++++++++++++ mkinitrd/rules.h | 66 +++++++++++ 6 files changed, 405 insertions(+), 26 deletions(-) create mode 100644 mkinitrd/rules.cpp create mode 100644 mkinitrd/rules.h diff --git a/Makefile b/Makefile index a8f8308c..25d0cc6a 100644 --- a/Makefile +++ b/Makefile @@ -112,7 +112,8 @@ all-archs: # Initializing RamDisk $(INITRD): suball - mkinitrd/mkinitrd $(SYSROOT)/bin/$(HOST) -o $(INITRD) + echo > "$(INITRD).filter" + mkinitrd/mkinitrd --filter "$(INITRD).filter" "$(SYSROOT)/bin/$(HOST)" -o "$(INITRD)" # Statistics linecount: diff --git a/compiler.mak b/compiler.mak index a1dacefa..28cde99d 100644 --- a/compiler.mak +++ b/compiler.mak @@ -13,9 +13,11 @@ endif ifeq ($(HOST),i486-sortix) CPU:=x86 + OTHER_PLATFORMS=x86-64-sortix endif ifeq ($(HOST),x86_64-sortix) CPU:=x64 + OTHER_PLATFORMS=i486-sortix endif ifndef BUILDCC diff --git a/mkinitrd/Makefile b/mkinitrd/Makefile index 19cdef50..a6a61a89 100644 --- a/mkinitrd/Makefile +++ b/mkinitrd/Makefile @@ -2,14 +2,14 @@ SORTIXKERNEL=../sortix LIBC=../libc CPPFLAGS=-I../sortix/include -CXXFLAGS=-Wall +CXXFLAGS=-g -Wall -Wextra BINARIES=mkinitrd initrdfs all: $(BINARIES) -%: %.cpp crc32.cpp $(LIBC)/ioleast.cpp - $(CXX) $(CPPFLAGS) $(CXXFLAGS) $< crc32.cpp $(LIBC)/ioleast.cpp -o $@ +%: %.cpp crc32.cpp rules.cpp $(LIBC)/ioleast.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $< crc32.cpp rules.cpp $(LIBC)/ioleast.cpp -o $@ clean: rm -f $(BINARIES) diff --git a/mkinitrd/mkinitrd.cpp b/mkinitrd/mkinitrd.cpp index 30ed77cb..9c251b65 100644 --- a/mkinitrd/mkinitrd.cpp +++ b/mkinitrd/mkinitrd.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2012. + Copyright(C) Jonas 'Sortie' Termansen 2012, 2013. This file is part of Sortix. @@ -36,6 +36,7 @@ #include #include "crc32.h" +#include "rules.h" #if !defined(sortix) __BEGIN_DECLS @@ -131,6 +132,8 @@ size_t cacheused = 0; size_t cachelen = 0; CacheEntry* cache = NULL; +InclusionRules path_filter; + Node* LookupCache(dev_t dev, ino_t ino) { for ( size_t i = 0; i < cacheused; i++ ) @@ -158,10 +161,16 @@ bool AddToCache(Node* node, dev_t dev, ino_t ino) return true; } -Node* RecursiveSearch(const char* rootpath, uint32_t* ino, Node* parent = NULL) +Node* RecursiveSearch(const char* real_path, const char* virt_path, + uint32_t* ino, Node* parent = NULL) { + printf("%s\n", virt_path); + + if ( virt_path[0] == '/' && !virt_path[1] ) + virt_path = ""; + struct stat st; - if ( lstat(rootpath, &st) ) { perror(rootpath); return NULL; } + if ( lstat(real_path, &st) ) { perror(real_path); return NULL; } Node* cached = LookupCache(st.st_dev, st.st_ino); if ( cached ) { cached->refcount++; return cached; } @@ -175,42 +184,61 @@ Node* RecursiveSearch(const char* rootpath, uint32_t* ino, Node* parent = NULL) node->ctime = st.st_ctime; node->mtime = st.st_mtime; - char* pathclone = strdup(rootpath); - if ( !pathclone ) { perror("strdup"); free(node); return NULL; } + char* real_path_clone = strdup(real_path); + if ( !real_path_clone ) { perror("strdup"); free(node); return NULL; } - node->path = pathclone; + node->path = real_path_clone; if ( !S_ISDIR(st.st_mode) ) { if ( !AddToCache(node, st.st_dev, st.st_ino) ) { - free(pathclone); + free(real_path_clone); free(node); return NULL; } return node; } - DIR* dir = opendir(rootpath); - if ( !dir ) { perror(rootpath); FreeNode(node); return NULL; } + DIR* dir = opendir(real_path); + if ( !dir ) { perror(real_path); FreeNode(node); return NULL; } - size_t rootpathlen = strlen(rootpath); + size_t real_path_len = strlen(real_path); + size_t virt_path_len = strlen(virt_path); bool successful = true; struct dirent* entry; while ( (entry = readdir(dir)) ) { size_t namelen = strlen(entry->d_name); - size_t subpathlen = namelen + 1 + rootpathlen; - char* subpath = (char*) malloc(subpathlen+1); - if ( !subpath ) { perror("malloc"); successful = false; break; } - stpcpy(stpcpy(stpcpy(subpath, rootpath), "/"), entry->d_name); + + size_t virt_subpath_len = virt_path_len + 1 + namelen; + char* virt_subpath = (char*) malloc(virt_subpath_len+1); + if ( !virt_subpath ) { perror("malloc"); successful = false; break; } + stpcpy(stpcpy(stpcpy(virt_subpath, virt_path), "/"), entry->d_name); + + if ( strcmp(entry->d_name, ".") != 0 && + strcmp(entry->d_name, "..") != 0 && + !path_filter.IncludesPath(virt_subpath) ) + { + free(virt_subpath); + continue; + } + + size_t real_subpath_len = real_path_len + 1 + namelen; + char* real_subpath = (char*) malloc(real_subpath_len+1); + if ( !real_subpath ) { free(virt_subpath); perror("malloc"); successful = false; break; } + stpcpy(stpcpy(stpcpy(real_subpath, real_path), "/"), entry->d_name); Node* child = NULL; - if ( !strcmp(entry->d_name, ".") ) { child = node; } - if ( !strcmp(entry->d_name, "..") ) { child = parent ? parent : node; } - if ( !child ) { child = RecursiveSearch(subpath, ino, node); } - free(subpath); + if ( !strcmp(entry->d_name, ".") ) + child = node; + if ( !strcmp(entry->d_name, "..") ) + child = parent ? parent : node; + if ( !child ) + child = RecursiveSearch(real_subpath, virt_subpath, ino, node); + free(real_subpath); + free(virt_subpath); if ( !child ) { successful = false; break; } if ( node->direntsused == node->direntslength ) @@ -410,7 +438,7 @@ bool Format(const char* pathname, uint32_t inodecount, Node* root) void Usage(FILE* fp, const char* argv0) { - fprintf(fp, "usage: %s -o \n", argv0); + fprintf(fp, "Usage: %s -o [-f ]\n", argv0); fprintf(fp, "Creates a init ramdisk for the Sortix kernel.\n"); } @@ -422,8 +450,8 @@ void Help(FILE* fp, const char* argv0) void Version(FILE* fp, const char* argv0) { (void) argv0; - fprintf(fp, "mkinitrd 0.3\n"); - fprintf(fp, "Copyright (C) 2012 Jonas 'Sortie' Termansen\n"); + fprintf(fp, "mkinitrd 0.4\n"); + fprintf(fp, "Copyright (C) 2012, 2013 Jonas 'Sortie' Termansen\n"); fprintf(fp, "This is free software; see the source for copying conditions. There is NO\n"); fprintf(fp, "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"); fprintf(fp, "website: http://www.maxsi.org/software/sortix/\n"); @@ -455,6 +483,23 @@ int main(int argc, char* argv[]) dest = argv[++i]; argv[i] = NULL; continue; } + if ( !strcmp(arg, "-f") || !strcmp(arg, "--filter") ) + { + if ( argsleft < 1 ) + { + fprintf(stderr, "No filter rule file specified\n"); + Usage(stderr, argv0); + exit(1); + } + const char* path = argv[++i]; argv[i] = NULL; + FILE* fp = fopen(path, "r"); + if ( !fp ) + error(1, errno, "%s", path); + if ( !path_filter.AddRulesFromFile(fp, stderr, path) ) + exit(1); + fclose(fp); + continue; + } fprintf(stderr, "%s: unknown option: %s\n", argv0, arg); Usage(stderr, argv0); exit(1); @@ -482,7 +527,7 @@ int main(int argc, char* argv[]) } uint32_t inodecount = 1; - Node* root = RecursiveSearch(rootstr, &inodecount); + Node* root = RecursiveSearch(rootstr, "/", &inodecount); if ( !root ) { exit(1); } if ( !Format(dest, inodecount, root) ) { exit(1); } diff --git a/mkinitrd/rules.cpp b/mkinitrd/rules.cpp new file mode 100644 index 00000000..d9364616 --- /dev/null +++ b/mkinitrd/rules.cpp @@ -0,0 +1,265 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along + with Sortix. If not, see . + + rules.cpp + Determines whether a given path is included in the filesystem. + +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rules.h" + +static void error_fp(FILE* fp, int status, int errnum, const char* format, ...) +{ + fprintf(fp, "%s: ", program_invocation_name); + + va_list list; + va_start(list, format); + vfprintf(fp, format, list); + va_end(list); + + if ( errnum ) + fprintf(fp, ": %s", strerror(errnum)); + fprintf(fp, "\n"); + if ( status ) + exit(status); +} + +static const char* SkipCharacters(const char* str, char c) +{ + while ( *str == c) + str++; + return str; +} + +// /usr/bin/foobar match /usr = true +// /usr/bin/foobar match usr = false +// ///usr////bin//foobar match //usr// = true +// ///usr////bin//foobar match //usr//./evince = false +// TODO: Should this support . and .. too? +static bool PathMatchesPattern(const char* path, const char* pattern) +{ + bool last_was_slash = false; + while ( true ) + { + if ( !*pattern ) + return !*path || last_was_slash; + if ( (last_was_slash = *pattern == '/') ) + { + if ( *path == '/' ) + { + path = SkipCharacters(path, '/'); + pattern = SkipCharacters(pattern, '/'); + continue; + } + return false; + } + if ( *pattern++ != *path++ ) + return false; + } +} + +InclusionRule::InclusionRule(const char* pattern, InclusionRuleType rule) +{ + this->pattern = strdup(pattern); + this->rule = rule; +} + +InclusionRule::~InclusionRule() +{ + free(pattern); +} + +bool InclusionRule::MatchesPath(const char* path) const +{ + return PathMatchesPattern(path, pattern); +} + +InclusionRules::InclusionRules() +{ + rules = NULL; + num_rules = num_rules_allocated = 0; + default_inclusion = true; +} + +InclusionRules::~InclusionRules() +{ + for ( size_t i = 0; i < num_rules; i++ ) + delete rules[i]; + delete[] rules; +} + +bool InclusionRules::IncludesPath(const char* path) const +{ + bool determined = false; + bool included = false; + for ( size_t i = 0; i < num_rules; i++ ) + { + InclusionRule* rule = rules[i]; + if ( !rule->MatchesPath(path) ) + continue; + switch ( rules[i]->rule ) + { + case RULE_INCLUDE: + included = true; + determined = true; + break; + case RULE_EXCLUDE: + included = false; + determined = true; + break; + } + } + if ( !determined ) + included = default_inclusion; + return included; +} + +bool InclusionRules::ChangeRulesAmount(size_t new_length) +{ + size_t new_num_rules = new_length < num_rules ? new_length : num_rules; + for ( size_t i = new_num_rules; i < num_rules; i++ ) + delete rules[i]; + num_rules = new_num_rules; + InclusionRule** new_rules = new InclusionRule*[new_length]; + for ( size_t i = 0; i < new_length && i < num_rules; i++ ) + new_rules[i] = rules[i]; + delete[] rules; rules = new_rules; + num_rules_allocated = new_length; + return true; +} + +bool InclusionRules::AddRule(InclusionRule* rule) +{ + if ( num_rules == num_rules_allocated ) + { + size_t new_length = num_rules_allocated ? 2 * num_rules_allocated : 32; + if ( !ChangeRulesAmount(new_length) ) + return false; + } + rules[num_rules++] = rule; + return true; +} + +static const char* SkipWhitespace(const char* line) +{ + while ( *line && isspace(*line) ) line++; + return line; +} + +static char* SkipWhitespace(char* line) +{ + return (char*) SkipWhitespace((const char*) line); +} + +static bool IsLineComment(const char* line) +{ + return !*line || *line == '#'; +} + +static const char* IsLineCommand(const char* line, const char* command) +{ + while ( *line && isspace(*line) ) line++; + size_t cmdlen = strlen(command); + if ( strncmp(line, command, cmdlen) != 0 ) + return NULL; + if ( line[cmdlen] && !isspace(line[cmdlen]) ) + return NULL; + while ( line[cmdlen] && isspace(line[cmdlen]) ) + cmdlen++; + return line + cmdlen; +} + +bool InclusionRules::AddRulesFromFile(FILE* fp, FILE* err, const char* fpname) +{ + size_t rules_at_start = num_rules; + size_t line_size; + size_t line_num = 0; + char* mem = NULL; + ssize_t line_len; + while ( 0 <= (line_len = getline(&mem, &line_size, fp)) ) + { + char* line = mem; + line_num++; + if ( line_len && line[line_len-1] == '\n' ) + line[line_len-1] = '\0'; + line = SkipWhitespace(line); + if ( IsLineComment(line) ) + continue; + const char* parameter; + if ( (parameter = IsLineCommand(line, "default")) ) + { + bool value; + if ( !strcmp(parameter, "true") ) + value = true; + else if ( !strcmp(parameter, "true") ) + value = false; + else + { + error_fp(err, 0, 0, "%s:%zu: not a boolean '%s'", fpname, + line_num, parameter); + goto error_out; + } + if ( !default_inclusion_determined ) + default_inclusion = value, + default_inclusion_determined = true; + else + default_inclusion = default_inclusion || value; + } + else if ( (parameter = IsLineCommand(line, "exclude")) || + (parameter = IsLineCommand(line, "include")) ) + { + if ( !*parameter ) + { + error_fp(err, 0, 0, "%s:%zu: no parameter given", fpname, + line_num); + goto error_out; + } + const char* pattern = parameter; + InclusionRuleType type = line[0] == 'e' ? RULE_EXCLUDE : RULE_INCLUDE; + InclusionRule* rule = new InclusionRule(pattern, type); + if ( !AddRule(rule) ) + goto error_out_errno; + } + else + { + error_fp(err, 0, 0, "%s:%zu: line not understood: '%s'", fpname, + line_num, line); + goto error_out; + } + } + free(mem); + if ( ferror(fp) ) + { + error_out_errno: + error_fp(err, 0, errno, "%s", fpname); + error_out: + ChangeRulesAmount(rules_at_start); + return false; + } + return true; +} diff --git a/mkinitrd/rules.h b/mkinitrd/rules.h new file mode 100644 index 00000000..b4ef0602 --- /dev/null +++ b/mkinitrd/rules.h @@ -0,0 +1,66 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along + with Sortix. If not, see . + + rules.h + Determines whether a given path is included in the filesystem. + +*******************************************************************************/ + +#ifndef RULES_H +#define RULES_H + +enum InclusionRuleType +{ + RULE_INCLUDE, + RULE_EXCLUDE, +}; + +class InclusionRule +{ +public: + InclusionRule(const char* pattern, InclusionRuleType rule); + ~InclusionRule(); + bool MatchesPath(const char* path) const; + +public: + char* pattern; + InclusionRuleType rule; + +}; + +class InclusionRules +{ +public: + InclusionRules(); + ~InclusionRules(); + bool IncludesPath(const char* path) const; + bool AddRule(InclusionRule* rule); + bool AddRulesFromFile(FILE* fp, FILE* err, const char* fpname); + bool ChangeRulesAmount(size_t newnum); + +public: + InclusionRule** rules; + size_t num_rules; + size_t num_rules_allocated; + bool default_inclusion; + bool default_inclusion_determined; + +}; + +#endif