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
137
138
139
140
#pragma once
///@file

#include "lix/libstore/store-api.hh"
#include "lix/libutil/types.hh"


namespace nix {


/**
 * Garbage-collector roots, referring to a store path
 */
typedef std::unordered_map<StorePath, std::unordered_set<std::string>> Roots;
/**
 * Possible garbage collector roots, referring to any path
 */
typedef std::unordered_map<Path, std::unordered_set<std::string>> UncheckedRoots;


struct GCOptions
{
    /**
     * Garbage collector operation:
     *
     * - `gcReturnLive`: return the set of paths reachable from
     *   (i.e. in the closure of) the roots.
     *
     * - `gcReturnDead`: return the set of paths not reachable from
     *   the roots.
     *
     * - `gcDeleteDead`: actually delete the latter set.
     *
     * - `gcDeleteSpecific`: delete the paths listed in
     *    `pathsToDelete`, failing if any are still reachable.
     *
     * - `gcTryDeleteSpecific`: delete the paths listed in
     *    `pathsToDelete`, insofar as they are not reachable.
     *    Any that could not be deleted are returned via the
     *    `kept` field of GCResults.
     */
    enum GCAction : uint8_t {
        gcReturnLive,
        gcReturnDead,
        gcDeleteDead,
        gcDeleteSpecific,
        gcTryDeleteSpecific,
    };

    GCAction action{gcDeleteDead};

    /**
     * If `ignoreLiveness` is set, then reachability from the roots is
     * ignored (dangerous!).  However, the paths must still be
     * unreferenced *within* the store (i.e., there can be no other
     * store paths that depend on them).
     */
    bool ignoreLiveness{false};

    /**
     * For `gcDeleteSpecific`, the paths to delete.
     */
    StorePathSet pathsToDelete;

    /**
     * Stop after at least `maxFreed` bytes have been freed.
     */
    uint64_t maxFreed{std::numeric_limits<uint64_t>::max()};
};


struct GCResults
{
    /**
     * Depending on the action, the GC roots, or the paths that would
     * be or have been deleted.
     */
    PathSet paths;

    /**
     * If the action was gcTryDeleteSpecific, the paths that were not
     * deleted because they are still live.
     */
    PathSet kept;

    /**
     * For `gcReturnDead`, `gcDeleteDead` and `gcDeleteSpecific`, the
     * number of bytes that would be or was freed.
     */
    uint64_t bytesFreed = 0;
};


/**
 * Mix-in class for \ref Store "stores" which expose a notion of garbage
 * collection.
 *
 * Garbage collection will allow deleting paths which are not
 * transitively "rooted".
 *
 * The notion of GC roots actually not part of this class.
 *
 *  - The base `Store` class has `Store::addTempRoot()` because for a store
 *    that doesn't support garbage collection at all, a temporary GC root is
 *    safely implementable as no-op.
 *
 *    @todo actually this is not so good because stores are *views*.
 *    Some views have only a no-op temp roots even though others to the
 *    same store allow triggering GC. For instance one can't add a root
 *    over ssh, but that doesn't prevent someone from gc-ing that store
 *    accesed via SSH locally).
 *
 *  - The derived `LocalFSStore` class has `LocalFSStore::addPermRoot`,
 *    which is not part of this class because it relies on the notion of
 *    an ambient file system. There are stores (`ssh-ng://`, for one),
 *    that *do* support garbage collection but *don't* expose any file
 *    system, and `LocalFSStore::addPermRoot` thus does not make sense
 *    for them.
 */
struct GcStore : public virtual Store
{
    inline static std::string operationName = "Garbage collection";

    /**
     * Find the roots of the garbage collector.  Each root is a pair
     * `(link, storepath)` where `link` is the path of the symlink
     * outside of the Nix store that point to `storePath`. If
     * `censor` is true, privacy-sensitive information about roots
     * found in `/proc` is censored.
     */
    virtual kj::Promise<Result<Roots>> findRoots(bool censor) = 0;

    /**
     * Perform a garbage collection.
     */
    virtual kj::Promise<Result<void>>
    collectGarbage(const GCOptions & options, GCResults & results) = 0;
};

}