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.
This commit is contained in:
Jonas 'Sortie' Termansen 2013-01-29 20:01:46 +01:00
parent 2bfaa81f76
commit 25a988442e
6 changed files with 405 additions and 26 deletions

View File

@ -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:

View File

@ -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

View File

@ -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)

View File

@ -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 <sortix/initrd.h>
#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 <ROOT> -o <DEST>\n", argv0);
fprintf(fp, "Usage: %s <ROOT> -o <DEST> [-f <PATH-FILTER>]\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); }

265
mkinitrd/rules.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
rules.cpp
Determines whether a given path is included in the filesystem.
*******************************************************************************/
#include <ctype.h>
#include <errno.h>
#include <error.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#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;
}

66
mkinitrd/rules.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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