diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 6670d0636a1..330d91d8240 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -1,6 +1,7 @@ #include "get-drvs.hh" #include "util.hh" #include "eval-inline.hh" +#include "globals.hh" namespace nix { @@ -11,7 +12,13 @@ string DrvInfo::queryDrvPath(EvalState & state) const if (drvPath == "" && attrs) { Bindings::iterator i = attrs->find(state.sDrvPath); PathSet context; - (string &) drvPath = i != attrs->end() ? state.coerceToPath(*i->value, context) : ""; + try { + (string &) drvPath = i != attrs->end() ? state.coerceToPath(*i->value, context) : ""; + } catch (ImportReadOnlyError & e) { + if (!recoverFromReadOnlyErrors) throw; + ((ErrorAttrs &) error).insert("drvPath"); + (string &) drvPath = ""; + } } return drvPath; } @@ -22,7 +29,13 @@ string DrvInfo::queryOutPath(EvalState & state) const if (outPath == "" && attrs) { Bindings::iterator i = attrs->find(state.sOutPath); PathSet context; - (string &) outPath = i != attrs->end() ? state.coerceToPath(*i->value, context) : ""; + try { + (string &) outPath = i != attrs->end() ? state.coerceToPath(*i->value, context) : ""; + } catch (ImportReadOnlyError & e) { + if (!recoverFromReadOnlyErrors) throw; + ((ErrorAttrs &) error).insert("drvPath"); + (string &) outPath = ""; + } } return outPath; } @@ -37,11 +50,23 @@ MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const Bindings::iterator a = attrs->find(state.sMeta); if (a == attrs->end()) return meta; /* fine, empty meta information */ - state.forceAttrs(*a->value); + try { + state.forceAttrs(*a->value); + } catch (ImportReadOnlyError & e) { + if (!recoverFromReadOnlyErrors) throw; + ((ErrorAttrs &) error).insert("meta"); + return meta; + } foreach (Bindings::iterator, i, *a->value->attrs) { MetaValue value; - state.forceValue(*i->value); + try { + state.forceAttrs(*a->value); + } catch (ImportReadOnlyError & e) { + if (!recoverFromReadOnlyErrors) throw; + ((ErrorAttrs &) metaError).insert(i->name); + continue; + } if (i->value->type == tString) { value.type = MetaValue::tpString; value.stringValue = i->value->string.s; @@ -69,6 +94,8 @@ MetaValue DrvInfo::queryMetaInfo(EvalState & state, const string & name) const void DrvInfo::setMetaInfo(const MetaInfo & meta) { + error.erase("meta"); + metaError.clear(); metaInfoRead = true; this->meta = meta; } @@ -96,17 +123,27 @@ static bool getDerivation(EvalState & state, Value & v, done.insert(v.attrs); DrvInfo drv; - + Bindings::iterator i = v.attrs->find(state.sName); /* !!! We really would like to have a decent back trace here. */ if (i == v.attrs->end()) throw TypeError("derivation name missing"); - drv.name = state.forceStringNoCtx(*i->value); + try { + drv.name = state.forceStringNoCtx(*i->value); + } catch (ImportReadOnlyError & e) { + if (!recoverFromReadOnlyErrors) throw; + drv.error.insert("name"); + } Bindings::iterator i2 = v.attrs->find(state.sSystem); if (i2 == v.attrs->end()) drv.system = "unknown"; else - drv.system = state.forceStringNoCtx(*i2->value); + try { + drv.system = state.forceStringNoCtx(*i2->value); + } catch (ImportReadOnlyError & e) { + if (!recoverFromReadOnlyErrors) throw; + drv.error.insert("system"); + } drv.attrs = v.attrs; @@ -117,6 +154,9 @@ static bool getDerivation(EvalState & state, Value & v, } catch (AssertionError & e) { return false; + } catch (ImportReadOnlyError & e) { + if (!recoverFromReadOnlyErrors) throw; + return false; } } @@ -179,6 +219,10 @@ static void getDerivations(EvalState & state, Value & vIn, attribute. */ if (v2.type == tAttrs) { Bindings::iterator j = v2.attrs->find(state.symbols.create("recurseForDerivations")); + /* !!! in the weird case where v2.recurseForDerivations + requires evaluating an import of an unrealised derivation + to be evaluated, this will throw in read-only + mode even if read-only recovery was specified */ if (j != v2.attrs->end() && state.forceBool(*j->value)) getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done); } diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh index 2d260c57bee..6fcfbc3805f 100644 --- a/src/libexpr/get-drvs.hh +++ b/src/libexpr/get-drvs.hh @@ -23,6 +23,7 @@ struct MetaValue typedef std::map MetaInfo; +typedef std::set ErrorAttrs; struct DrvInfo { @@ -37,6 +38,8 @@ public: string name; string attrPath; /* path towards the derivation */ string system; + ErrorAttrs error; + ErrorAttrs metaError; /* !!! make this private */ Bindings * attrs; @@ -50,11 +53,13 @@ public: void setDrvPath(const string & s) { + error.erase("drvPath"); drvPath = s; } void setOutPath(const string & s) { + error.erase("outPath"); outPath = s; } diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 6eb771a726b..f3811ed1484 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -17,6 +17,7 @@ MakeError(ThrownError, AssertionError) MakeError(Abort, EvalError) MakeError(TypeError, EvalError) MakeError(ImportError, EvalError) // error building an imported derivation +MakeError(ImportReadOnlyError, EvalError) // error when trying to import a derivation in read-only mode /* Position objects. */ diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 4a171a2aeb4..1589f365e3a 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -45,18 +45,35 @@ static void prim_import(EvalState & state, Value * * args, Value & v) foreach (PathSet::iterator, i, context) { Path ctx = decodeContext(*i).first; + string outputName = decodeContext(*i).second; assert(isStorePath(ctx)); - if (!store->isValidPath(ctx)) - throw EvalError(format("cannot import `%1%', since path `%2%' is not valid") - % path % ctx); - if (isDerivation(ctx)) - try { - /* !!! If using a substitute, we only need to fetch - the selected output of this derivation. */ - store->buildDerivations(singleton(ctx)); - } catch (Error & e) { - throw ImportError(e.msg()); + if (!store->isValidPath(ctx)) { + if (outputName.empty()) + throw EvalError(format("cannot import `%1%', since path `%2%' is not valid") + % path % ctx); + else + throw ImportReadOnlyError(format("cannot import `%1%', since path `%2%' cannot be written to the store in read-only mode") + % path % ctx); + } + if (isDerivation(ctx)) { + Derivation drv = derivationFromPath(*store, ctx); + + if (outputName.empty() || + !store->isValidPath(drv.outputs[outputName].path)) { + if (readOnlyMode) + foreach (DerivationOutputs::iterator, j, drv.outputs) + if (!store->isValidPath(j->second.path)) + throw ImportReadOnlyError(format("cannot import `%1%', since derivation `%2%' cannot be realised in read-only mode") + % path % ctx); + try { + /* !!! If using a substitute, we only need to fetch + the selected output of this derivation. */ + store->buildDerivations(singleton(ctx)); + } catch (Error & e) { + throw ImportError(e.msg()); + } } + } } state.evalFile(path, v); diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index 5c22f140664..b361ffd787c 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -26,6 +26,7 @@ Verbosity buildVerbosity = lvlError; unsigned int maxBuildJobs = 1; unsigned int buildCores = 1; bool readOnlyMode = false; +bool recoverFromReadOnlyErrors = false; string thisSystem = "unset"; time_t maxSilentTime = 0; time_t buildTimeout = 0; diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 12a9b9ca15c..745627dfc9e 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -64,6 +64,10 @@ extern unsigned int buildCores; database. */ extern bool readOnlyMode; +/* Whether failures due to read-only mode should cause a crash or allow + the tool to try to work around them. */ +extern bool recoverFromReadOnlyErrors; + /* The canonical system name, as returned by config.guess. */ extern string thisSystem; diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index a8d9076cfb8..eba8a748df6 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -169,7 +169,8 @@ static void loadDerivations(EvalState & state, Path nixExprPath, system. */ for (DrvInfos::iterator i = elems.begin(), j; i != elems.end(); i = j) { j = i; j++; - if (systemFilter != "*" && i->system != systemFilter) + if (systemFilter != "*" && i->error.find("system") == i->error.end() && + i->system != systemFilter) elems.erase(i); } } @@ -211,9 +212,12 @@ static int comparePriorities(EvalState & state, static bool isPrebuilt(EvalState & state, const DrvInfo & elem) { + string outpath = elem.queryOutPath(state); + if (elem.error.find("outPath") != elem.error.end()) + return false; return - store->isValidPath(elem.queryOutPath(state)) || - store->hasSubstitutes(elem.queryOutPath(state)); + store->isValidPath(outpath) || + store->hasSubstitutes(outpath); } @@ -759,6 +763,12 @@ static bool cmpChars(char a, char b) static bool cmpElemByName(const DrvInfo & a, const DrvInfo & b) { + if (a.error.find("name") != a.error.end()) + return false; + + if (b.error.find("name") != b.error.end()) + return true; + return lexicographical_compare( a.name.begin(), a.name.end(), b.name.begin(), b.name.end(), cmpChars); @@ -810,12 +820,17 @@ typedef enum { cvLess, cvEqual, cvGreater, cvUnavail } VersionDiff; static VersionDiff compareVersionAgainstSet( const DrvInfo & elem, const DrvInfos & elems, string & version) { - DrvName name(elem.name); - VersionDiff diff = cvUnavail; version = "?"; - + + if (elem.error.find("name") != elem.error.end()) + return diff; + + DrvName name(elem.name); + for (DrvInfos::const_iterator i = elems.begin(); i != elems.end(); ++i) { + if (i->error.find("name") != i->error.end()) + continue; DrvName name2(i->name); if (name.name == name2.name) { int d = compareVersions(name.version, name2.version); @@ -867,6 +882,7 @@ static void opQuery(Globals & globals, enum { sInstalled, sAvailable } source = sInstalled; readOnlyMode = true; /* makes evaluation a bit faster */ + recoverFromReadOnlyErrors = true; for (Strings::iterator i = args.begin(); i != args.end(); ) { string arg = *i++; @@ -949,18 +965,28 @@ static void opQuery(Globals & globals, XMLAttrs attrs; if (printStatus) { - bool hasSubs = store->hasSubstitutes(i->queryOutPath(globals.state)); - bool isInstalled = installed.find(i->queryOutPath(globals.state)) != installed.end(); - bool isValid = store->isValidPath(i->queryOutPath(globals.state)); - if (xmlOutput) { - attrs["installed"] = isInstalled ? "1" : "0"; - attrs["valid"] = isValid ? "1" : "0"; - attrs["substitutable"] = hasSubs ? "1" : "0"; - } else - columns.push_back( - (string) (isInstalled ? "I" : "-") - + (isValid ? "P" : "-") - + (hasSubs ? "S" : "-")); + string outpath = i->queryOutPath(globals.state); + if (i->error.find("outPath") == i->error.end()) { + bool hasSubs = store->hasSubstitutes(i->queryOutPath(globals.state)); + bool isInstalled = installed.find(i->queryOutPath(globals.state)) != installed.end(); + bool isValid = store->isValidPath(i->queryOutPath(globals.state)); + if (xmlOutput) { + attrs["installed"] = isInstalled ? "1" : "0"; + attrs["valid"] = isValid ? "1" : "0"; + attrs["substitutable"] = hasSubs ? "1" : "0"; + } else + columns.push_back( + (string) (isInstalled ? "I" : "-") + + (isValid ? "P" : "-") + + (hasSubs ? "S" : "-")); + } else { + if (xmlOutput) { + attrs["installed"] = "!"; + attrs["valid"] = "!"; + attrs["substitutable"] = "!"; + } else + columns.push_back("!!!"); + } } if (xmlOutput) @@ -968,10 +994,16 @@ static void opQuery(Globals & globals, else if (printAttrPath) columns.push_back(i->attrPath); - if (xmlOutput) - attrs["name"] = i->name; - else if (printName) - columns.push_back(i->name); + if (i->error.find("name") == i->error.end()) + if (xmlOutput) + attrs["name"] = i->name; + else if (printName) + columns.push_back(i->name); + else + if (xmlOutput) + attrs["name"] = "!name-unknown-in-readonly-mode!"; + else if (printName) + columns.push_back("!name-unknown-in-readonly-mode!"); if (compareVersions) { /* Compare this element against the versions of the @@ -1002,63 +1034,81 @@ static void opQuery(Globals & globals, } } - if (xmlOutput) { - if (i->system != "") attrs["system"] = i->system; - } - else if (printSystem) - columns.push_back(i->system); + if (i->error.find("system") == i->error.end()) + if (xmlOutput) { + if (i->system != "") attrs["system"] = i->system; + } + else if (printSystem) + columns.push_back(i->system); + else if (!xmlOutput && printSystem) + columns.push_back("!system-unknown-in-readonly-mode!"); if (printDrvPath) { string drvPath = i->queryDrvPath(globals.state); - if (xmlOutput) { - if (drvPath != "") attrs["drvPath"] = drvPath; - } else - columns.push_back(drvPath == "" ? "-" : drvPath); + if (i->error.find("drvPath") == i->error.end()) + if (xmlOutput) { + if (drvPath != "") attrs["drvPath"] = drvPath; + } else + columns.push_back(drvPath == "" ? "-" : drvPath); + else if (!xmlOutput) + columns.push_back("!drvPath-unknown-in-readonly-mode!"); } if (printOutPath) { string outPath = i->queryOutPath(globals.state); - if (xmlOutput) { - if (outPath != "") attrs["outPath"] = outPath; - } else - columns.push_back(outPath); + if (i->error.find("outPath") == i->error.end()) + if (xmlOutput) { + if (outPath != "") attrs["outPath"] = outPath; + } else + columns.push_back(outPath); + else if (!xmlOutput) + columns.push_back("!outPath-unknown-in-readonly-mode!"); } if (printDescription) { MetaInfo meta = i->queryMetaInfo(globals.state); - MetaValue value = meta["description"]; - string descr = value.type == MetaValue::tpString ? value.stringValue : ""; - if (xmlOutput) { - if (descr != "") attrs["description"] = descr; - } else - columns.push_back(descr); + if (i->error.find("meta") == i->error.end()) { + MetaValue value = meta["description"]; + if (i->metaError.find("description") == i->metaError.end()) { + string descr = value.type == MetaValue::tpString ? value.stringValue : ""; + if (xmlOutput) { + if (descr != "") attrs["description"] = descr; + } else + columns.push_back(descr); + } else if (!xmlOutput) + columns.push_back("!description-unknown-in-readonly-mode!"); + } else if (!xmlOutput) + columns.push_back("!meta-info-unknown-in-readonly-mode!"); } if (xmlOutput) if (printMeta) { - XMLOpenElement item(xml, "item", attrs); MetaInfo meta = i->queryMetaInfo(globals.state); - for (MetaInfo::iterator j = meta.begin(); j != meta.end(); ++j) { - XMLAttrs attrs2; - attrs2["name"] = j->first; - if (j->second.type == MetaValue::tpString) { - attrs2["type"] = "string"; - attrs2["value"] = j->second.stringValue; - xml.writeEmptyElement("meta", attrs2); - } else if (j->second.type == MetaValue::tpInt) { - attrs2["type"] = "int"; - attrs2["value"] = (format("%1%") % j->second.intValue).str(); - xml.writeEmptyElement("meta", attrs2); - } else if (j->second.type == MetaValue::tpStrings) { - attrs2["type"] = "strings"; - XMLOpenElement m(xml, "meta", attrs2); - foreach (Strings::iterator, k, j->second.stringValues) { - XMLAttrs attrs3; - attrs3["value"] = *k; - xml.writeEmptyElement("string", attrs3); - } + if (i->error.find("meta") == i->error.end()) { + XMLOpenElement item(xml, "item", attrs); + for (MetaInfo::iterator j = meta.begin(); j != meta.end(); ++j) { + XMLAttrs attrs2; + attrs2["name"] = j->first; + if (j->second.type == MetaValue::tpString) { + attrs2["type"] = "string"; + attrs2["value"] = j->second.stringValue; + xml.writeEmptyElement("meta", attrs2); + } else if (j->second.type == MetaValue::tpInt) { + attrs2["type"] = "int"; + attrs2["value"] = (format("%1%") % j->second.intValue).str(); + xml.writeEmptyElement("meta", attrs2); + } else if (j->second.type == MetaValue::tpStrings) { + attrs2["type"] = "strings"; + XMLOpenElement m(xml, "meta", attrs2); + foreach (Strings::iterator, k, j->second.stringValues) { + XMLAttrs attrs3; + attrs3["value"] = *k; + xml.writeEmptyElement("string", attrs3); + } + } } - } + } else + xml.writeEmptyElement("item", attrs); } else xml.writeEmptyElement("item", attrs);