Skip to content

Commit

Permalink
Merge pull request #12044 from roberth/c-api-nix-store
Browse files Browse the repository at this point in the history
C API: nix_store_open doc, add storedir, real_path
  • Loading branch information
mergify[bot] authored Dec 14, 2024
2 parents bf24092 + 2a98168 commit 6a23803
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 7 deletions.
23 changes: 23 additions & 0 deletions src/libstore-c/nix_api_store.cc
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,17 @@ nix_err nix_store_get_uri(nix_c_context * context, Store * store, nix_get_string
NIXC_CATCH_ERRS
}

nix_err
nix_store_get_storedir(nix_c_context * context, Store * store, nix_get_string_callback callback, void * user_data)
{
if (context)
context->last_err_code = NIX_OK;
try {
return call_nix_get_string_callback(store->ptr->storeDir, callback, user_data);
}
NIXC_CATCH_ERRS
}

nix_err
nix_store_get_version(nix_c_context * context, Store * store, nix_get_string_callback callback, void * user_data)
{
Expand All @@ -89,6 +100,18 @@ bool nix_store_is_valid_path(nix_c_context * context, Store * store, StorePath *
NIXC_CATCH_ERRS_RES(false);
}

nix_err nix_store_real_path(
nix_c_context * context, Store * store, StorePath * path, nix_get_string_callback callback, void * user_data)
{
if (context)
context->last_err_code = NIX_OK;
try {
auto res = store->ptr->toRealPath(path->path);
return call_nix_get_string_callback(res, callback, user_data);
}
NIXC_CATCH_ERRS
}

StorePath * nix_store_parse_path(nix_c_context * context, Store * store, const char * path)
{
if (context)
Expand Down
56 changes: 51 additions & 5 deletions src/libstore-c/nix_api_store.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,27 @@ nix_err nix_libstore_init_no_load_config(nix_c_context * context);
* Store instances may share state and resources behind the scenes.
*
* @param[out] context Optional, stores error information
* @param[in] uri URI of the Nix store, copied. See [*Store URL format* in the Nix Reference
*
* @param[in] uri @parblock
* URI of the Nix store, copied.
*
* If `NULL`, the store from the settings will be used.
* Note that `"auto"` holds a strange middle ground, reading part of the general environment, but not all of it. It
* ignores `NIX_REMOTE` and the `store` option. For this reason, `NULL` is most likely the better choice.
*
* For supported store URLs, see [*Store URL format* in the Nix Reference
* Manual](https://nixos.org/manual/nix/stable/store/types/#store-url-format).
* @param[in] params optional, null-terminated array of key-value pairs, e.g. {{"endpoint",
* "https://s3.local"}}. See [*Store Types* in the Nix Reference
* Manual](https://nixos.org/manual/nix/stable/store/types).
* @endparblock
*
* @param[in] params @parblock
* optional, null-terminated array of key-value pairs, e.g. {{"endpoint",
* "https://s3.local"}}.
*
* See [*Store Types* in the Nix Reference Manual](https://nixos.org/manual/nix/stable/store/types).
* @endparblock
*
* @return a Store pointer, NULL in case of errors
*
* @see nix_store_free
*/
Store * nix_store_open(nix_c_context * context, const char * uri, const char *** params);
Expand All @@ -78,7 +93,18 @@ void nix_store_free(Store * store);
*/
nix_err nix_store_get_uri(nix_c_context * context, Store * store, nix_get_string_callback callback, void * user_data);

// returns: owned StorePath*
/**
* @brief get the storeDir of a Nix store, typically `"/nix/store"`
* @param[out] context Optional, stores error information
* @param[in] store nix store reference
* @param[in] callback Called with the URI.
* @param[in] user_data optional, arbitrary data, passed to the callback when it's called.
* @see nix_get_string_callback
* @return error code, NIX_OK on success.
*/
nix_err
nix_store_get_storedir(nix_c_context * context, Store * store, nix_get_string_callback callback, void * user_data);

/**
* @brief Parse a Nix store path into a StorePath
*
Expand Down Expand Up @@ -123,6 +149,26 @@ void nix_store_path_free(StorePath * p);
* @return true or false, error info in context
*/
bool nix_store_is_valid_path(nix_c_context * context, Store * store, StorePath * path);

/**
* @brief Get the physical location of a store path
*
* A store may reside at a different location than its `storeDir` suggests.
* This situation is called a relocated store.
* Relocated stores are used during NixOS installation, as well as in restricted computing environments that don't offer
* a writable `/nix/store`.
*
* Not all types of stores support this operation.
*
* @param[in] context Optional, stores error information
* @param[in] store nix store reference
* @param[in] path the path to get the real path from
* @param[in] callback called with the real path
* @param[in] user_data arbitrary data, passed to the callback when it's called.
*/
nix_err nix_store_real_path(
nix_c_context * context, Store * store, StorePath * path, nix_get_string_callback callback, void * user_data);

// nix_err nix_store_ensure(Store*, const char*);
// nix_err nix_store_build_paths(Store*);
/**
Expand Down
5 changes: 4 additions & 1 deletion src/libstore-tests/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ cxx = meson.get_compiler('cpp')

subdir('nix-meson-build-support/deps-lists')

nix_store = dependency('nix-store')

deps_private_maybe_subproject = [
dependency('nix-store'),
nix_store,
dependency('nix-store-c'),
dependency('nix-store-test-support'),
]
Expand Down Expand Up @@ -90,6 +92,7 @@ this_exe = executable(
include_directories : include_dirs,
# TODO: -lrapidcheck, see ../libutil-support/build.meson
link_args: linker_export_flags + ['-lrapidcheck'],
cpp_args : [ '-DNIX_STORE_DIR="' + nix_store.get_variable('storedir') + '"' ],
# get main from gtest
install : true,
)
Expand Down
113 changes: 113 additions & 0 deletions src/libstore-tests/nix_api_store.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,39 @@ TEST_F(nix_api_store_test, nix_store_get_uri)
ASSERT_STREQ("local", str.c_str());
}

TEST_F(nix_api_util_context, nix_store_get_storedir_default)
{
if (nix::getEnv("HOME").value_or("") == "/homeless-shelter") {
// skipping test in sandbox because nix_store_open tries to create /nix/var/nix/profiles
GTEST_SKIP();
}
nix_libstore_init(ctx);
Store * store = nix_store_open(ctx, nullptr, nullptr);
assert_ctx_ok();
ASSERT_NE(store, nullptr);

std::string str;
auto ret = nix_store_get_storedir(ctx, store, OBSERVE_STRING(str));
assert_ctx_ok();
ASSERT_EQ(NIX_OK, ret);

// These tests run with a unique storeDir, but not a relocated store
ASSERT_STREQ(NIX_STORE_DIR, str.c_str());

nix_store_free(store);
}

TEST_F(nix_api_store_test, nix_store_get_storedir)
{
std::string str;
auto ret = nix_store_get_storedir(ctx, store, OBSERVE_STRING(str));
assert_ctx_ok();
ASSERT_EQ(NIX_OK, ret);

// These tests run with a unique storeDir, but not a relocated store
ASSERT_STREQ(nixStoreDir.c_str(), str.c_str());
}

TEST_F(nix_api_store_test, InvalidPathFails)
{
nix_store_parse_path(ctx, store, "invalid-path");
Expand Down Expand Up @@ -86,4 +119,84 @@ TEST_F(nix_api_store_test, nix_store_is_valid_path_not_in_store)
ASSERT_EQ(false, nix_store_is_valid_path(ctx, store, path));
}

TEST_F(nix_api_store_test, nix_store_real_path)
{
StorePath * path = nix_store_parse_path(ctx, store, (nixStoreDir + PATH_SUFFIX).c_str());
std::string rp;
auto ret = nix_store_real_path(ctx, store, path, OBSERVE_STRING(rp));
assert_ctx_ok();
ASSERT_EQ(NIX_OK, ret);
// Assumption: we're not testing with a relocated store
ASSERT_STREQ((nixStoreDir + PATH_SUFFIX).c_str(), rp.c_str());

nix_store_path_free(path);
}

TEST_F(nix_api_util_context, nix_store_real_path_relocated)
{
if (nix::getEnv("HOME").value_or("") == "/homeless-shelter") {
// Can't open default store from within sandbox
GTEST_SKIP();
}
auto tmp = nix::createTempDir();
std::string storeRoot = tmp + "/store";
std::string stateDir = tmp + "/state";
std::string logDir = tmp + "/log";
const char * rootkv[] = {"root", storeRoot.c_str()};
const char * statekv[] = {"state", stateDir.c_str()};
const char * logkv[] = {"log", logDir.c_str()};
// const char * rokv[] = {"read-only", "true"};
const char ** kvs[] = {rootkv, statekv, logkv, NULL};

nix_libstore_init(ctx);
assert_ctx_ok();

Store * store = nix_store_open(ctx, "local", kvs);
assert_ctx_ok();
ASSERT_NE(store, nullptr);

std::string nixStoreDir;
auto ret = nix_store_get_storedir(ctx, store, OBSERVE_STRING(nixStoreDir));
ASSERT_EQ(NIX_OK, ret);
ASSERT_STREQ(NIX_STORE_DIR, nixStoreDir.c_str());

StorePath * path = nix_store_parse_path(ctx, store, (nixStoreDir + PATH_SUFFIX).c_str());
assert_ctx_ok();
ASSERT_NE(path, nullptr);

std::string rp;
ret = nix_store_real_path(ctx, store, path, OBSERVE_STRING(rp));
assert_ctx_ok();
ASSERT_EQ(NIX_OK, ret);

// Assumption: we're not testing with a relocated store
ASSERT_STREQ((storeRoot + NIX_STORE_DIR + PATH_SUFFIX).c_str(), rp.c_str());

nix_store_path_free(path);
}

TEST_F(nix_api_util_context, nix_store_real_path_binary_cache)
{
if (nix::getEnv("HOME").value_or("") == "/homeless-shelter") {
// TODO: override NIX_CACHE_HOME?
// skipping test in sandbox because narinfo cache can't be written
GTEST_SKIP();
}

Store * store = nix_store_open(ctx, "https://cache.nixos.org", nullptr);
assert_ctx_ok();
ASSERT_NE(store, nullptr);

std::string path_raw = std::string(NIX_STORE_DIR) + PATH_SUFFIX;
StorePath * path = nix_store_parse_path(ctx, store, path_raw.c_str());
assert_ctx_ok();
ASSERT_NE(path, nullptr);

std::string rp;
auto ret = nix_store_real_path(ctx, store, path, OBSERVE_STRING(rp));
assert_ctx_ok();
ASSERT_EQ(NIX_OK, ret);
ASSERT_STREQ(path_raw.c_str(), rp.c_str());
}

} // namespace nixC
2 changes: 1 addition & 1 deletion src/libutil-c/nix_api_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ extern "C" {
*/
// Error codes
/**
* @brief Type for error codes in the NIX system
* @brief Type for error codes in the Nix system
*
* This type can have one of several predefined constants:
* - NIX_OK: No error occurred (0)
Expand Down

0 comments on commit 6a23803

Please sign in to comment.