/* Copyright (C) 2018 Anders Blomdell <anders.blomdell@control.lth.se> This file is part of hashtoc. Hashtoc is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */ #include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/xattr.h> #include <fcntl.h> #include <time.h> #include <unistd.h> #include <openssl/md5.h> #include <errno.h> #include <limits.h> #include <stdint.h> #include "libhash.h" #define BLOCKSIZE 4096 #define XATTR_MD5HASH "trusted.md5.control.lth.se" struct __attribute__((__packed__)) hash_attr { time_t hash_time; time_t mtime; uint32_t mtime_nsec; uint8_t hash[MD5_DIGEST_LENGTH]; }; int hash_md5_buf(char *buf, size_t length, unsigned char *hash) { MD5_CTX ctx; MD5_Init(&ctx); MD5_Update(&ctx, buf, length); MD5_Final(hash, &ctx); return 0; } int hash_md5_file(char *filename, time_t mtime, int64_t mtime_nsec, int flags, time_t maxage, unsigned char *hash) { int fd; int result = 0, recalc = 1; struct hash_attr hash_attr; if (flags & FLAGS_CLEAR_XATTR) { if (removexattr(filename, XATTR_MD5HASH) != 0) { if (errno != ENODATA) { result = -1; fprintf(stderr, "Failed to remove xattr '%s'\n", filename); goto out; } else if ((flags & FLAGS_VERBOSE_MASK) > FLAGS_VERBOSE2) { fprintf(stderr, "No xattr for '%s'\n", filename); } } else { if ((flags & FLAGS_VERBOSE_MASK) > FLAGS_VERBOSE2) { fprintf(stderr, "Removed xattr for '%s'\n", filename); } } } if (flags & FLAGS_NO_CALC_HASH) { // We should not calculate hash goto out; } if (flags & FLAGS_READ_XATTR) { int err; err = getxattr(filename, XATTR_MD5HASH, &hash_attr, sizeof(hash_attr)); if (err < 0 && errno != ENODATA) { result = -1; goto out; } if (err != sizeof(hash_attr)) { recalc = 1; } else if (le64toh(hash_attr.mtime) != mtime) { recalc = 1; } else if (le32toh(hash_attr.mtime_nsec) != mtime_nsec) { recalc = 1; } else if (flags & FLAGS_MAX_AGE && le64toh(hash_attr.hash_time) < maxage) { recalc = 1; } else { recalc = 0; memcpy(hash, hash_attr.hash, MD5_DIGEST_LENGTH); } } if (recalc == 0) { goto out; } fd = open (filename, O_RDONLY); if (fd < 0) { result = -1; goto out; } else { MD5_CTX ctx; unsigned char buffer[BLOCKSIZE]; int count; time_t now; time(&now); MD5_Init(&ctx); while ((count = read(fd, buffer, BLOCKSIZE)) > 0) { MD5_Update(&ctx, buffer, count); } MD5_Final(hash, &ctx); if (count < 0) { result = -1; fremovexattr(fd, XATTR_MD5HASH); goto out_close; } if (flags & FLAGS_WRITE_XATTR) { int err; hash_attr.hash_time = htole64(now); hash_attr.mtime = htole64(mtime); hash_attr.mtime_nsec = htole32(mtime_nsec); memcpy(hash_attr.hash, hash, MD5_DIGEST_LENGTH); err = fsetxattr(fd, XATTR_MD5HASH, &hash_attr, sizeof(hash_attr), 0); if (err < 0) { result = -1; fremovexattr(fd, XATTR_MD5HASH); goto out_close; } } else if (flags & FLAGS_READ_XATTR) { fremovexattr(fd, XATTR_MD5HASH); } } out_close: close(fd); out: return result; }