diff options
-rw-r--r-- | hscript/disk.cc | 106 | ||||
-rw-r--r-- | hscript/disk.hh | 25 | ||||
-rw-r--r-- | hscript/script.cc | 25 | ||||
-rw-r--r-- | tests/fixtures/0076-diskid-basic.installfile | 2 | ||||
-rw-r--r-- | tests/fixtures/0122-disklabel-apm.installfile | 7 | ||||
-rw-r--r-- | tests/fixtures/0123-disklabel-mbr.installfile | 7 | ||||
-rw-r--r-- | tests/fixtures/0124-disklabel-gpt.installfile | 7 | ||||
-rw-r--r-- | tests/fixtures/0125-disklabel-duplicate.installfile | 8 | ||||
-rw-r--r-- | tests/fixtures/0126-disklabel-loop0.installfile | 6 | ||||
-rw-r--r-- | tests/fixtures/0127-disklabel-enoent.installfile | 6 | ||||
-rw-r--r-- | tests/fixtures/0128-disklabel-invalid-type.installfile | 7 | ||||
-rw-r--r-- | tests/fixtures/0129-disklabel-without-label.installfile | 7 | ||||
-rw-r--r-- | tests/spec/validator_spec.rb | 48 |
13 files changed, 242 insertions, 19 deletions
diff --git a/hscript/disk.cc b/hscript/disk.cc index 137d3cf..875d84f 100644 --- a/hscript/disk.cc +++ b/hscript/disk.cc @@ -28,6 +28,34 @@ using namespace Horizon::Keys; + +#ifdef HAS_INSTALL_ENV +/*! Determine if _block is a valid block device. + * @param key The key associated with this test. + * @param line The line number where the key exists. + * @param _block The path to test. + * @returns true if _block is valid, false otherwise. + * @note Will output_error if an error occurs. + */ +bool is_block_device(const std::string &key, int line, const std::string &_block) { + struct stat blk_stat; + const char *block_c = _block.c_str(); + if(access(block_c, F_OK) != 0 || stat(block_c, &blk_stat) != 0) { + output_error("installfile:" + std::to_string(line), + key + ": error opening device " + _block, + strerror(errno)); + return false; + } + if(!S_ISBLK(blk_stat.st_mode)) { + output_error("installfile:" + std::to_string(line), + key + ": " + _block + " is not a valid block device"); + return false; + } + return true; +} +#endif /* HAS_INSTALL_ENV */ + + Key *DiskId::parseFromData(const std::string &data, int lineno, int *errors, int *warnings) { std::string block, ident; @@ -46,26 +74,15 @@ Key *DiskId::parseFromData(const std::string &data, int lineno, int *errors, } bool DiskId::validate(ScriptOptions options) const { - /* We only validate if running in an Installation Environment. */ - if(!options.test(InstallEnvironment)) return true; - #ifdef HAS_INSTALL_ENV - /* Unlike 'mount', 'diskid' *does* require that the block device exist - * before installation begins. This test is always valid. */ - struct stat blk_stat; - const char *block_c = _block.c_str(); - if(access(block_c, F_OK) != 0 || stat(block_c, &blk_stat) != 0) { - output_error("installfile:" + std::to_string(line), - "diskid: error opening device " + _block, - strerror(errno)); - return false; - } - if(!S_ISBLK(blk_stat.st_mode)) { - output_error("installfile:" + std::to_string(line), - "diskid: " + _block + " is not a valid block device"); - return false; + /* We only validate if running in an Installation Environment. */ + if(options.test(InstallEnvironment)) { + /* Unlike 'mount', 'diskid' *does* require that the block device exist + * before installation begins. This test is always valid. */ + return is_block_device("diskid", this->lineno(), _block); } #endif /* HAS_INSTALL_ENV */ + return true; } @@ -117,6 +134,61 @@ bool DiskId::execute(ScriptOptions options) const { return match; } + +Key *DiskLabel::parseFromData(const std::string &data, int lineno, int *errors, + int *warnings) { + std::string block, label; + std::string::size_type sep = data.find_first_of(' '); + LabelType type; + + /* REQ: Runner.Validate.disklabel.Validity */ + if(sep == std::string::npos || data.length() == sep + 1) { + if(errors) *errors += 1; + output_error("installfile:" + std::to_string(lineno), + "disklabel: expected a label type", + "valid format for disklabel is: [disk] [type]"); + return nullptr; + } + + block = data.substr(0, sep); + label = data.substr(sep + 1); + std::transform(label.begin(), label.end(), label.begin(), ::tolower); + /* REQ: Runner.Validate.disklabel.LabelType */ + if(label == "apm") { + type = APM; + } else if(label == "mbr") { + type = MBR; + } else if(label == "gpt") { + type = GPT; + } else { + if(errors) *errors += 1; + output_error("installfile:" + std::to_string(lineno), + "disklabel: '" + label + "' is not a valid label type", + "valid label types are: apm, mbr, gpt"); + return nullptr; + } + + return new DiskLabel(lineno, block, type); +} + +bool DiskLabel::validate(ScriptOptions options) const { +#ifdef HAS_INSTALL_ENV + /* REQ: Runner.Validate.disklabel.Block */ + if(options.test(InstallEnvironment)) { + /* disklabels are created before any others, so we can check now */ + return is_block_device("disklabel", this->lineno(), _block); + } +#endif /* HAS_INSTALL_ENV */ + + return true; +} + +bool DiskLabel::execute(ScriptOptions) const { + /* TODO XXX NOTIMPLEMENTED */ + return false; +} + + Key *Mount::parseFromData(const std::string &data, int lineno, int *errors, int *warnings) { std::string dev, where, opt; diff --git a/hscript/disk.hh b/hscript/disk.hh index 02ecb98..f8a9574 100644 --- a/hscript/disk.hh +++ b/hscript/disk.hh @@ -37,6 +37,31 @@ public: }; class DiskLabel : public Key { +public: + /*! The type of disklabel. */ + enum LabelType { + /*! Apple Partition Map (APM) */ + APM, + /*! Master Boot Record (MBR) */ + MBR, + /*! GUID Partition Table (GPT) */ + GPT + }; +private: + const std::string _block; + const LabelType _type; + + DiskLabel(int _line, const std::string &_b, const LabelType &_t) : + Key(_line), _block(_b), _type(_t) {} +public: + /*! Retrieve the block device that this key identifies. */ + const std::string device() const { return this->_block; } + /*! Retrieve the type of disklabel for the block device. */ + const LabelType type() const { return this->_type; } + + static Key *parseFromData(const std::string &, int, int*, int*); + bool validate(ScriptOptions) const override; + bool execute(ScriptOptions) const override; }; class Partition : public Key { diff --git a/hscript/script.cc b/hscript/script.cc index 785e40e..ee638bb 100644 --- a/hscript/script.cc +++ b/hscript/script.cc @@ -103,6 +103,8 @@ struct Script::ScriptPrivate { /*! Disk identification keys */ std::vector< std::unique_ptr<DiskId> > diskids; + /*! Disklabel configuration keys */ + std::vector< std::unique_ptr<DiskLabel> > disklabels; /*! Target system's mountpoints. */ std::vector< std::unique_ptr<Mount> > mounts; @@ -157,6 +159,10 @@ struct Script::ScriptPrivate { std::unique_ptr<DiskId> diskid(dynamic_cast<DiskId *>(obj)); this->diskids.push_back(std::move(diskid)); return true; + } else if(key_name == "disklabel") { + std::unique_ptr<DiskLabel> l(dynamic_cast<DiskLabel *>(obj)); + this->disklabels.push_back(std::move(l)); + return true; } else if(key_name == "mount") { std::unique_ptr<Mount> mount(dynamic_cast<Mount *>(obj)); this->mounts.push_back(std::move(mount)); @@ -496,7 +502,7 @@ const Script *Script::load(std::istream &sstream, bool Script::validate() const { int failures = 0; - std::set<std::string> seen_diskids, seen_mounts; + std::set<std::string> seen_diskids, seen_labels, seen_mounts; std::map<const std::string, int> seen_iface; /* REQ: Runner.Validate.network */ @@ -698,6 +704,23 @@ bool Script::validate() const { seen_diskids.insert(diskid->device()); } + /* REQ: Runner.Validate.disklabel */ + for(auto &label : this->internal->disklabels) { + if(!label->validate(this->opts)) { + failures++; + continue; + } + + /* REQ: Runner.Validate.disklabel.Unique */ + if(seen_labels.find(label->device()) != seen_labels.end()) { + failures++; + output_error("installfile:" + std::to_string(label->lineno()), + "disklabel: device " + label->device() + + " already has a label queued"); + } + seen_labels.insert(label->device()); + } + /* REQ: Runner.Validate.mount */ for(auto &mount : this->internal->mounts) { if(!mount->validate(this->opts)) { diff --git a/tests/fixtures/0076-diskid-basic.installfile b/tests/fixtures/0076-diskid-basic.installfile index 329e70c..611428c 100644 --- a/tests/fixtures/0076-diskid-basic.installfile +++ b/tests/fixtures/0076-diskid-basic.installfile @@ -3,4 +3,4 @@ hostname test.machine pkginstall adelie-base rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/ mount /dev/sda1 / -diskid /dev/sda SEAGATE +diskid /dev/sda ST1000 diff --git a/tests/fixtures/0122-disklabel-apm.installfile b/tests/fixtures/0122-disklabel-apm.installfile new file mode 100644 index 0000000..8acb90b --- /dev/null +++ b/tests/fixtures/0122-disklabel-apm.installfile @@ -0,0 +1,7 @@ +network false +hostname test.machine +pkginstall adelie-base +rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/ +mount /dev/sda1 / +diskid /dev/sda ST1000 +disklabel /dev/sda apm diff --git a/tests/fixtures/0123-disklabel-mbr.installfile b/tests/fixtures/0123-disklabel-mbr.installfile new file mode 100644 index 0000000..f865628 --- /dev/null +++ b/tests/fixtures/0123-disklabel-mbr.installfile @@ -0,0 +1,7 @@ +network false +hostname test.machine +pkginstall adelie-base +rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/ +mount /dev/sda1 / +diskid /dev/sda ST1000 +disklabel /dev/sda mbr diff --git a/tests/fixtures/0124-disklabel-gpt.installfile b/tests/fixtures/0124-disklabel-gpt.installfile new file mode 100644 index 0000000..d793970 --- /dev/null +++ b/tests/fixtures/0124-disklabel-gpt.installfile @@ -0,0 +1,7 @@ +network false +hostname test.machine +pkginstall adelie-base +rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/ +mount /dev/sda1 / +diskid /dev/sda ST1000 +disklabel /dev/sda gpt diff --git a/tests/fixtures/0125-disklabel-duplicate.installfile b/tests/fixtures/0125-disklabel-duplicate.installfile new file mode 100644 index 0000000..2a2b7e2 --- /dev/null +++ b/tests/fixtures/0125-disklabel-duplicate.installfile @@ -0,0 +1,8 @@ +network false +hostname test.machine +pkginstall adelie-base +rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/ +mount /dev/sda1 / +diskid /dev/sda ST1000 +disklabel /dev/sda apm +disklabel /dev/sda gpt diff --git a/tests/fixtures/0126-disklabel-loop0.installfile b/tests/fixtures/0126-disklabel-loop0.installfile new file mode 100644 index 0000000..cead2fd --- /dev/null +++ b/tests/fixtures/0126-disklabel-loop0.installfile @@ -0,0 +1,6 @@ +network false +hostname test.machine +pkginstall adelie-base +rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/ +mount /dev/sda1 / +disklabel /dev/loop0 apm diff --git a/tests/fixtures/0127-disklabel-enoent.installfile b/tests/fixtures/0127-disklabel-enoent.installfile new file mode 100644 index 0000000..9a5835c --- /dev/null +++ b/tests/fixtures/0127-disklabel-enoent.installfile @@ -0,0 +1,6 @@ +network false +hostname test.machine +pkginstall adelie-base +rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/ +mount /dev/sda1 / +disklabel /tmp gpt diff --git a/tests/fixtures/0128-disklabel-invalid-type.installfile b/tests/fixtures/0128-disklabel-invalid-type.installfile new file mode 100644 index 0000000..b4c14b2 --- /dev/null +++ b/tests/fixtures/0128-disklabel-invalid-type.installfile @@ -0,0 +1,7 @@ +network false +hostname test.machine +pkginstall adelie-base +rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/ +mount /dev/sda1 / +diskid /dev/sda ST1000 +disklabel /dev/sda sun diff --git a/tests/fixtures/0129-disklabel-without-label.installfile b/tests/fixtures/0129-disklabel-without-label.installfile new file mode 100644 index 0000000..5c6455c --- /dev/null +++ b/tests/fixtures/0129-disklabel-without-label.installfile @@ -0,0 +1,7 @@ +network false +hostname test.machine +pkginstall adelie-base +rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/ +mount /dev/sda1 / +diskid /dev/sda ST1000 +disklabel /dev/sda diff --git a/tests/spec/validator_spec.rb b/tests/spec/validator_spec.rb index 3ed43ea..624eaa2 100644 --- a/tests/spec/validator_spec.rb +++ b/tests/spec/validator_spec.rb @@ -548,6 +548,54 @@ RSpec.describe 'HorizonScript validation', :type => :aruba do expect(last_command_started).to have_output(/error: .*diskid.*already/) end end + context "for 'disklabel' key" do + it "requires a label type" do + use_fixture '0129-disklabel-without-label.installfile' + run_validate + expect(last_command_started).to have_output(/error: .*disklabel.*expected/) + end + it "succeeds with a APM disk label" do + use_fixture '0122-disklabel-apm.installfile' + run_validate + expect(last_command_started).to have_output(PARSER_SUCCESS) + expect(last_command_started).to have_output(VALIDATOR_SUCCESS) + end + it "succeeds with a MBR disk label" do + use_fixture '0123-disklabel-mbr.installfile' + run_validate + expect(last_command_started).to have_output(PARSER_SUCCESS) + expect(last_command_started).to have_output(VALIDATOR_SUCCESS) + end + it "succeeds with a GPT disk label" do + use_fixture '0124-disklabel-gpt.installfile' + run_validate + expect(last_command_started).to have_output(PARSER_SUCCESS) + expect(last_command_started).to have_output(VALIDATOR_SUCCESS) + end + it "fails with a duplicate disk" do + use_fixture '0125-disklabel-duplicate.installfile' + run_validate + expect(last_command_started).to have_output(/error: .*disklabel.*already/) + end + it "succeeds with present disk" do + use_fixture '0126-disklabel-loop0.installfile' + run_validate ' -i' + skip "This build does not support this test" if last_command_started.stdout =~ /runtime environment only/ + expect(last_command_started).to have_output(PARSER_SUCCESS) + expect(last_command_started).to have_output(VALIDATOR_SUCCESS) + end + it "fails with non-present disk" do + use_fixture '0127-disklabel-enoent.installfile' + run_validate ' -i' + skip "This build does not support this test" if last_command_started.stdout =~ /runtime environment only/ + expect(last_command_started).to have_output(/error: .*disklabel.*block/) + end + it "requires a valid label type" do + use_fixture '0128-disklabel-invalid-type.installfile' + run_validate + expect(last_command_started).to have_output(/error: .*disklabel.*type/) + end + end end context "unique keys" do # Runner.Validate.network. |