aboutsummaryrefslogtreecommitdiff
#include "config.h"

#include <iostream>
#include <cstring>

#include "hash.hh"
#include "archive.hh"
#include "util.hh"

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


namespace nix {


Hash::Hash()
{
    type = htUnknown;
    hashSize = 0;
    memset(hash, 0, maxHashSize);
}


Hash::Hash(HashType type)
{
    this->type = type;
    hashSize = gcry_md_get_algo_dlen(type);

    if (hashSize == 0) throw Error("unknown hash type");
    assert(hashSize <= maxHashSize);
    memset(hash, 0, maxHashSize);
}


bool Hash::operator == (const Hash & h2) const
{
    if (hashSize != h2.hashSize) return false;
    for (unsigned int i = 0; i < hashSize; i++)
        if (hash[i] != h2.hash[i]) return false;
    return true;
}


bool Hash::operator != (const Hash & h2) const
{
    return !(*this == h2);
}


bool Hash::operator < (const Hash & h) const
{
    for (unsigned int i = 0; i < hashSize; i++) {
        if (hash[i] < h.hash[i]) return true;
        if (hash[i] > h.hash[i]) return false;
    }
    return false;
}


const string base16Chars = "0123456789abcdef";


string printHash(const Hash & hash)
{
    char buf[hash.hashSize * 2];
    for (unsigned int i = 0; i < hash.hashSize; i++) {
        buf[i * 2] = base16Chars[hash.hash[i] >> 4];
        buf[i * 2 + 1] = base16Chars[hash.hash[i] & 0x0f];
    }
    return string(buf, hash.hashSize * 2);
}


Hash parseHash(HashType ht, const string & s)
{
    Hash hash(ht);
    if (s.length() != hash.hashSize * 2) {
	string algo = gcry_md_algo_name(ht);
        throw Error(format("invalid %1% hash '%2%' (%3% bytes but expected %4%)")
		    % algo % s % (s.length() / 2) % hash.hashSize);
    }
    for (unsigned int i = 0; i < hash.hashSize; i++) {
        string s2(s, i * 2, 2);
        if (!isxdigit(s2[0]) || !isxdigit(s2[1]))
            throw Error(format("invalid hash `%1%'") % s);
        std::istringstream str(s2);
        int n;
        str >> std::hex >> n;
        hash.hash[i] = n;
    }
    return hash;
}


unsigned int hashLength32(const Hash & hash)
{
    return (hash.hashSize * 8 - 1) / 5 + 1;
}


// omitted: E O U T
const string base32Chars = "0123456789abcdfghijklmnpqrsvwxyz";


string printHash32(const Hash & hash)
{
    Hash hash2(hash);
    unsigned int len = hashLength32(hash);

    string s;
    s.reserve(len);

    for (int n = len - 1; n >= 0; n--) {
        unsigned int b = n * 5;
        unsigned int i = b / 8;
        unsigned int j = b % 8;
        unsigned char c =
            (hash.hash[i] >> j)
            | (i >= hash.hashSize - 1 ? 0 : hash.hash[i + 1] << (8 - j));
        s.push_back(base32Chars[c & 0x1f]);
    }

    return s;
}


string printHash16or32(const Hash & hash)
{
    return hash.type == htMD5 ? printHash(hash) : printHash32(hash);
}


Hash parseHash32(HashType ht, const string & s)
{
    Hash hash(ht);
    unsigned int len = hashLength32(ht);
    assert(s.size() == len);

    for (unsigned int n = 0; n < len; ++n) {
        char c = s[len - n - 1];
        unsigned char digit;
        for (digit = 0; digit < base32Chars.size(); ++digit) /* !!! slow */
            if (base32Chars[digit] == c) break;
        if (digit >= 32)
            throw Error(format("invalid base-32 hash '%1%'") % s);
        unsigned int b = n * 5;
        unsigned int i = b / 8;
        unsigned int j = b % 8;
        hash.hash[i] |= digit << j;
        if (i < hash.hashSize - 1) hash.hash[i + 1] |= digit >> (8 - j);
    }

    return hash;
}


Hash parseHash16or32(HashType ht, const string & s)
{
    Hash hash(ht);
    if (s.size() == hash.hashSize * 2)
        /* hexadecimal representation */
        hash = parseHash(ht, s);
    else if (s.size() == hashLength32(hash))
        /* base-32 representation */
        hash = parseHash32(ht, s);
    else
        throw Error(format("hash `%1%' has wrong length for hash type `%2%'")
            % s % printHashType(ht));
    return hash;
}


bool isHash(const string & s)
{
    if (s.length() != 32) return false;
    for (int i = 0; i < 32; i++) {
        char c = s[i];
        if (!((c >= '0' && c <= '9') ||
              (c >= 'a' && c <= 'f')))
            return false;
    }
    return true;
}

/* The "hash context".  */
struct Ctx
{
  /* This copy constructor is needed in 'HashSink::currentHash()' where we
     expect the copy of a 'Ctx' object to yield a truly different context.  */
  Ctx(Ctx &ref)
  {
    if (ref.md_handle == NULL)
      md_handle = NULL;
    else
      gcry_md_copy (&md_handle, ref.md_handle);
  }

