Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: NixOS/nix
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: f134fc4cbecc
Choose a base ref
...
head repository: NixOS/nix
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: edbb105e98b1
Choose a head ref
  • 5 commits
  • 4 files changed
  • 2 contributors

Commits on May 15, 2017

  1. nar-accessor: use tree, fixes readDirectory missing children

    Previously, if a directory `foo` existed and a file `foo-` (where `-` is any character that is sorted before `/`), then  `readDirectory` would return an empty list.
    
    To fix this, we now use a tree where we can just access the children of the node, and do not need to rely on sorting behavior to list the contents of a directory.
    bennofs committed May 15, 2017
    Copy the full SHA
    75a1d98 View commit details
  2. Copy the full SHA
    4412f7c View commit details
  3. nar-accessor: non-recursive NarMember::find

    This avoids a possible stack overflow if directories are very deeply nested.
    bennofs committed May 15, 2017
    Copy the full SHA
    5ee06e6 View commit details
  4. Copy the full SHA
    a1f428b View commit details

Commits on May 24, 2017

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    edbb105 View commit details
Showing with 123 additions and 34 deletions.
  1. +75 −33 src/libstore/nar-accessor.cc
  2. +2 −1 tests/local.mk
  3. +23 −0 tests/nar-index.nix
  4. +23 −0 tests/nar-index.sh
108 changes: 75 additions & 33 deletions src/libstore/nar-accessor.cc
Original file line number Diff line number Diff line change
@@ -2,6 +2,8 @@
#include "archive.hh"

#include <map>
#include <stack>
#include <algorithm>

namespace nix {

@@ -16,45 +18,62 @@ struct NarMember
size_t start, size;

std::string target;

/* If this is a directory, all the children of the directory. */
std::map<std::string, NarMember> children;
};

