nbos

nbos

https://git.tonybtw.com/nbos.git git://git.tonybtw.com/nbos.git
3,989 bytes raw
1
#define _GNU_SOURCE
2
#include "store.h"
3
4
#include <errno.h>
5
#include <fcntl.h>
6
#include <stdint.h>
7
#include <stdio.h>
8
#include <stdlib.h>
9
#include <string.h>
10
#include <sys/stat.h>
11
#include <sys/types.h>
12
#include <unistd.h>
13
14
#include "hash.h"
15
16
static const char b32_alphabet[] = "0123456789abcdfghijklmnpqrsvwxyz";
17
18
static void base32_encode(const uint8_t *in, size_t len, char *out) {
19
    size_t bits = 0;
20
    uint32_t buf = 0;
21
    size_t out_idx = 0;
22
23
    for (size_t i = 0; i < len; i++) {
24
        buf = (buf << 8) | in[i];
25
        bits += 8;
26
        while (bits >= 5) {
27
            bits -= 5;
28
            out[out_idx++] = b32_alphabet[(buf >> bits) & 0x1f];
29
        }
30
    }
31
    if (bits > 0) {
32
        out[out_idx++] = b32_alphabet[(buf << (5 - bits)) & 0x1f];
33
    }
34
    out[out_idx] = '\0';
35
}
36
37
38
39
40
/**
41
 * store_path_compute() - Compute the content-addressed store path for @p.
42
 * @a: Arena that owns the returned string.
43
 * @p: Package whose inputs determine the path.
44
 * @resolved_pkgs: Resolved entries already topo-sorted; deps must already
45
 *                 have @store_path populated.
46
 * @n_resolved: Number of valid entries in @resolved_pkgs.
47
 *
48
 * Hashes a length-prefixed canonical encoding of the package inputs
49
 * (name, version, src URL, source sha256, build flags, build system,
50
 * resolved dep paths). The first 20 bytes of the digest are base32
51
 * encoded; identical inputs always produce the same path.
52
 *
53
 * Return: "/nb/store/<hash>-<name>-<version>" owned by @a, or NULL if a
54
 * dep's store path is not yet resolved.
55
 */
56
const char *store_path_compute(
57
    arena              *a,
58
    const pkg          *p,
59
    const resolved     *resolved_pkgs,
60
    size_t              n_resolved)
61
{
62
    sha256_ctx ctx;
63
    sha256_init(&ctx);
64
65
    #define HASH_FIELD(s) do { \
66
        size_t l = strlen(s); \
67
        sha256_update(&ctx, &l, sizeof(l)); \
68
        sha256_update(&ctx, (s), l); \
69
        sha256_update(&ctx, "\0", 1); \
70
    } while (0)
71
72
    HASH_FIELD(p->name);
73
    HASH_FIELD(p->version);
74
    HASH_FIELD(p->src);
75
    HASH_FIELD(p->sha256);
76
    HASH_FIELD(p->build_flags);
77
78
    uint8_t bs = (uint8_t)p->build_sys;
79
    sha256_update(&ctx, &bs, 1);
80
    sha256_update(&ctx, "\0", 1);
81
82
    for (size_t i = 0; i < p->deps.len; i++) {
83
        const pkg *target = p->deps.data[i];
84
        const char *dep_path = nullptr;
85
        for (size_t j = 0; j < n_resolved; j++) {
86
            if (resolved_pkgs[j].def == target) {
87
                dep_path = resolved_pkgs[j].store_path;
88
                break;
89
            }
90
        }
91
        if (dep_path == nullptr) return nullptr;
92
        HASH_FIELD(dep_path);
93
    }
94
95
    #undef HASH_FIELD
96
97
    uint8_t digest[32];
98
    sha256_final(&ctx, digest);
99
100
    char b32[33];
101
    base32_encode(digest, 20, b32);
102
103
    return arena_sprintf(a, "%s/%s-%s-%s",
104
                         NB_STORE_ROOT, b32, p->name, p->version);
105
}
106
107
/**
108
 * store_path_exists() - Check whether @store_path is a populated store entry.
109
 * @store_path: Path to test.
110
 *
111
 * Return: true if @store_path is an existing directory.
112
 */
113
bool store_path_exists(const char *store_path) {
114
    struct stat st;
115
    if (stat(store_path, &st) < 0) return false;
116
    return S_ISDIR(st.st_mode);
117
}
118
119
/**
120
 * store_install() - Atomically move a build output into the store.
121
 * @temp_path: Source directory produced by the build.
122
 * @store_path: Destination inside the store.
123
 *
124
 * Return: 0 on success, errno value on failure.
125
 */
126
int store_install(const char *temp_path, const char *store_path) {
127
    if (rename(temp_path, store_path) < 0) {
128
        return errno;
129
    }
130
    return 0;
131
}
132
133
/**
134
 * store_remove() - Recursively remove a store entry.
135
 * @store_path: Path to remove.
136
 *
137
 * Return: 0 on success, errno value on failure.
138
 */
139
int store_remove(const char *store_path) {
140
    char cmd[4096];
141
    int n = snprintf(cmd, sizeof(cmd), "/bin/rm -rf '%s'", store_path);
142
    if (n < 0 || (size_t)n >= sizeof(cmd)) return ENAMETOOLONG;
143
    int rc = system(cmd);
144
    return rc == 0 ? 0 : EIO;
145
}