  /* Make sure 'md_handle' is always initialized.  */
  Ctx(): md_handle (NULL) { };

  gcry_md_hd_t md_handle;
};


static void start(HashType ht, Ctx & ctx)
{
    gcry_error_t err;

    err = gcry_md_open (&ctx.md_handle, ht, 0);
    assert (err == GPG_ERR_NO_ERROR);
}


static void update(HashType ht, Ctx & ctx,
    const unsigned char * bytes, unsigned int len)
{
    gcry_md_write (ctx.md_handle, bytes, len);
}


static void finish(HashType ht, Ctx & ctx, unsigned char * hash)
{
    memcpy (hash, gcry_md_read (ctx.md_handle, ht),
	    gcry_md_get_algo_dlen (ht));
    gcry_md_close (ctx.md_handle);
    ctx.md_handle = NULL;
}


Hash hashString(HashType ht, const string & s)
{
    Ctx ctx;
    Hash hash(ht);
    start(ht, ctx);
    update(ht, ctx, (const unsigned char *) s.data(), s.length());
    finish(ht, ctx, hash.hash);
    return hash;
}


Hash hashFile(HashType ht, const Path & path)
{
    Ctx ctx;
    Hash hash(ht);
    start(ht, ctx);

    AutoCloseFD fd = open(path.c_str(), O_RDONLY);
    if (fd == -1) throw SysError(format("computing hash of file `%1%'") % path);

    unsigned char buf[8192];
    ssize_t n;
    while ((n = read(fd, buf, sizeof(buf)))) {
        checkInterrupt();
        if (n == -1) throw SysError(format("reading file `%1%'") % path);
        update(ht, ctx, buf, n);
    }

    finish(ht, ctx, hash.hash);
    return hash;
}


HashSink::HashSink(HashType ht) : ht(ht)
{
    ctx = new Ctx;
    bytes = 0;
    start(ht, *ctx);
}

HashSink::~HashSink()
{
    bufPos = 0;
    delete ctx;
}

void HashSink::write(const unsigned char * data, size_t len)
{
    bytes += len;
    update(ht, *ctx, data, len);
}

HashResult HashSink::finish()
{
    flush();
    Hash hash(ht);
    nix::finish(ht, *ctx, hash.hash);
    return HashResult(hash, bytes);
}

HashResult HashSink::currentHash()
{
    flush();
    Ctx ctx2 = *ctx;
    Hash hash(ht);
    nix::finish(ht, ctx2, hash.hash);
    return HashResult(hash, bytes);
}


HashResult hashPath(
    HashType ht, const Path & path, PathFilter & filter)
{
    HashSink sink(ht);
    dumpPath(path, sink, filter);
    return sink.finish();
}


Hash compressHash(const Hash & hash, unsigned int newSize)
{
    Hash h;
    h.hashSize = newSize;
    for (unsigned int i = 0; i < hash.hashSize; ++i)
        h.hash[i % newSize] ^= hash.hash[i];
    return h;
}


HashType parseHashType(const string & s)
{
    if (s == "md5") return htMD5;
    else if (s == "sha1") return htSHA1;
    else if (s == "sha256") return htSHA256;
    else if (s == "sha512") return htSHA512;
    else if (s == "sha3-256") return htSHA3_256;
    else if (s == "sha3-512") return htSHA3_512;
    else if (s == "blake2s-256") return htBLAKE2s_256;
    else return htUnknown;
}


string printHashType(HashType ht)
{
    if (ht == htMD5) return "md5";
    else if (ht == htSHA1) return "sha1";
    else if (ht == htSHA256) return "sha256";
    else if (ht == htSHA512) return "sha512";
    else if (ht == htSHA3_256) return "sha3-256";
    else if (ht == htSHA3_512) return "sha3-512";
    else if (ht == htBLAKE2s_256) return "blake2s-256";
    else throw Error("cannot print unknown hash type");
}


}
): Do not use "setup" as a word in generated files. End sentences with a period. Ludovic Courtès 2022-06-29home: services: shells: Export Fish environment variables....Variables set in the Fish configuration weren't previously being exported. * gnu/home/services/shells.scm (serialize-fish-env-vars): Output the `set -x` command instead of `set`. Signed-off-by: Tobias Geerinckx-Rice <me@tobias.gr> ( 2022-04-29home: shells: Migrate fish to xdg-configuration-files....* gnu/home/services/shells.scm (home-fish-service-type): Use new xdg-configuration-files-service-type service. Signed-off-by: Ludovic Courtès <ludo@gnu.org> Bastien Rivière 2022-04-29home: services: fish: Fix incorrect ".config" file name....This is a followup to 5832d9fb601c7d4ec5380654db2b62b906bc658f. * gnu/home/services/shells.scm (fish-files-service): Change "config/" to ".config/". Signed-off-by: Ludovic Courtès <ludo@gnu.org> Maya 2022-04-11home: shells: Rename zsh related functions....* gnu/home/services/shells.scm (home-zsh-service-type): Make zsh related private functions more consistently named. Signed-off-by: Ludovic Courtès <ludo@gnu.org> Andrew Tropin 2022-04-08home: symlink-manager: Remove appending of leading dot....* gnu/home/services.scm (xdg-configuration-files-directory): Add leading dot. * gnu/home/services.scm (xdg-configuration-files-service-type): Change name. * gnu/home/services/shells.scm (add-shell-profile-file, zsh-get-configuration-files, add-zsh-dot-configuration, add-zsh-xdg-configuration, add-bash-configuration): Add leading dots. * gnu/home/services/symlink-manager.scm (update-symlinks-script): Remove leading dot. Signed-off-by: Ludovic Courtès <ludo@gnu.org> Andrew Tropin 2022-04-03home: services: zsh: Fix zshenv creation....Fixes a regression introduced by 4935501fd9f690d8990bb798237d51702b87fb3f that would skip generating the user's ~/.config/zsh/.zshenv unless both of zshenv and environment-variables are set in the configuration. * gnu/home/services/shells.scm (zsh-get-configuration-files): Generate ~/.config/zsh/.zshenv when only one of zshenv or environment-variables is specified. Signed-off-by: Ludovic Courtès <ludo@gnu.org> Brian Kubisiak 2022-03-20home: shells: Migrate zsh to xdg-configuration-files....* gnu/home/services.scm (home-zsh-service-type): Additionally extend home-xdg-configuration-files-service-type. Signed-off-by: Ludovic Courtès <ludo@gnu.org> Andrew Tropin 2022-03-18home: services: Fix bash aliases without guix-defaults....* gnu/home/services/shells.scm: Fix bash aliases not being added if guix-defaults? was #f. Also fix inaccuracy in documentation about placement of defaults. * doc/guix.texi (Shells Home Services): Adjust accordingly. Signed-off-by: Ludovic Courtès <ludo@gnu.org> angryrectangle 2021-11-24home: services: bash: Only source /etc/bashrc if it exists....* gnu/home/services/shells.scm (home-bash-configuration): Check whether /etc/bashrc exists before trying to ‘source’ it. Reported by guixy on #guix. Tobias Geerinckx-Rice 2021-11-16home: services: bash: Emit 'extra-content' first again....Fixes a regression introduced in 4b96998292442ec03024481c911d88f86c7c36b5 that would less to a 'tests/guix-home.sh' failure. * gnu/home/services/shells.scm (add-bash-configuration)[file-if-not-empty]: Move EXTRA-CONTENT first. Ludovic Courtès 2021-11-14gnu: home: services: Fix typo....* gnu/home/services/shells.scm (home-shell-profile-configuration): Fix spelling of "available". Vagrant Cascadian 2021-11-07doc: Improve documentation of the Bash home service...* doc/guix.texi (Shells Home Services): Document ‘home-bash-extension’ configuration record. * gnu/home/services/shells.scm (generate-home-bash-documentation): Extract docstrings from ‘home-bash-extension’. (home-bash-configuration): Expound on docstrings. (home-bash-extension): Likewise. Fixes: <https://issues.guix.gnu.org/50991> Signed-off-by: Ludovic Courtès <ludo@gnu.org> Xinglu Chen 2021-11-07home: services: bash: Add ‘aliases’ field....* doc/guix.texi (Shells Home Services): Document it. * gnu/home/services/shells.scm (bash-serialize-aliases): New procedure. (home-bash-configuration, home-bash-extension): Add ‘aliases’ field. (home-bash-extensions): Adjust accordingly. * guix/scripts/home/import.scm (generate-bash-configuration+modules): Populate the ‘alias’ field. Signed-off-by: Ludovic Courtès <ludo@gnu.org> Xinglu Chen 2021-10-16home: services: shells: Fix documentation about file-like objects....* gnu/home/services/shells.scm: (home-shell-profile-configuration, home-shell-profile-service-type, home-zsh-configuration, home-zsh-extension, home-bash-configuration, home-bash-extension, home-fish-configuration, home-fish-extension): Replace 'strings or gexps' with 'file-like objects' in the documentation. * doc/guix.texi (Shells Home Services): Same. Oleg Pykhalov 2021-10-09home: services: configuration: Support file-like objects....* gnu/home/services/configuration.scm (interpose): Operate only with file-like objects. (string-or-gexp?): Delete procedure. (serialize-string-or-gexp): Rename to 'serialize-file-like'. (text-config?): Call 'file-like' intead of 'string-or-gexp?'. * guix/scripts/home/import.scm: (generate-bash-module+configuration): Don't call slurp-file-gexp. * gnu/home/services/configuration.scm: Move content ... * gnu/services/configuration.scm: here. * gnu/home/services/shells.scm: Delete (gnu home services configuration). * gnu/home/services/xdg.scm: Same. * gnu/local.mk: Same. * tests/guix-home.sh: Test home-bash-service-type and extension with home-bash-extension. Oleg Pykhalov 2021-10-09Move (gnu home-services) to (gnu home services)....* gnu/home-services.scm (%guix-home-root-directory): Replace gnu/home-services.scm with "gnu/home/services.scm". Rename to gnu/home/services.scm. * gnu/local.mk (GNU_SYSTEM_MODULES): Rename gnu/home-services.scm to gnu/home/services.scm. * doc/he-config-bare-bones.scm: Replace (gnu home-services) with (gnu home services). * gnu/home.scm: Same. * gnu/home/services/fontutils.scm: Same. * gnu/home/services/mcron.scm: Same. * gnu/home/services/shells.scm: Same. * gnu/home/services/shepherd.scm: Same. * gnu/home/services/symlink-manager.scm: Same. * gnu/home/services/xdg.scm: Same. * guix/scripts/home.scm: Same. * guix/self.scm: Same. Oleg Pykhalov 2021-10-08gnu: Move (gnu home-services) to (gnu home services)....* gnu/home-services/configuration.scm: Move the content ... * gnu/home/services/configuration.scm: ... here. * doc/guix.texi: Replace (gnu home-services mcron) with (gnu home services mcron). Replace (gnu home-services) with (gnu home services). * gnu/home.scm: Replace (gnu home-services fontutils) with (gnu services fontutils). Replace (gnu home-services shells) with (gnu home services shells). Replace (gnu home-services symlink-manager) with (gnu home services symlink-manager). Replace (gnu home-services xdg) with (gnu home services xdg). * gnu/home-services/fontutils.scm: Rename to gnu/services/fontutils.scm. * gnu/home-services/mcron.scm: Move to gnu/home/services/mcron.scm. Replace (gnu home-services shepherd) with (gnu home services shepherd). * gnu/home-services.scm (%service-type-path): Search home services in "gnu/services". * gnu/home-services/shells.scm: Replace (gnu home-services configuration) with (gnu home services configuration). Rename to gnu/home/services/shells.scm. Replace (gnu home-services utils) with (gnu home services utils). * gnu/home-services/shepherd.scm: Move to gnu/home/services/shepherd.scm. * gnu/home-services/symlink-manager.scm: Rename to gnu/home/services/symlink-manager.scm. * gnu/home-services/utils.scm: Rename to gnu/home/services/utils.scm. * gnu/home-services/xdg.scm: Rename to gnu/home/services/xdg.scm. * guix/scripts/home/import.scm: Replace (gnu home-services bash) with (gnu home services bash). * gnu/home-services.scm: Update documentation string. * doc/he-config-bare-bones.scm: Apply new (gnu home-services ...) modules location. * gnu/local.mk (GNU_SYSTEM_MODULES): Same. Oleg Pykhalov