struct NarIndexer : ParseSink, StringSource
{
// FIXME: should store this as a tree. Now we're vulnerable to
// O(nm) memory consumption (e.g. for x_0/.../x_n/{y_0..y_m}).
typedef std::map<Path, NarMember> Members;
Members members;
NarMember root;
std::stack<NarMember*> parents;

Path currentPath;
std::string currentStart;
bool isExec = false;

NarIndexer(const std::string & nar) : StringSource(nar)
{
}

void createMember(const Path & path, NarMember member) {
size_t level = std::count(path.begin(), path.end(), '/');
while(parents.size() > level) {
parents.pop();
}

if(parents.empty()) {
root = std::move(member);
parents.push(&root);
} else {
if(parents.top()->type != FSAccessor::Type::tDirectory) {
throw Error(format("NAR file missing parent directory of path ‘%1%’") % path);
}
auto result = parents.top()->children.emplace(baseNameOf(path), std::move(member));
parents.push(&result.first->second);
}
}

void createDirectory(const Path & path) override
{
members.emplace(path,
NarMember{FSAccessor::Type::tDirectory, false, 0, 0});
createMember(path, {FSAccessor::Type::tDirectory, false, 0, 0 });
}

void createRegularFile(const Path & path) override
{
currentPath = path;
createMember(path, {FSAccessor::Type::tRegular, false, 0, 0 });
}

void isExecutable() override
{
isExec = true;
parents.top()->isExecutable = true;
}

void preallocateContents(unsigned long long size) override
{
currentStart = string(s, pos, 16);
assert(size <= std::numeric_limits<size_t>::max());
members.emplace(currentPath,
NarMember{FSAccessor::Type::tRegular, isExec, pos, (size_t) size});
parents.top()->size = (size_t)size;
parents.top()->start = pos;
}

void receiveContents(unsigned char * data, unsigned int len) override
@@ -68,16 +87,42 @@ struct NarIndexer : ParseSink, StringSource

void createSymlink(const Path & path, const string & target) override
{
members.emplace(path,
createMember(path,
NarMember{FSAccessor::Type::tSymlink, false, 0, 0, target});
}

Members::iterator find(const Path & path)
NarMember* find(const Path & path)
{
auto i = members.find(path);
if (i == members.end())
Path canon = path == "" ? "" : canonPath(path);
NarMember* current = &root;
auto end = path.end();
for(auto it = path.begin(); it != end; ) {
// because it != end, the remaining component is non-empty so we need
// a directory
if(current->type != FSAccessor::Type::tDirectory) return nullptr;

// skip slash (canonPath above ensures that this is always a slash)
assert(*it == '/');
it += 1;

// lookup current component
auto next = std::find(it, end, '/');
auto child = current->children.find(std::string(it, next));
if(child == current->children.end()) return nullptr;
current = &child->second;

it = next;
}

return current;
}

NarMember& at(const Path & path) {
auto result = find(path);
if(result == nullptr) {
throw Error(format("NAR file does not contain path ‘%1%’") % path);
return i;
}
return *result;
}
};

@@ -93,44 +138,41 @@ struct NarAccessor : public FSAccessor

Stat stat(const Path & path) override
{
auto i = indexer.members.find(path);
if (i == indexer.members.end())
auto i = indexer.find(path);
if (i == nullptr)
return {FSAccessor::Type::tMissing, 0, false};
return {i->second.type, i->second.size, i->second.isExecutable};
return {i->type, i->size, i->isExecutable};
}

StringSet readDirectory(const Path & path) override
{
auto i = indexer.find(path);
auto i = indexer.at(path);

if (i->second.type != FSAccessor::Type::tDirectory)
if (i.type != FSAccessor::Type::tDirectory)
throw Error(format("path ‘%1%’ inside NAR file is not a directory") % path);

++i;
StringSet res;
while (i != indexer.members.end() && isInDir(i->first, path)) {
// FIXME: really bad performance.
if (i->first.find('/', path.size() + 1) == std::string::npos)
res.insert(std::string(i->first, path.size() + 1));
++i;
for(auto&& child : i.children) {
res.insert(child.first);

}
return res;
}

std::string readFile(const Path & path) override
{
auto i = indexer.find(path);
if (i->second.type != FSAccessor::Type::tRegular)
auto i = indexer.at(path);
if (i.type != FSAccessor::Type::tRegular)
throw Error(format("path ‘%1%’ inside NAR file is not a regular file") % path);
return std::string(*nar, i->second.start, i->second.size);
return std::string(*nar, i.start, i.size);
}

std::string readLink(const Path & path) override
{
auto i = indexer.find(path);
if (i->second.type != FSAccessor::Type::tSymlink)
auto i = indexer.at(path);
if (i.type != FSAccessor::Type::tSymlink)
throw Error(format("path ‘%1%’ inside NAR file is not a symlink") % path);
return i->second.target;
return i.target;
}
};

3 changes: 2 additions & 1 deletion tests/local.mk
Original file line number Diff line number Diff line change
@@ -13,7 +13,8 @@ nix_tests = \
check-reqs.sh pass-as-file.sh tarball.sh restricted.sh \
placeholders.sh nix-shell.sh \
linux-sandbox.sh \
build-remote.sh
build-remote.sh \
nar-index.sh
# parallel.sh

install-tests += $(foreach x, $(nix_tests), tests/$(x))
23 changes: 23 additions & 0 deletions tests/nar-index.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
with import ./config.nix;

rec {
a = mkDerivation {
name = "nar-index-a";
builder = builtins.toFile "builder.sh"
''
mkdir $out
mkdir $out/foo
touch $out/foo-x
touch $out/foo/bar
touch $out/foo/baz
touch $out/qux
mkdir $out/zyx
cat >$out/foo/data <<EOF
lasjdöaxnasd
asdom 12398
ä"§Æẞ¢«»”alsd
EOF
'';
};
}
23 changes: 23 additions & 0 deletions tests/nar-index.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
source common.sh

echo "building test path"
storePath="$(nix-build nar-index.nix -A a --no-out-link)"

cd "$TEST_ROOT"

echo "dumping path to nar"
narFile="$TEST_ROOT/path.nar"
nix-store --dump $storePath > $narFile

echo "check that find and ls-nar match"
( cd $storePath; find . | sort ) > files.find
nix ls-nar -R -d $narFile "" | sort > files.ls-nar
diff -u files.find files.ls-nar

echo "check that file contents of data match"
nix cat-nar $narFile /foo/data > data.cat-nar
diff -u data.cat-nar $storePath/foo/data

echo "check that file contents of baz match"
nix cat-nar $narFile /foo/baz > baz.cat-nar
diff -u baz.cat-nar $storePath/foo/baz