aboutsummaryrefslogtreecommitdiff
path: root/src/libstore/export-import.cc
blob: 6090ee3e9f8319b9baee5376a89db25d36c57149 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#include "store-api.hh"
#include "archive.hh"
#include "worker-protocol.hh"

#include <algorithm>

namespace nix {

struct HashAndWriteSink : Sink
{
    Sink & writeSink;
    HashSink hashSink;
    HashAndWriteSink(Sink & writeSink) : writeSink(writeSink), hashSink(htSHA256)
    {
    }
    virtual void operator () (const unsigned char * data, size_t len)
    {
        writeSink(data, len);
        hashSink(data, len);
    }
    Hash currentHash()
    {
        return hashSink.currentHash().first;
    }
};

void Store::exportPaths(const Paths & paths, Sink & sink)
{
    Paths sorted = topoSortPaths(PathSet(paths.begin(), paths.end()));
    std::reverse(sorted.begin(), sorted.end());

    std::string doneLabel("paths exported");
    logger->incExpected(doneLabel, sorted.size());

    for (auto & path : sorted) {
        Activity act(*logger, lvlInfo, format("exporting path ‘%s’") % path);
        sink << 1;
        exportPath(path, sink);
        logger->incProgress(doneLabel);
    }

    sink << 0;
}

void Store::exportPath(const Path & path, Sink & sink)
{
    auto info = queryPathInfo(path);

    HashAndWriteSink hashAndWriteSink(sink);

    narFromPath(path, hashAndWriteSink);

    /* Refuse to export paths that have changed.  This prevents
       filesystem corruption from spreading to other machines.
       Don't complain if the stored hash is zero (unknown). */
    Hash hash = hashAndWriteSink.currentHash();
    if (hash != info->narHash && info->narHash != Hash(info->narHash.type))
        throw Error(format("hash of path ‘%1%’ has changed from ‘%2%’ to ‘%3%’!") % path
            % printHash(info->narHash) % printHash(hash));

    hashAndWriteSink << exportMagic << path << info->references << info->deriver << 0;
}

struct TeeSource : Source
{
    Source & readSource;
    ref<std::string> data;
    TeeSource(Source & readSource)
        : readSource(readSource)
        , data(make_ref<std::string>())
    {
    }
    size_t read(unsigned char * data, size_t len)
    {
        size_t n = readSource.read(data, len);
        this->data->append((char *) data, n);
        return n;
    }
};

struct NopSink : ParseSink
{
};

Paths Store::importPaths(Source & source, std::shared_ptr<FSAccessor> accessor, bool dontCheckSigs)
{
    Paths res;
    while (true) {
        unsigned long long n = readLongLong(source);
        if (n == 0) break;
        if (n != 1) throw Error("input doesn't look like something created by ‘nix-store --export’");

        /* Extract the NAR from the source. */
        TeeSource tee(source);
        NopSink sink;
        parseDump(sink, tee);

        uint32_t magic = readInt(source);
        if (magic != exportMagic)
            throw Error("Nix archive cannot be imported; wrong format");

        ValidPathInfo info;

        info.path = readStorePath(*this, source);

        Activity act(*logger, lvlInfo, format("importing path ‘%s’") % info.path);

        info.references = readStorePaths<PathSet>(*this, source);

        info.deriver = readString(source);
        if (info.deriver != "") assertStorePath(info.deriver);

        info.narHash = hashString(htSHA256, *tee.data);
        info.narSize = tee.data->size();

        // Ignore optional legacy signature.
        if (readInt(source) == 1)
            readString(source);

        addToStore(info, *tee.data, false, dontCheckSigs);

        // FIXME: implement accessors?
        assert(!accessor);
#if 0
        auto accessor_ = std::dynamic_pointer_cast<BinaryCacheStoreAccessor>(accessor);
        if (accessor_)
            accessor_->nars.emplace(info.path, makeNarAccessor(tee.data));
#endif

        res.push_back(info.path);
    }

    return res;
}

}