diff --git a/src/libstore/build.cc b/src/libstore/build.cc index 70797656f..f721f5f49 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -860,7 +860,7 @@ void DerivationGoal::inputsRealised() void DerivationGoal::tryToBuild() { trace("trying to build"); - + try { /* Is the build hook willing to accept this job? */ @@ -1296,7 +1296,7 @@ bool DerivationGoal::prepareBuild() deletePathWrapped(path); } } - + /* Gather information necessary for computing the closure and/or running the build hook. */ @@ -1310,7 +1310,7 @@ bool DerivationGoal::prepareBuild() /* The state output is a referenceable path */ if(isStateDrv(drv)) allStatePaths.insert(drv.stateOutputs.find("state")->second.statepath); - + /* Determine the full set of input paths. */ /* First, the input derivations. */ @@ -1331,13 +1331,13 @@ bool DerivationGoal::prepareBuild() format("derivation `%1%' requires non-existent output `%2%' from input derivation `%3%'") % drvPath % *j % i->first); } - + /* Second, the input sources. */ for (PathSet::iterator i = drv.inputSrcs.begin(); i != drv.inputSrcs.end(); ++i){ computeFSClosure(*i, inputPaths, true, false, 0); //TODO !!!!!!!!!!!!!!!!!!!!!!!!!!! WE (MAY) ALSO NEED TO COPY STATE (done?) computeFSClosure(*i, inputStatePaths, false, true, 0); } - + debug(format("added input paths %1%") % showPaths(inputPaths)); debug(format("added input state paths %1%") % showPaths(inputStatePaths)); @@ -1829,18 +1829,11 @@ void DerivationGoal::computeClosure() //Shared state Path sharedState = drv.stateOutputs.find("state")->second.sharedState; if(sharedState != ""){ - //Remove state path - deletePathWrapped(statePath); - symlinkPath(sharedState, statePath); - - //Set in database - setSharedStateTxn(txn, sharedState, statePath); + shareStateTxn(txn, sharedState, statePath, false); } //If not shared: create the dir and set the rights else{ - ensureDirExists(statePath); - setChown(statePath, queryCallingUsername(), "nixbld"); - setChmod(statePath, "700"); + ensureStateDir(statePath, queryCallingUsername(), "nixbld", "700"); } } @@ -2598,7 +2591,7 @@ void LocalStore::buildDerivations(const PathSet & drvPaths) Worker worker; Goals goals; for (PathSet::const_iterator i = drvPaths.begin(); i != drvPaths.end(); ++i){ - //printMsg(lvlError, format("BUILD: '%1%'") % *i); + printMsg(lvlError, format("BUILD: '%1%'") % *i); goals.insert(worker.makeDerivationGoal(*i)); } diff --git a/src/libstore/db.cc b/src/libstore/db.cc index bb5eecf6e..90b7d31c7 100644 --- a/src/libstore/db.cc +++ b/src/libstore/db.cc @@ -447,331 +447,6 @@ void Database::enumTable(const Transaction & txn, TableId table, } catch (DbException e) { rethrow(e); } } -/* State specific db functions */ -Path Database::mergeToDBKey(const Path & statePath, const unsigned int intvalue) -{ - string prefix = "-KEY-"; - return statePath + prefix + unsignedInt2String(intvalue); -} - -void Database::splitDBKey(const Path & revisionedStatePath, Path & statePath, unsigned int & intvalue) -{ - string prefix = "-KEY-"; - - int pos = revisionedStatePath.find_last_of(prefix); - statePath = revisionedStatePath.substr(0, pos - prefix.length() + 1); - //printMsg(lvlError, format("'%2%' - '%1%'") % revisionedStatePath.substr(pos+1, revisionedStatePath.length()) % int2String(pos)); - bool succeed = string2UnsignedInt(revisionedStatePath.substr(pos+1, revisionedStatePath.length()), intvalue); - if(!succeed) - throw Error(format("Malformed revision value of path '%1%'") % revisionedStatePath); -} - -unsigned int Database::getNewRevisionNumber(const Transaction & txn, TableId table, - const Path & statePath) -{ - //query - string data; - bool notEmpty = queryString(txn, table, statePath, data); - - if(!notEmpty){ - setString(txn, table, statePath, int2String(1)); - return 1; //we begin counting from 1 since 0 is a special value representing the last revision - } - - unsigned int revision; - bool succeed = string2UnsignedInt(data, revision); - if(!succeed) - throw Error(format("Malformed revision counter value of path '%1%'") % statePath); - - revision++; - setString(txn, table, statePath, unsignedInt2String(revision)); - - return revision; -} - -bool Database::lookupHighestRevivison(const Strings & keys, const Path & statePath, string & key, unsigned int lowerthan) -{ - unsigned int highestRev = 0; - - //Lookup which key we need - for (Strings::const_iterator i = keys.begin(); i != keys.end(); ++i) { - - if((*i).substr(0, statePath.length()) != statePath || (*i).length() == statePath.length()) //dont check the new-revision key or other keys - continue; - - //printMsg(lvlError, format("'%1%' - '%2%'") % *i % statePath); - Path getStatePath; - unsigned int getRevision; - splitDBKey(*i, getStatePath, getRevision); - if(getRevision > highestRev){ - - if(lowerthan != 0){ - if(getRevision <= lowerthan) //if we have an uppper limit, see to it that we downt go over it - highestRev = getRevision; - } - else - highestRev = getRevision; - } - } - - if(highestRev == 0) //no records found - return false; - - key = mergeToDBKey(statePath, highestRev); //final key that matches revision + statePath - return true; -} - -bool Database::revisionToTimeStamp(const Transaction & txn, TableId revisions_table, const Path & statePath, const int revision, unsigned int & timestamp) -{ - string key = mergeToDBKey(statePath, revision); - Strings references; - bool notempty = queryStrings(txn, revisions_table, key, references); - - if(notempty){ - Path empty; - splitDBKey(*(references.begin()), empty, timestamp); //extract the timestamp - //printMsg(lvlError, format("PRINT '%1%'") % timestamp); - return true; - } - else - return false; -} - -void Database::setStateReferences(const Transaction & txn, TableId references_table, TableId revisions_table, - const Path & statePath, const Strings & references, const unsigned int revision, const unsigned int timestamp) -{ - //printMsg(lvlError, format("setStateReferences '%1%' for '%2%'") % references_table % statePath); - - //Find the timestamp if we need - unsigned int timestamp2 = timestamp; - if(revision == 0 && timestamp == 0) - timestamp2 = getTimeStamp(); - else if(revision != 0 && timestamp == 0){ - bool found = revisionToTimeStamp(txn, revisions_table, statePath, revision, timestamp2); - if(!found) - throw Error(format("Revision '%1%' cannot be matched to a timestamp...") % revision); - } - - //for (Strings::const_iterator i = references.begin(); i != references.end(); ++i) - // printMsg(lvlError, format("setStateReferences::: '%1%'") % *i); - - //Warning if it already exists - Strings empty; - if( queryStrings(txn, references_table, mergeToDBKey(statePath, timestamp2), empty) ) - printMsg(lvlError, format("Warning: The timestamp '%1%' (now: '%5%') / revision '%4%' already exists for set-references of path '%2%' with db '%3%'") - % timestamp2 % statePath % references_table % revision % getTimeStamp()); - - //Create the key - string key = mergeToDBKey(statePath, timestamp2); - - //printMsg(lvlError, format("Set references '%1%'") % key); - //for (Strings::const_iterator i = references.begin(); i != references.end(); ++i) - // printMsg(lvlError, format("reference '%1%'") % *i); - - //Insert - setStrings(txn, references_table, key, references, false); //The false makes sure also empty references are set -} - -bool Database::queryStateReferences(const Transaction & txn, TableId references_table, TableId revisions_table, - const Path & statePath, Strings & references, const unsigned int revision, const unsigned int timestamp) -{ - //printMsg(lvlError, format("queryStateReferences '%1%' with revision '%2%'") % references_table % revision); - - //Convert revision to timestamp number useing the revisions_table - unsigned int timestamp2 = timestamp; - if(timestamp == 0 && revision != 0){ - bool found = revisionToTimeStamp(txn, revisions_table, statePath, revision, timestamp2); - if(!found) //we are asked for references of some revision, but there are no references registered yet, so we return false; - return false; - } - - Strings keys; - enumTable(txn, references_table, keys); - - //Mabye we need the latest timestamp? - string key = ""; - if(timestamp2 == 0){ - bool foundsomething = lookupHighestRevivison(keys, statePath, key); - if(!foundsomething) - return false; - else - return queryStrings(txn, references_table, key, references); - } - - //If a specific key is given: check if this timestamp exists key in the table - key = mergeToDBKey(statePath, timestamp2); - bool found = false; - for (Strings::const_iterator i = keys.begin(); i != keys.end(); ++i) { - if(key == *i){ - found = true; - key = mergeToDBKey(statePath, timestamp2); - } - } - - //If it doesn't exist in the table then find the highest key lower than it - if(!found){ - bool foundsomething = lookupHighestRevivison(keys, statePath, key, 0); - if(!foundsomething) - return false; - //printMsg(lvlError, format("Warning: References for timestamp '%1%' not was not found, so taking the highest rev-key possible for statePath '%2%'") % timestamp2 % statePath); - } - - return queryStrings(txn, references_table, key, references); //now that we have the key, we can query the references -} - -void Database::setStateRevisions(const Transaction & txn, TableId revisions_table, TableId revisions_comments, - TableId snapshots_table, const RevisionClosure & revisions, const Path & rootStatePath, const string & comment) -{ - if( !isStatePath(rootStatePath) ) //weak check on statePath - throw Error(format("StatePath '%1%' is not a statepath") % rootStatePath); - - unsigned int timestamp = getTimeStamp(); - - //Insert all ss_epochs into snapshots_table with the current ts. - for (RevisionClosure::const_iterator i = revisions.begin(); i != revisions.end(); ++i){ - string key = mergeToDBKey((*i).first, timestamp); - Strings data; - //the map<> takes care of the sorting on the Path - for (Snapshots::const_iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) - data.push_back(int2String((*j).second)); - setStrings(txn, snapshots_table, key, data); - } - - //Insert for each statePath a new revision record linked to the ss_epochs - for (RevisionClosure::const_iterator i = revisions.begin(); i != revisions.end(); ++i){ - Path statePath = (*i).first; - - unsigned int revision = getNewRevisionNumber(txn, revisions_table, statePath); //get a new revision number - - string key = mergeToDBKey(statePath, revision); - - //get all its requisites - PathSet statePath_references; - storePathRequisitesTxn(txn, statePath, false, statePath_references, false, true, 0); - statePath_references.insert(statePath); - - //save in db - Strings data; - for (PathSet::const_iterator j = statePath_references.begin(); j != statePath_references.end(); ++j) - data.push_back(mergeToDBKey(*j, timestamp)); - - setStrings(txn, revisions_table, key, data, false); //The false makes sure also empty revisions are set - - //save the date and comments - Strings metadata; - metadata.push_back(unsignedInt2String(timestamp)); - - //get all paths that point to the same state (using shareing) and check if one of them equals the rootStatePath - PathSet sharedWith = getSharedWithPathSetRecTxn(txn, statePath); - if(statePath == rootStatePath || sharedWith.find(rootStatePath) != sharedWith.end()) - metadata.push_back(comment); - else - metadata.push_back("Part of the snashot closure for " + rootStatePath); - setStrings(txn, revisions_comments, key, metadata); - - } -} - -bool Database::queryStateRevisions(const Transaction & txn, TableId revisions_table, TableId snapshots_table, - const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const unsigned int root_revision) -{ - string key; - - if(root_revision == 0){ - Strings keys; - enumTable(txn, revisions_table, keys); //get all revisions - bool foundsomething = lookupHighestRevivison(keys, statePath, key); - if(!foundsomething) - return false; - } - else - key = mergeToDBKey(statePath, root_revision); - - //Get references pointing to snapshots_table from revisions_table with root_revision - Strings statePaths; - bool notempty = queryStrings(txn, revisions_table, key, statePaths); - - if(!notempty) - throw Error(format("Root revision '%1%' not found of statePath '%2%'") % unsignedInt2String(root_revision) % statePath); - - //For each statePath add the revisions - for (Strings::iterator i = statePaths.begin(); i != statePaths.end(); ++i){ - - Path getStatePath; - unsigned int getTimestamp; - splitDBKey(*i, getStatePath, getTimestamp); - - //query state versioined directorys/files - vector sortedPaths; - Derivation drv = derivationFromPathTxn(txn, queryStatePathDrvTxn(txn, getStatePath)); - DerivationStateOutputs stateOutputs = drv.stateOutputs; - DerivationStateOutputDirs stateOutputDirs = drv.stateOutputDirs; - for (DerivationStateOutputDirs::const_iterator j = stateOutputDirs.begin(); j != stateOutputDirs.end(); ++j){ - string thisdir = (j->second).path; - string fullstatedir = getStatePath + "/" + thisdir; - if(thisdir == "/") //exception for the root dir - fullstatedir = statePath + "/"; - sortedPaths.push_back(fullstatedir); - } - sort(sortedPaths.begin(), sortedPaths.end()); //sort - - Strings snapshots_s; - Snapshots snapshots; - queryStrings(txn, snapshots_table, *i, snapshots_s); - int counter=0; - for (Strings::iterator j = snapshots_s.begin(); j != snapshots_s.end(); ++j){ - - unsigned int revision; - bool succeed = string2UnsignedInt(*j, revision); - if(!succeed) - throw Error(format("Malformed epoch (snapshot timestamp) value of path '%1%'") % statePath); - - snapshots[sortedPaths.at(counter)] = revision; - counter++; - } - - revisions[getStatePath] = snapshots; - timestamps[getStatePath] = getTimestamp; - } - - return notempty; -} - -//TODO include comments into revisions? -bool Database::queryAvailableStateRevisions(const Transaction & txn, TableId revisions_table, TableId revisions_comments, - const Path & statePath, RevisionInfos & revisions) -{ - Strings keys; - enumTable(txn, revisions_table, keys); //get all revisions - - for (Strings::const_iterator i = keys.begin(); i != keys.end(); ++i) { - - if((*i).substr(0, statePath.length()) != statePath || (*i).length() == statePath.length()) //dont check the new-revision key or other keys - continue; - - Path getStatePath; - unsigned int getRevision; - splitDBKey(*i, getStatePath, getRevision); - - //save the date and comments - RevisionInfo rev; - Strings metadata; - queryStrings(txn, revisions_comments, *i, metadata); - unsigned int ts; - bool succeed = string2UnsignedInt(*(metadata.begin()), ts); - if(!succeed) - throw Error(format("Malformed timestamp in the revisions-comments table of path '%1%'") % *i); - rev.timestamp = ts; - metadata.pop_front(); - rev.comment = *(metadata.begin()); - revisions[getRevision] = rev; - } - - if(revisions.empty()) - return false; - else - return true; -} } diff --git a/src/libstore/db.hh b/src/libstore/db.hh index 8f7526810..496a07f90 100644 --- a/src/libstore/db.hh +++ b/src/libstore/db.hh @@ -60,11 +60,7 @@ private: void open2(const string & path, bool removeOldEnv); - /* TODO */ - bool lookupHighestRevivison(const Strings & keys, const Path & statePath, string & key, unsigned int lowerthan = 0); - /* TODO */ - unsigned int getNewRevisionNumber(const Transaction & txn, TableId table, const Path & statePath); public: Database(); @@ -96,37 +92,6 @@ public: void enumTable(const Transaction & txn, TableId table, Strings & keys, const string & keyPrefix = ""); - /* TODO */ - Path mergeToDBKey(const Path & statePath, const unsigned int intvalue); - - /* TODO */ - void splitDBKey(const Path & revisionedStatePath, Path & statePath, unsigned int & intvalue); - - /* TODO */ - bool revisionToTimeStamp(const Transaction & txn, TableId revisions_table, const Path & statePath, const int revision, unsigned int & timestamp); - - /* Set the stateReferences for a specific revision (and older until the next higher revision number in the table) */ - void setStateReferences(const Transaction & txn, TableId references_table, TableId revisions_table, - const Path & statePath, const Strings & references, const unsigned int revision = 0, const unsigned int timestamp = 0); - - /* Returns the references for a specific revision (and older until the next higher revision number in the table) */ - bool queryStateReferences(const Transaction & txn, TableId references_table, TableId revisions_table, - const Path & statePath, Strings & references, const unsigned int revision = 0, const unsigned int timestamp = 0); - - /* Set the revision number of the statePath and the revision numbers of all state paths in the references closure */ - void setStateRevisions(const Transaction & txn, TableId revisions_table, TableId revisions_comments, - TableId snapshots_table, const RevisionClosure & revisions, const Path & rootStatePath, const string & comment); - - /* Returns all the revision numbers of the state references closure of the given state path */ - bool queryStateRevisions(const Transaction & txn, TableId revisions_table, TableId snapshots_table, - const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const unsigned int root_revision = 0); - - /* Returns all available revision numbers of the given state path */ - bool queryAvailableStateRevisions(const Transaction & txn, TableId revisions_table, TableId revisions_comments, - const Path & statePath, RevisionInfos & revisions); - - - }; diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 9cb64cbab..75cd72db1 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -477,7 +477,6 @@ void setReferences(const Transaction & txn, const Path & store_or_statePath, nixDB.setStrings(txn, dbComponentComponentReferences, store_or_statePath, Paths(references.begin(), references.end())); nixDB.setStrings(txn, dbComponentStateReferences, store_or_statePath, Paths(stateReferences.begin(), stateReferences.end())); - } else if(isRealisableStatePath(txn, store_or_statePath)) { @@ -489,15 +488,15 @@ void setReferences(const Transaction & txn, const Path & store_or_statePath, //query the references of revision (0 is query the latest references) Paths oldStateReferences_s_c; Paths oldStateReferences_s_s; - nixDB.queryStateReferences(txn, dbStateComponentReferences, dbStateRevisions, store_or_statePath, oldStateReferences_s_c, revision); - nixDB.queryStateReferences(txn, dbStateStateReferences, dbStateRevisions, store_or_statePath, oldStateReferences_s_s, revision); + queryStateReferences(nixDB, txn, dbStateComponentReferences, dbStateRevisions, store_or_statePath, oldStateReferences_s_c, revision); + queryStateReferences(nixDB, txn, dbStateStateReferences, dbStateRevisions, store_or_statePath, oldStateReferences_s_s, revision); PathSet oldReferences = PathSet(oldStateReferences_s_c.begin(), oldStateReferences_s_c.end()); PathSet oldStateReferences = PathSet(oldStateReferences_s_s.begin(), oldStateReferences_s_s.end()); //set the references of revision (0 insert as a new timestamp) - nixDB.setStateReferences(txn, dbStateComponentReferences, dbStateRevisions, store_or_statePath, Paths(references.begin(), references.end()), revision); - nixDB.setStateReferences(txn, dbStateStateReferences, dbStateRevisions, store_or_statePath, Paths(stateReferences.begin(), stateReferences.end()), revision); + setStateReferences(nixDB, txn, dbStateComponentReferences, dbStateRevisions, store_or_statePath, Paths(references.begin(), references.end()), revision); + setStateReferences(nixDB, txn, dbStateStateReferences, dbStateRevisions, store_or_statePath, Paths(stateReferences.begin(), stateReferences.end()), revision); } else throw Error(format("Path '%1%' is not a valid component or state path") % store_or_statePath); @@ -516,13 +515,13 @@ void queryXReferencesTxn(const Transaction & txn, const Path & store_or_statePat else{ table1 = dbComponentStateReferences; table2 = dbStateStateReferences; - } + } if(isRealisablePath(txn, store_or_statePath)) nixDB.queryStrings(txn, table1, store_or_statePath, references2); else if(isRealisableStatePath(txn, store_or_statePath)){ Path statePath_ns = toNonSharedPathTxn(txn, store_or_statePath); //Lookup its where it points to if its shared - nixDB.queryStateReferences(txn, table2, dbStateRevisions, statePath_ns, references2, revision, timestamp); + queryStateReferences(nixDB, txn, table2, dbStateRevisions, statePath_ns, references2, revision, timestamp); } else throw Error(format("Path '%1%' is not a valid component or state path") % store_or_statePath); @@ -593,7 +592,7 @@ static PathSet getXReferrers(const Transaction & txn, const Path & store_or_stat //Now in references of ALL referrers, (possibly lookup their latest TS based on revision) unsigned int timestamp; if(revision != 0){ - bool succeed = nixDB.revisionToTimeStamp(txn, dbStateRevisions, path, revision, timestamp); + bool succeed = revisionToTimeStamp(nixDB, txn, dbStateRevisions, path, revision, timestamp); if(!succeed) throw Error(format("Getreferrers cannot find timestamp for revision: '%1%'") % revision); } @@ -604,7 +603,7 @@ static PathSet getXReferrers(const Transaction & txn, const Path & store_or_stat for (Strings::const_iterator i = keys.begin(); i != keys.end(); ++i){ Path getStatePath; unsigned int getRevision; - nixDB.splitDBKey(*i, getStatePath, getRevision); + splitDBKey(*i, getStatePath, getRevision); if(latest[getStatePath] == 0) //either it is unset latest[getStatePath] = getRevision; @@ -623,7 +622,7 @@ static PathSet getXReferrers(const Transaction & txn, const Path & store_or_stat //printMsg(lvlError, format("AAAAA '%1%'") % (*i).first); Strings references; - nixDB.queryStrings(txn, table, nixDB.mergeToDBKey((*i).first, (*i).second), references); + nixDB.queryStrings(txn, table, mergeToDBKey((*i).first, (*i).second), references); for (Strings::iterator j = references.begin(); j != references.end(); ++j){ //printMsg(lvlError, format("TEST: '%1%' has ref '%2%' check with '%3%'") % (*i).first % *j % path); if(*j == path) @@ -680,7 +679,7 @@ void setDeriver(const Transaction & txn, const Path & storePath, const Path & de if (!isRealisablePath(txn, storePath)) throw Error(format("path `%1%' is not valid") % storePath); - + if (isStateDrvPathTxn(txn, deriver)){ //Redirect if its a state component addStateDeriver(txn, storePath, deriver); } @@ -739,7 +738,7 @@ void addStateDeriver(const Transaction & txn, const Path & storePath, const Path string identifier = drv.stateOutputs.find("state")->second.stateIdentifier; string user = drv.stateOutputs.find("state")->second.username; - PathSet currentDerivers = queryDerivers(txn, storePath, identifier, user); + PathSet currentDerivers = queryDerivers(txn, storePath, identifier, user); PathSet updatedDerivers = mergeNewDerivationIntoListTxn(txn, storePath, deriver, currentDerivers, true); Strings data; @@ -1076,11 +1075,11 @@ void registerValidPaths(const Transaction & txn, const ValidPathInfos & infos) throw Error(format("cannot register path `%1%' as valid, since its reference `%2%' is invalid") % i->path % *j); //We cannot check the statePath since registerValidPath is called twice, first for the component path, and then for the state path.... - + if(isStorePath_b){ setDeriver(txn, i->path, i->deriver); } - + //TODO maybe also set a state deriver into dbStateDerivers .... well state is already linked to a drvpath in dbValidStatePaths .... } } @@ -1665,7 +1664,7 @@ void queryAllValidPathsTxn(const Transaction & txn, PathSet & allComponentPaths, void setStateRevisionsTxn(const Transaction & txn, const RevisionClosure & revisions, const Path & rootStatePath, const string & comment) { - nixDB.setStateRevisions(txn, dbStateRevisions, dbStateRevisionsComments, dbStateSnapshots, revisions, rootStatePath, comment); + setStateRevisions(nixDB, txn, dbStateRevisions, dbStateRevisionsComments, dbStateSnapshots, revisions, rootStatePath, comment); } void LocalStore::setStateRevisions(const RevisionClosure & revisions, const Path & rootStatePath, const string & comment) @@ -1675,7 +1674,7 @@ void LocalStore::setStateRevisions(const RevisionClosure & revisions, const Path bool queryStateRevisionsTxn(const Transaction & txn, const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const unsigned int revision) { - return nixDB.queryStateRevisions(txn, dbStateRevisions, dbStateSnapshots, statePath, revisions, timestamps, revision); + return queryStateRevisions(nixDB, txn, dbStateRevisions, dbStateSnapshots, statePath, revisions, timestamps, revision); } bool LocalStore::queryStateRevisions(const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const unsigned int revision) @@ -1685,7 +1684,7 @@ bool LocalStore::queryStateRevisions(const Path & statePath, RevisionClosure & r bool queryAvailableStateRevisionsTxn(const Transaction & txn, const Path & statePath, RevisionInfos & revisions) { - return nixDB.queryAvailableStateRevisions(txn, dbStateRevisions, dbStateRevisionsComments, statePath, revisions); + return queryAvailableStateRevisions(nixDB, txn, dbStateRevisions, dbStateRevisionsComments, statePath, revisions); } bool LocalStore::queryAvailableStateRevisions(const Path & statePath, RevisionInfos & revisions) @@ -1727,25 +1726,97 @@ bool querySolidStateReferencesTxn(const Transaction & txn, const Path & statePat return notempty; } -void setSharedStateTxn(const Transaction & txn, const Path & fromExisting, const Path & toNew) +void unShareStateTxn(const Transaction & txn, const Path & path, const bool copyFromOld) { - //TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! LEGALITY CHECK IF THE PATH MAY BE SHARED !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - //TODO - - //Remove earlier entries - nixDB.delPair(txn, dbSharedState, toNew); - - //Set new entry - nixDB.setString(txn, dbSharedState, toNew, fromExisting); + //Check if is statePath + if(!isValidStatePathTxn(txn, path)) + throw Error(format("Path `%1%' is not a valid state path") % path); + + //Check if path was shared... + Path sharedWithOldPath; + if(!querySharedStateTxn(txn, path, sharedWithOldPath)) + throw Error(format("Path `%1%' is not a shared so cannot be unshared") % path); + + //Remove Symlink + removeSymlink(path); + + //Touch dir with correct rights + Derivation drv = derivationFromPathTxn(txn, queryStatePathDrvTxn(txn, path)); + ensureStateDir(path, drv.stateOutputs.find("state")->second.username, "nixbld", "700"); + + //Copy if necessary (TODO first snapshot?) + if(copyFromOld){ + copyContents(sharedWithOldPath, path); + } + + //Remove earlier entries (unshare) + nixDB.delPair(txn, dbSharedState, path); } -void LocalStore::setSharedState(const Path & fromExisting, const Path & toNew) +void LocalStore::unShareState(const Path & path, const bool copyFromOld) { Transaction txn(nixDB); - setSharedStateTxn(txn, fromExisting, toNew); + unShareStateTxn(txn, path, copyFromOld); txn.commit(); } +/* FROM (NEW) --> TO (EXISTING) */ +void shareStateTxn(const Transaction & txn, const Path & from, const Path & to, const bool snapshot) +{ + //Check if is statePath + if(! (isValidStatePathTxn(txn, from) && isValidStatePathTxn(txn, to))) + throw Error(format("Path `%1%' or `%2%' is not a valid state path") % from % to); + + //Cant share with yourself + if(from == to) + throw Error(format("You cannot share with yourself `%1%'") % from); + + //Check for infinite recursion + //we do querySharedStateTxn until there the current path is not a shared path anymore + Path sharedPath; + Path returnedPath = to; + while(querySharedStateTxn(txn, returnedPath, sharedPath)){ + if(sharedPath == from) + throw Error(format("You cannot create an infinite sharing loop !! `%1%' is already shared with `%2%'") % sharedPath % from); + returnedPath = sharedPath; + } + + //Check if the user has the right to share this path + Derivation from_drv = derivationFromPathTxn(txn, queryStatePathDrvTxn(txn, from)); + Derivation to_drv = derivationFromPathTxn(txn, queryStatePathDrvTxn(txn, to)); + //TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + //Snapshot if necessary + if(snapshot){ + //Commit state (we only include our own state in the rivisionMapping) + RevisionClosure rivisionMapping; + rivisionMapping[from] = commitStatePathTxn(txn, from); + + //Save the new revision + setStateRevisionsTxn(txn, rivisionMapping, from, ("Before sharing revision with '" + to)+"'"); + } + + //If from was shared: unshare + Path empty; + if(querySharedStateTxn(txn, from, empty)) + unShareStateTxn(txn, from, false); + + //Remove state path and link + deletePathWrapped(from); + symlinkPath(to, from); //a link named 'from' pointing to existing dir 'to' + + //Set new entry + nixDB.setString(txn, dbSharedState, from, to); +} + +void LocalStore::shareState(const Path & from, const Path & to, const bool snapshot) +{ + Transaction txn(nixDB); + shareStateTxn(txn, from, to, snapshot); + txn.commit(); +} + + bool querySharedStateTxn(const Transaction & txn, const Path & statePath, Path & shared_with) { @@ -1758,9 +1829,11 @@ Path toNonSharedPathTxn(const Transaction & txn, const Path & statePath) Path sharedPath; Path returnedPath = statePath; - while(querySharedStateTxn(txn, returnedPath, sharedPath)) + while(querySharedStateTxn(txn, returnedPath, sharedPath)){ + //printMsg(lvlError, format("querySharedStateTxn '%1%' '%2%'") % returnedPath % sharedPath); returnedPath = sharedPath; - + } + return returnedPath; } @@ -1782,12 +1855,12 @@ PathSet LocalStore::toNonSharedPathSet(const PathSet & statePaths) void setStateComponentReferencesTxn(const Transaction & txn, const Path & statePath, const Strings & references, const unsigned int revision, const unsigned int timestamp) { - nixDB.setStateReferences(txn, dbStateComponentReferences, dbStateRevisions, statePath, references, revision, timestamp); + setStateReferences(nixDB, txn, dbStateComponentReferences, dbStateRevisions, statePath, references, revision, timestamp); } void setStateStateReferencesTxn(const Transaction & txn, const Path & statePath, const Strings & references, const unsigned int revision, const unsigned int timestamp) { - nixDB.setStateReferences(txn, dbStateStateReferences, dbStateRevisions, statePath, references, revision, timestamp); + setStateReferences(nixDB, txn, dbStateStateReferences, dbStateRevisions, statePath, references, revision, timestamp); } //Lookups which statePaths directy share (point to) statePath diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 41e46e022..eb300e942 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -114,7 +114,9 @@ public: void revertToRevision(const Path & statePath, const unsigned int revision_arg, const bool recursive); - void setSharedState(const Path & fromExisting, const Path & toNew); + void shareState(const Path & from, const Path & to, const bool snapshot); + + void unShareState(const Path & path, const bool copyFromOld); }; @@ -217,13 +219,14 @@ bool isStateDrvPathTxn(const Transaction & txn, const Path & drvPath); bool isStateDrv(const Derivation & drv); -//TODO can this ????? + +//TODO CHECK IF THESE DONT BELONG HERE, REFACTOR CODE, EG MOVE FUNCTIONS AROUND + void queryAllValidPathsTxn(const Transaction & txn, PathSet & allComponentPaths, PathSet & allStatePaths); bool isValidStatePathTxn(const Transaction & txn, const Path & path); void queryXReferencesTxn(const Transaction & txn, const Path & path, PathSet & references, const bool component_or_state, const unsigned int revision, const unsigned int timestamp = 0); -//TODO THESE DONT BELONG HERE, REFACTOR CODE, EG MOVE FUNCTIONS AROUND void setStateComponentReferencesTxn(const Transaction & txn, const Path & statePath, const Strings & references, const unsigned int revision, const unsigned int timestamp); void setStateStateReferencesTxn(const Transaction & txn, const Path & statePath, const Strings & references, const unsigned int revision, const unsigned int timestamp); @@ -240,7 +243,9 @@ bool isValidStatePathTxn(const Transaction & txn, const Path & path); void setSolidStateReferencesTxn(const Transaction & txn, const Path & statePath, const PathSet & paths); bool querySolidStateReferencesTxn(const Transaction & txn, const Path & statePath, PathSet & paths); -void setSharedStateTxn(const Transaction & txn, const Path & fromExisting, const Path & toNew); +void shareStateTxn(const Transaction & txn, const Path & from, const Path & to, const bool snapshot); +void unShareStateTxn(const Transaction & txn, const Path & path, const bool copyFromOld); + PathSet toNonSharedPathSetTxn(const Transaction & txn, const PathSet & statePaths); Path toNonSharedPathTxn(const Transaction & txn, const Path & statePath); @@ -254,6 +259,7 @@ IntVector getStatePathsIntervalTxn(const Transaction & txn, const PathSet & stat bool queryStateRevisionsTxn(const Transaction & txn, const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const unsigned int revision); void setStatePathsIntervalTxn(const Transaction & txn, const PathSet & statePath, const IntVector & intervals, bool allZero = false); +bool querySharedStateTxn(const Transaction & txn, const Path & statePath, Path & shared_with); } diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index 1fb4545c8..c2993e082 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -45,7 +45,7 @@ void computeFSClosureTxn(const Transaction & txn, const Path & path, PathSet & p if(!withComponents && !withState) throw Error(format("Useless call to computeFSClosure, at leat withComponents or withState must be true")); - //TODO MAYBE EDIT: HOW CAN THESE PATHS ALREADY BE VALID SOMETIMES ..... ????????????????????? + //TODO HOW CAN THESE PATHS ALREADY BE VALID SOMETIMES ..... ????????????????????? for (PathSet::iterator i = allPaths.begin(); i != allPaths.end(); ++i) if ( !isValidPathTxn(txn, *i) && !isValidStatePathTxn(txn, *i) ) throw Error(format("Not a state or store path: ") % *i); @@ -70,7 +70,8 @@ void computeFSClosureTxn(const Transaction & txn, const Path & path, PathSet & p void computeFSClosureRecTxn(const Transaction & txn, const Path & path, PathSet & paths, const int revision, const bool & flipDirection) { - if (paths.find(path) != paths.end()) return; //takes care of double entries + if (paths.find(path) != paths.end()) //takes care of double entries + return; paths.insert(path); diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index b5d3f31e3..6d6e24953 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -523,11 +523,21 @@ void RemoteStore::revertToRevision(const Path & statePath, const unsigned int re readInt(from); } -void RemoteStore::setSharedState(const Path & fromExisting, const Path & toNew) +void RemoteStore::shareState(const Path & from_arg, const Path & to_arg, const bool snapshot) { - writeInt(wopSetSharedState, to); - writeString(fromExisting, to); - writeString(toNew, to); + writeInt(wopShareState, to); + writeString(from_arg, to); + writeString(to_arg, to); + writeInt(snapshot ? 1 : 0, to); + processStderr(); + readInt(from); +} + +void RemoteStore::unShareState(const Path & path, const bool copyFromOld) +{ + writeInt(wopUnShareState, to); + writeString(path, to); + writeInt(copyFromOld ? 1 : 0, to); processStderr(); readInt(from); } diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh index 2e77360c2..a259f4fa0 100644 --- a/src/libstore/remote-store.hh +++ b/src/libstore/remote-store.hh @@ -100,7 +100,9 @@ public: void revertToRevision(const Path & statePath, const unsigned int revision_arg, const bool recursive); - void setSharedState(const Path & fromExisting, const Path & toNew); + void shareState(const Path & from, const Path & to, const bool snapshot); + + void unShareState(const Path & path, const bool copyFromOld); private: AutoCloseFD fdSocket; diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index ebe5d36bc..13fc7dc86 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -146,7 +146,7 @@ void checkStatePath(const Derivation & drv) //TODO Name check - + //if( user != callinguser Path calculatedPath = makeStatePath(componentHash, suffix, stateIdentifier); //TODO INCLUDE USER !!!!!!!!!!!! diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index fd325f26e..4a82c94a1 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -244,7 +244,10 @@ public: virtual void revertToRevision(const Path & statePath, const unsigned int revision_arg, const bool recursive) = 0; /* TODO */ - virtual void setSharedState(const Path & fromExisting, const Path & toNew) = 0; + virtual void shareState(const Path & from, const Path & to, const bool snapshot) = 0; + + /* TODO */ + virtual void unShareState(const Path & path, const bool copyFromOld) = 0; }; diff --git a/src/libstore/store-state.cc b/src/libstore/store-state.cc index 3be10e54e..fcde47495 100644 --- a/src/libstore/store-state.cc +++ b/src/libstore/store-state.cc @@ -17,6 +17,8 @@ #include "snapshot.hh" #include "references.hh" +//for nixDB + namespace nix { /* @@ -113,7 +115,7 @@ void revertToRevisionTxn(const Transaction & txn, const Path & statePath, const unsigned int timestamp = getTimestamps[statePath]; //get its derivation-state-items - Derivation statePath_drv = derivationFromPathTxn(txn, queryStatePathDrvTxn(noTxn, statePath)); + Derivation statePath_drv = derivationFromPathTxn(txn, queryStatePathDrvTxn(txn, statePath)); DerivationStateOutputDirs stateOutputDirs = statePath_drv.stateOutputDirs; //TODO Sort snapshots??? eg first restore root, then the subdirs?? @@ -377,4 +379,333 @@ void scanAndUpdateAllReferencesRecusivelyTxn(const Transaction & txn, const Path } } +// ******************************************************************* DB FUNCTIONS + +/* State specific db functions */ + +Path mergeToDBKey(const Path & statePath, const unsigned int intvalue) +{ + string prefix = "-KEY-"; + return statePath + prefix + unsignedInt2String(intvalue); +} + +void splitDBKey(const Path & revisionedStatePath, Path & statePath, unsigned int & intvalue) +{ + string prefix = "-KEY-"; + + int pos = revisionedStatePath.find_last_of(prefix); + statePath = revisionedStatePath.substr(0, pos - prefix.length() + 1); + //printMsg(lvlError, format("'%2%' - '%1%'") % revisionedStatePath.substr(pos+1, revisionedStatePath.length()) % int2String(pos)); + bool succeed = string2UnsignedInt(revisionedStatePath.substr(pos+1, revisionedStatePath.length()), intvalue); + if(!succeed) + throw Error(format("Malformed revision value of path '%1%'") % revisionedStatePath); +} + +unsigned int getNewRevisionNumber(Database & nixDB, const Transaction & txn, TableId table, + const Path & statePath) +{ + //query + string data; + bool notEmpty = nixDB.queryString(txn, table, statePath, data); + + if(!notEmpty){ + nixDB.setString(txn, table, statePath, int2String(1)); + return 1; //we begin counting from 1 since 0 is a special value representing the last revision + } + + unsigned int revision; + bool succeed = string2UnsignedInt(data, revision); + if(!succeed) + throw Error(format("Malformed revision counter value of path '%1%'") % statePath); + + revision++; + nixDB.setString(txn, table, statePath, unsignedInt2String(revision)); + + return revision; +} + +bool lookupHighestRevivison(const Strings & keys, const Path & statePath, string & key, unsigned int lowerthan) +{ + unsigned int highestRev = 0; + + //Lookup which key we need + for (Strings::const_iterator i = keys.begin(); i != keys.end(); ++i) { + + if((*i).substr(0, statePath.length()) != statePath || (*i).length() == statePath.length()) //dont check the new-revision key or other keys + continue; + + //printMsg(lvlError, format("'%1%' - '%2%'") % *i % statePath); + Path getStatePath; + unsigned int getRevision; + splitDBKey(*i, getStatePath, getRevision); + if(getRevision > highestRev){ + + if(lowerthan != 0){ + if(getRevision <= lowerthan) //if we have an uppper limit, see to it that we downt go over it + highestRev = getRevision; + } + else + highestRev = getRevision; + } + } + + if(highestRev == 0) //no records found + return false; + + key = mergeToDBKey(statePath, highestRev); //final key that matches revision + statePath + return true; +} + +bool revisionToTimeStamp(Database & nixDB, const Transaction & txn, TableId revisions_table, const Path & statePath, const int revision, unsigned int & timestamp) +{ + string key = mergeToDBKey(statePath, revision); + Strings references; + bool notempty = nixDB.queryStrings(txn, revisions_table, key, references); + + if(notempty){ + Path empty; + splitDBKey(*(references.begin()), empty, timestamp); //extract the timestamp + //printMsg(lvlError, format("PRINT '%1%'") % timestamp); + return true; + } + else + return false; +} + +void setStateReferences(Database & nixDB, const Transaction & txn, TableId references_table, TableId revisions_table, + const Path & statePath, const Strings & references, const unsigned int revision, const unsigned int timestamp) +{ + //printMsg(lvlError, format("setStateReferences '%1%' for '%2%'") % references_table % statePath); + + //Find the timestamp if we need + unsigned int timestamp2 = timestamp; + if(revision == 0 && timestamp == 0) + timestamp2 = getTimeStamp(); + else if(revision != 0 && timestamp == 0){ + bool found = revisionToTimeStamp(nixDB, txn, revisions_table, statePath, revision, timestamp2); + if(!found) + throw Error(format("Revision '%1%' cannot be matched to a timestamp...") % revision); + } + + //for (Strings::const_iterator i = references.begin(); i != references.end(); ++i) + // printMsg(lvlError, format("setStateReferences::: '%1%'") % *i); + + //Warning if it already exists + Strings empty; + if( nixDB.queryStrings(txn, references_table, mergeToDBKey(statePath, timestamp2), empty) ) + printMsg(lvlError, format("Warning: The timestamp '%1%' (now: '%5%') / revision '%4%' already exists for set-references of path '%2%' with db '%3%'") + % timestamp2 % statePath % references_table % revision % getTimeStamp()); + + //Create the key + string key = mergeToDBKey(statePath, timestamp2); + + //printMsg(lvlError, format("Set references '%1%'") % key); + //for (Strings::const_iterator i = references.begin(); i != references.end(); ++i) + // printMsg(lvlError, format("reference '%1%'") % *i); + + //Insert + nixDB.setStrings(txn, references_table, key, references, false); //The false makes sure also empty references are set +} + +bool queryStateReferences(Database & nixDB, const Transaction & txn, TableId references_table, TableId revisions_table, + const Path & statePath, Strings & references, const unsigned int revision, const unsigned int timestamp) +{ + //printMsg(lvlError, format("queryStateReferences '%1%' with revision '%2%'") % references_table % revision); + + //Convert revision to timestamp number useing the revisions_table + unsigned int timestamp2 = timestamp; + if(timestamp == 0 && revision != 0){ + bool found = revisionToTimeStamp(nixDB, txn, revisions_table, statePath, revision, timestamp2); + if(!found) //we are asked for references of some revision, but there are no references registered yet, so we return false; + return false; + } + + Strings keys; + nixDB.enumTable(txn, references_table, keys); + + //Mabye we need the latest timestamp? + string key = ""; + if(timestamp2 == 0){ + bool foundsomething = lookupHighestRevivison(keys, statePath, key); + if(!foundsomething) + return false; + else + return nixDB.queryStrings(txn, references_table, key, references); + } + + //If a specific key is given: check if this timestamp exists key in the table + key = mergeToDBKey(statePath, timestamp2); + bool found = false; + for (Strings::const_iterator i = keys.begin(); i != keys.end(); ++i) { + if(key == *i){ + found = true; + key = mergeToDBKey(statePath, timestamp2); + } + } + + //If it doesn't exist in the table then find the highest key lower than it + if(!found){ + bool foundsomething = lookupHighestRevivison(keys, statePath, key, 0); + if(!foundsomething) + return false; + //printMsg(lvlError, format("Warning: References for timestamp '%1%' not was not found, so taking the highest rev-key possible for statePath '%2%'") % timestamp2 % statePath); + } + + return nixDB.queryStrings(txn, references_table, key, references); //now that we have the key, we can query the references +} + +void setStateRevisions(Database & nixDB, const Transaction & txn, TableId revisions_table, TableId revisions_comments, + TableId snapshots_table, const RevisionClosure & revisions, const Path & rootStatePath, const string & comment) +{ + if( !isStatePath(rootStatePath) ) //weak check on statePath + throw Error(format("StatePath '%1%' is not a statepath") % rootStatePath); + + unsigned int timestamp = getTimeStamp(); + + //Insert all ss_epochs into snapshots_table with the current ts. + for (RevisionClosure::const_iterator i = revisions.begin(); i != revisions.end(); ++i){ + string key = mergeToDBKey((*i).first, timestamp); + Strings data; + //the map<> takes care of the sorting on the Path + for (Snapshots::const_iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) + data.push_back(int2String((*j).second)); + nixDB.setStrings(txn, snapshots_table, key, data); + } + + //Insert for each statePath a new revision record linked to the ss_epochs + for (RevisionClosure::const_iterator i = revisions.begin(); i != revisions.end(); ++i){ + Path statePath = (*i).first; + + unsigned int revision = getNewRevisionNumber(nixDB, txn, revisions_table, statePath); //get a new revision number + + string key = mergeToDBKey(statePath, revision); + + //get all its requisites + PathSet statePath_references; + storePathRequisitesTxn(txn, statePath, false, statePath_references, false, true, 0); + statePath_references.insert(statePath); + + //save in db + Strings data; + for (PathSet::const_iterator j = statePath_references.begin(); j != statePath_references.end(); ++j) + data.push_back(mergeToDBKey(*j, timestamp)); + + nixDB.setStrings(txn, revisions_table, key, data, false); //The false makes sure also empty revisions are set + + //save the date and comments + Strings metadata; + metadata.push_back(unsignedInt2String(timestamp)); + + //get all paths that point to the same state (using shareing) and check if one of them equals the rootStatePath + PathSet sharedWith = getSharedWithPathSetRecTxn(txn, statePath); + if(statePath == rootStatePath || sharedWith.find(rootStatePath) != sharedWith.end()) + metadata.push_back(comment); + else + metadata.push_back("Part of the snashot closure for " + rootStatePath); + nixDB.setStrings(txn, revisions_comments, key, metadata); + + } +} + +bool queryStateRevisions(Database & nixDB, const Transaction & txn, TableId revisions_table, TableId snapshots_table, + const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const unsigned int root_revision) +{ + string key; + + if(root_revision == 0){ + Strings keys; + nixDB.enumTable(txn, revisions_table, keys); //get all revisions + bool foundsomething = lookupHighestRevivison(keys, statePath, key); + if(!foundsomething) + return false; + } + else + key = mergeToDBKey(statePath, root_revision); + + //Get references pointing to snapshots_table from revisions_table with root_revision + Strings statePaths; + bool notempty = nixDB.queryStrings(txn, revisions_table, key, statePaths); + + if(!notempty) + throw Error(format("Root revision '%1%' not found of statePath '%2%'") % unsignedInt2String(root_revision) % statePath); + + //For each statePath add the revisions + for (Strings::iterator i = statePaths.begin(); i != statePaths.end(); ++i){ + + Path getStatePath; + unsigned int getTimestamp; + splitDBKey(*i, getStatePath, getTimestamp); + + //query state versioined directorys/files + vector sortedPaths; + Derivation drv = derivationFromPathTxn(txn, queryStatePathDrvTxn(txn, getStatePath)); + DerivationStateOutputs stateOutputs = drv.stateOutputs; + DerivationStateOutputDirs stateOutputDirs = drv.stateOutputDirs; + for (DerivationStateOutputDirs::const_iterator j = stateOutputDirs.begin(); j != stateOutputDirs.end(); ++j){ + string thisdir = (j->second).path; + string fullstatedir = getStatePath + "/" + thisdir; + if(thisdir == "/") //exception for the root dir + fullstatedir = statePath + "/"; + sortedPaths.push_back(fullstatedir); + } + sort(sortedPaths.begin(), sortedPaths.end()); //sort + + Strings snapshots_s; + Snapshots snapshots; + nixDB.queryStrings(txn, snapshots_table, *i, snapshots_s); + int counter=0; + for (Strings::iterator j = snapshots_s.begin(); j != snapshots_s.end(); ++j){ + + unsigned int revision; + bool succeed = string2UnsignedInt(*j, revision); + if(!succeed) + throw Error(format("Malformed epoch (snapshot timestamp) value of path '%1%'") % statePath); + + snapshots[sortedPaths.at(counter)] = revision; + counter++; + } + + revisions[getStatePath] = snapshots; + timestamps[getStatePath] = getTimestamp; + } + + return notempty; +} + +//TODO include comments into revisions? +bool queryAvailableStateRevisions(Database & nixDB, const Transaction & txn, TableId revisions_table, TableId revisions_comments, + const Path & statePath, RevisionInfos & revisions) +{ + Strings keys; + nixDB.enumTable(txn, revisions_table, keys); //get all revisions + + for (Strings::const_iterator i = keys.begin(); i != keys.end(); ++i) { + + if((*i).substr(0, statePath.length()) != statePath || (*i).length() == statePath.length()) //dont check the new-revision key or other keys + continue; + + Path getStatePath; + unsigned int getRevision; + splitDBKey(*i, getStatePath, getRevision); + + //save the date and comments + RevisionInfo rev; + Strings metadata; + nixDB.queryStrings(txn, revisions_comments, *i, metadata); + unsigned int ts; + bool succeed = string2UnsignedInt(*(metadata.begin()), ts); + if(!succeed) + throw Error(format("Malformed timestamp in the revisions-comments table of path '%1%'") % *i); + rev.timestamp = ts; + metadata.pop_front(); + rev.comment = *(metadata.begin()); + revisions[getRevision] = rev; + } + + if(revisions.empty()) + return false; + else + return true; +} + } diff --git a/src/libstore/store-state.hh b/src/libstore/store-state.hh index 3ef6fcb32..eaabd4e23 100644 --- a/src/libstore/store-state.hh +++ b/src/libstore/store-state.hh @@ -7,25 +7,65 @@ namespace nix { -/* Create a state directory. */ -void createSubStateDirsTxn(const Transaction & txn, const DerivationStateOutputDirs & stateOutputDirs, const DerivationStateOutputs & stateOutputs); + /* Create a state directory. */ + void createSubStateDirsTxn(const Transaction & txn, const DerivationStateOutputDirs & stateOutputDirs, const DerivationStateOutputs & stateOutputs); + + /* TODO */ + Snapshots commitStatePathTxn(const Transaction & txn, const Path & statePath); + + /* TODO */ + //void updateRevisionsRecursivelyTxn(const Transaction & txn, const Path & statePath); + + /* TODO */ + //int readRevisionNumber(Path statePath); + + + void scanAndUpdateAllReferencesTxn(const Transaction & txn, const Path & statePath + , PathSet & newFoundComponentReferences, PathSet & newFoundStateReferences); + + void scanAndUpdateAllReferencesRecusivelyTxn(const Transaction & txn, const Path & statePath); + + void revertToRevisionTxn(const Transaction & txn, const Path & statePath, const int revision_arg, const bool recursive); -/* TODO */ -Snapshots commitStatePathTxn(const Transaction & txn, const Path & statePath); + + // **************************************** ******************************************* + + + /* TODO */ + bool lookupHighestRevivison(const Strings & keys, const Path & statePath, string & key, unsigned int lowerthan = 0); + + /* TODO */ + unsigned int getNewRevisionNumber(Database & nixDB, const Transaction & txn, TableId table, const Path & statePath); -/* TODO */ -//void updateRevisionsRecursivelyTxn(const Transaction & txn, const Path & statePath); + /* TODO */ + Path mergeToDBKey(const Path & statePath, const unsigned int intvalue); + + /* TODO */ + void splitDBKey(const Path & revisionedStatePath, Path & statePath, unsigned int & intvalue); -/* TODO */ -//int readRevisionNumber(Path statePath); + /* TODO */ + bool revisionToTimeStamp(Database & nixDB, const Transaction & txn, TableId revisions_table, const Path & statePath, const int revision, unsigned int & timestamp); + + /* Set the stateReferences for a specific revision (and older until the next higher revision number in the table) */ + void setStateReferences(Database & nixDB, const Transaction & txn, TableId references_table, TableId revisions_table, + const Path & statePath, const Strings & references, const unsigned int revision = 0, const unsigned int timestamp = 0); + + /* Returns the references for a specific revision (and older until the next higher revision number in the table) */ + bool queryStateReferences(Database & nixDB, const Transaction & txn, TableId references_table, TableId revisions_table, + const Path & statePath, Strings & references, const unsigned int revision = 0, const unsigned int timestamp = 0); + /* Set the revision number of the statePath and the revision numbers of all state paths in the references closure */ + void setStateRevisions(Database & nixDB, const Transaction & txn, TableId revisions_table, TableId revisions_comments, + TableId snapshots_table, const RevisionClosure & revisions, const Path & rootStatePath, const string & comment); + + /* Returns all the revision numbers of the state references closure of the given state path */ + bool queryStateRevisions(Database & nixDB, const Transaction & txn, TableId revisions_table, TableId snapshots_table, + const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const unsigned int root_revision = 0); + + /* Returns all available revision numbers of the given state path */ + bool queryAvailableStateRevisions(Database & nixDB, const Transaction & txn, TableId revisions_table, TableId revisions_comments, + const Path & statePath, RevisionInfos & revisions); -void scanAndUpdateAllReferencesTxn(const Transaction & txn, const Path & statePath - , PathSet & newFoundComponentReferences, PathSet & newFoundStateReferences); - -void scanAndUpdateAllReferencesRecusivelyTxn(const Transaction & txn, const Path & statePath); - -void revertToRevisionTxn(const Transaction & txn, const Path & statePath, const int revision_arg, const bool recursive); } diff --git a/src/libstore/worker-protocol.hh b/src/libstore/worker-protocol.hh index 4499bc15c..8775e9604 100644 --- a/src/libstore/worker-protocol.hh +++ b/src/libstore/worker-protocol.hh @@ -46,7 +46,8 @@ typedef enum { wopScanAndUpdateAllReferences, wopToNonSharedPathSet, wopRevertToRevision, - wopSetSharedState, + wopShareState, + wopUnShareState, } WorkerOp; diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 5579b338d..9d64f6412 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -1281,24 +1281,45 @@ string padd(const string & s, char c , unsigned int size, bool front) return ss; } -void symlinkPath(const Path & fromExisting, const Path & toNew) +void symlinkPath(const Path & existingDir, const Path & newLinkName) { //Symlink link to the share path //Usage: ln [OPTION]... [-T] TARGET LINK_NAME (1st form) Strings p_args; p_args.push_back("-sf"); - p_args.push_back(fromExisting); - p_args.push_back(toNew); + p_args.push_back(existingDir); + p_args.push_back(newLinkName); runProgram_AndPrintOutput("ln", true, p_args, "ln"); - //printMsg(lvlError, format("ln -sf %1% %2%") % fromExisting % toNew); + //printMsg(lvlError, format("ln -sf %1% %2%") % existingDir % newLinkName); +} + +void removeSymlink(const string & path) +{ + if(path[path.length() - 1] != '/') + throw Error(format("We dont want to remove the enitre directory, only the symlink, but a / is given for `%1%'") % path); + deletePath(path); +} + +void ensureStateDir(const Path & statePath, const string & user, const string & group, const string & chmod) +{ + ensureDirExists(statePath); + setChown(statePath, user, group); + setChmod(statePath, chmod); } void copyContents(const Path & from, const Path & to) { + //Copy all files + dirs recursively Strings p_args; p_args.push_back("-R"); - p_args.push_back(from + "/"); //the / makes sure it copys the contents of the dir, not just the symlink + p_args.push_back(from + "/*"); + p_args.push_back(to); + runProgram_AndPrintOutput("cp", true, p_args, "cp"); + + //Also copy the hidden files (but not the ../ dir) + p_args.clear(); + p_args.push_back(from + "/.[a-zA-Z0-9]*"); p_args.push_back(to); runProgram_AndPrintOutput("cp", true, p_args, "cp"); } diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 63cd37b5d..18eefae55 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -330,7 +330,11 @@ void setChmod(const Path & pathOrFile, const string & chmod); string padd(const string & s, char c , unsigned int size, bool front = false); /* Symlinks one path to the other */ -void symlinkPath(const Path & fromExisting, const Path & toNew); +void symlinkPath(const Path & existingDir, const Path & newLinkName); + +void removeSymlink(const string & path); + +void ensureStateDir(const Path & statePath, const string & user, const string & group, const string & chmod); /* Copy all files and folders recursively (also the hidden ones) from the dir from/... to the dir to/... */ void copyContents(const Path & from, const Path & to); diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index 0be385bbe..ce7909dbd 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -609,25 +609,20 @@ static void installDerivations(Globals & globals, return; } - printMsg(lvlError, format("DONE!!!!!!!!")); - createUserEnv(globals.state, allElems, profile, globals.keepDerivations); - printMsg(lvlError, format("DONE!!!!!!!!")); - //After all components have been built succesfully, share their state paths with the old ones for (StringPairs::iterator i = toBeShared.begin(); i != toBeShared.end(); ++i){ printMsg(lvlError, format("Sharing state from old <-- new component '%1%' <-- '%2%'") % i->first % i->second); - deletePath(i->second); //Remove contents of current new state path - symlinkPath(i->first, i->second); //Share new statepath to the old statepath - - //Set in database - store->setSharedState(i->first, i->second); + //Share from new --> to existing + store->shareState(i->second, i->first, false); } + + //********************** //Let the stateDirs in /nix/state point to the solidStateDependencies for (StringPairs::iterator i = externalStates.begin(); i != externalStates.end(); ++i){ @@ -668,6 +663,8 @@ static void installDerivations(Globals & globals, symlinkPath(statePath, externalState); } + //********************** + } diff --git a/src/nix-state/nix-state.cc b/src/nix-state/nix-state.cc index 387231dd3..1432eb034 100644 --- a/src/nix-state/nix-state.cc +++ b/src/nix-state/nix-state.cc @@ -196,7 +196,7 @@ static void queryAvailableStateRevisions(Strings opFlags, Strings opArgs) Derivation drv = getDerivation_andCheckArgs(opFlags, opArgs, componentPath, statePath, binary, derivationPath, isStateComponent, program_args); } - //Unshare if neccacary + //Lookup unshared path if neccacary PathSet nonSharedPaths; nonSharedPaths.insert(statePath); Path nonSharedStatePath = *((store->toNonSharedPathSet(nonSharedPaths)).begin()); //TODO CHECK IF THIS WORKS !!!!!!!! @@ -505,11 +505,15 @@ void run(Strings args) PathSet comparePaths_result = store->toNonSharedPathSet(comparePaths); for (PathSet::const_iterator j = comparePaths_result.begin(); j != comparePaths_result.end(); ++j) printMsg(lvlError, format("RES '%1%'") % *j); + + deletePath("/nix/state/b"); + copyContents("/nix/state/a", "/nix/state/b"); return; */ + /* test */ for (Strings::iterator i = args.begin(); i != args.end(); ) { diff --git a/src/nix-worker/nix-worker.cc b/src/nix-worker/nix-worker.cc index 7e88c871f..335a911d5 100644 --- a/src/nix-worker/nix-worker.cc +++ b/src/nix-worker/nix-worker.cc @@ -591,11 +591,22 @@ static void performOp(Source & from, Sink & to, unsigned int op) break; } - case wopSetSharedState: { - Path fromExisting = readString(from); - Path toNew = readString(from); + case wopShareState: { + Path from_arg = readString(from); + Path to_arg = readString(from); + bool snapshot = readInt(from) == 1; startWork(); - store->setSharedState(fromExisting, toNew); + store->shareState(from_arg, to_arg, snapshot); + stopWork(); + writeInt(1, to); + break; + } + + case wopUnShareState: { + Path path = readString(from); + bool copyFromOld = readInt(from) == 1; + startWork(); + store->unShareState(path, copyFromOld); stopWork(); writeInt(1, to); break;