├── .version ├── .gitignore ├── bin ├── crccp ├── crcmv └── crcsum ├── src ├── crcsum.x ├── crccp.x ├── crc64.h ├── crcmv.x ├── cu-progs.mk.patch ├── copy.h.patch ├── cu-progs.m4.patch ├── crcsum.h ├── local.mk.patch ├── crcsum_cli.c ├── crcmv.c.patch ├── crccp.c.patch ├── copy.c.patch ├── crc64.c └── crcsum.c ├── create-patch-files ├── ChangeLog ├── test └── test.sh └── README.md /.version: -------------------------------------------------------------------------------- 1 | 9.7.0 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.tar.xz 2 | coreutils-*/* 3 | notes.txt 4 | build-beta 5 | -------------------------------------------------------------------------------- /bin/crccp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansij66/securecopy/HEAD/bin/crccp -------------------------------------------------------------------------------- /bin/crcmv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansij66/securecopy/HEAD/bin/crcmv -------------------------------------------------------------------------------- /bin/crcsum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansij66/securecopy/HEAD/bin/crcsum -------------------------------------------------------------------------------- /src/crcsum.x: -------------------------------------------------------------------------------- 1 | [NAME] 2 | crcsum \- check crc sum 3 | [DESCRIPTION] 4 | .\" Add any additional description here 5 | -------------------------------------------------------------------------------- /src/crccp.x: -------------------------------------------------------------------------------- 1 | [NAME] 2 | crccp \- copy files and directories 3 | [DESCRIPTION] 4 | .\" Add any additional description here 5 | -------------------------------------------------------------------------------- /src/crc64.h: -------------------------------------------------------------------------------- 1 | static const uint64_t crc64_tab[256]; 2 | uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l) __attribute__((pure)); 3 | -------------------------------------------------------------------------------- /src/crcmv.x: -------------------------------------------------------------------------------- 1 | [NAME] 2 | crcmv \- move (rename) files 3 | [DESCRIPTION] 4 | .\" Add any additional description here 5 | [SEE ALSO] 6 | rename(2) 7 | -------------------------------------------------------------------------------- /src/cu-progs.mk.patch: -------------------------------------------------------------------------------- 1 | --- /home/ijntema/truenas/programming/securecopy/core-utils-based/V9.7.0/coreutils-9.7/src/cu-progs.mk.org 2025-08-14 14:54:55.265733000 +0200 2 | +++ /home/ijntema/truenas/programming/securecopy/core-utils-based/V9.7.0/coreutils-9.7/src/cu-progs.mk 2025-08-14 14:54:55.287442300 +0200 3 | @@ -105,10 +105,15 @@ 4 | default__progs += src/tty 5 | default__progs += src/uname 6 | default__progs += src/unexpand 7 | default__progs += src/uniq 8 | default__progs += src/unlink 9 | default__progs += src/uptime 10 | default__progs += src/vdir 11 | default__progs += src/wc 12 | default__progs += src/whoami 13 | default__progs += src/yes 14 | +# HIJN 15 | +default__progs += src/crccp 16 | +default__progs += src/crcsum 17 | +default__progs += src/crcmv 18 | +# END HIJN 19 | -------------------------------------------------------------------------------- /create-patch-files: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Target coreutils (tested version) 4 | COREUTILS="coreutils-9.7" 5 | 6 | # Internal for this script 7 | CURRENTDIR=${PWD} 8 | 9 | PATCHDIR="${CURRENTDIR}/src" 10 | COREUTILSSRCDIR="${CURRENTDIR}/${COREUTILS}/src" 11 | 12 | echo ${PATCHDIR} 13 | echo ${COREUTILSSRCDIR} 14 | 15 | diff -U 10 ${COREUTILSSRCDIR}/copy.h.org ${COREUTILSSRCDIR}/copy.h > ${PATCHDIR}/copy.h.patch 16 | diff -U 10 ${COREUTILSSRCDIR}/copy.c.org ${COREUTILSSRCDIR}/copy.c > ${PATCHDIR}/copy.c.patch 17 | diff -U 10 ${COREUTILSSRCDIR}/cp.c ${COREUTILSSRCDIR}/crccp.c > ${PATCHDIR}/crccp.c.patch 18 | diff -U 10 ${COREUTILSSRCDIR}/mv.c ${COREUTILSSRCDIR}/crcmv.c > ${PATCHDIR}/crcmv.c.patch 19 | diff -U 10 ${COREUTILSSRCDIR}/cu-progs.mk.org ${COREUTILSSRCDIR}/cu-progs.mk > ${PATCHDIR}/cu-progs.mk.patch 20 | diff -U 10 ${COREUTILSSRCDIR}/local.mk.org ${COREUTILSSRCDIR}/local.mk > ${PATCHDIR}/local.mk.patch 21 | diff -U 10 ${CURRENTDIR}/${COREUTILS}/m4/cu-progs.m4.org ${CURRENTDIR}/${COREUTILS}/m4/cu-progs.m4 > ${PATCHDIR}/cu-progs.m4.patch 22 | -------------------------------------------------------------------------------- /src/copy.h.patch: -------------------------------------------------------------------------------- 1 | --- /home/ijntema/truenas/programming/securecopy/core-utils-based/V9.7.0/coreutils-9.7/src/copy.h.org 2025-08-14 14:54:55.261733000 +0200 2 | +++ /home/ijntema/truenas/programming/securecopy/core-utils-based/V9.7.0/coreutils-9.7/src/copy.h 2025-08-14 14:54:55.278003100 +0200 3 | @@ -112,20 +112,28 @@ 4 | following programs: mv (when rename doesn't work), cp, install. 5 | So, if you add a new member, be sure to initialize it in 6 | mv.c, cp.c, and install.c. */ 7 | struct cp_options 8 | { 9 | enum backup_type backup_type; 10 | 11 | /* How to handle symlinks in the source. */ 12 | enum Dereference_symlink dereference; 13 | 14 | + /* HIJN */ 15 | + /* Use CRC checksum to verify whether destination copy is same as source */ 16 | + bool crc; 17 | + 18 | + /* HIJN */ 19 | + /* As crc, nut use xattr to get (fresh) crc or if empty/stale, update crc */ 20 | + bool crc_with_xattr; 21 | + 22 | /* This value is used to determine whether to prompt before removing 23 | each existing destination file. It works differently depending on 24 | whether move_mode is set. See code/comments in copy.c. */ 25 | enum Interactive interactive; 26 | 27 | /* Control creation of sparse files. */ 28 | enum Sparse_type sparse_mode; 29 | 30 | /* Set the mode of the destination file to exactly this value 31 | if SET_MODE is nonzero. */ 32 | -------------------------------------------------------------------------------- /src/cu-progs.m4.patch: -------------------------------------------------------------------------------- 1 | --- /home/ijntema/truenas/programming/securecopy/core-utils-based/V9.7.0/coreutils-9.7/m4/cu-progs.m4.org 2025-08-14 14:54:55.289733400 +0200 2 | +++ /home/ijntema/truenas/programming/securecopy/core-utils-based/V9.7.0/coreutils-9.7/m4/cu-progs.m4 2025-08-14 14:54:55.295975300 +0200 3 | @@ -88,20 +88,23 @@ 4 | gl_ADD_PROG([optional_bin_progs], [tty]) 5 | gl_ADD_PROG([optional_bin_progs], [uname]) 6 | gl_ADD_PROG([optional_bin_progs], [unexpand]) 7 | gl_ADD_PROG([optional_bin_progs], [uniq]) 8 | gl_ADD_PROG([optional_bin_progs], [unlink]) 9 | gl_ADD_PROG([optional_bin_progs], [uptime]) 10 | gl_ADD_PROG([optional_bin_progs], [vdir]) 11 | gl_ADD_PROG([optional_bin_progs], [wc]) 12 | gl_ADD_PROG([optional_bin_progs], [whoami]) 13 | gl_ADD_PROG([optional_bin_progs], [yes]) 14 | +gl_ADD_PROG([optional_bin_progs], [crccp]) 15 | +gl_ADD_PROG([optional_bin_progs], [crcsum]) 16 | +gl_ADD_PROG([optional_bin_progs], [crcmv]) 17 | no_install_progs_default='arch coreutils hostname' 18 | # Given the name of a variable containing a space-separated 19 | # list of install-by-default programs and the actual list of 20 | # do-not-install-by-default programs, modify the former variable 21 | # to reflect any "do-install" and "don't-install" requests. 22 | # That is, add any program specified via --enable-install-program, 23 | # and remove any program specified via --enable-no-install-program. 24 | # Note how the second argument below is a literal, with "," 25 | # separators. That is required due to the way the macro works, 26 | # and since the corresponding ./configure option argument is 27 | -------------------------------------------------------------------------------- /src/crcsum.h: -------------------------------------------------------------------------------- 1 | #ifndef _CRCSUM_H 2 | #define _CRCSUM_H 1 3 | 4 | #include 5 | 6 | #define MAX_BUF_LEN (65536) 7 | #define RESET_TEXT() printf("\033[0;0m") 8 | 9 | #define RESET 0 10 | #define BRIGHT 1 11 | #define DIM 2 12 | #define UNDERLINE 3 13 | #define BLINK 4 14 | #define REVERSE 7 15 | #define HIDDEN 8 16 | 17 | #define BLACK 0 18 | #define RED 1 19 | #define GREEN 2 20 | #define YELLOW 3 21 | #define BLUE 4 22 | #define MAGENTA 5 23 | #define CYAN 6 24 | 25 | #define VERBOSE 0x01 26 | #define STORE 0x02 27 | #define CHECK 0x04 28 | #define REMOVE 0x10 29 | #define RECURSE 0x20 30 | #define OVERWRITE 0x40 31 | #define PRINT 0x80 32 | #define UPDATE 0x100 33 | #define DEBUG 0x200 34 | #define OMIT 0x400 35 | 36 | // For option -e OMIT 37 | #define MAXEXT 20 38 | 39 | 40 | typedef unsigned long long t_crc64; 41 | //typedef uint64_t t_crc64; 42 | 43 | struct s_crc64 44 | { 45 | t_crc64 crc; 46 | t_crc64 t; 47 | // time_t t; this is not constant size accross 32 and 64bit OSses, resulting in xattr errors when copying files 48 | }; 49 | 50 | t_crc64 CalculateCRC64(const char *filename); 51 | int processFile(const char *filename); 52 | int nftw_callback(const char *, const struct stat *, int, struct FTW *); 53 | int processCmdLine(const char *filename); 54 | uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l); 55 | t_crc64 getCRC(const char *filename); 56 | int presentCRC64(const char *filename); 57 | int removeCRC(const char *filename); 58 | int bPutCRC(const char *filename, t_crc64 *crc, bool bError); 59 | t_crc64 getCRC(const char *filename); 60 | void textcolor(int attr, int fg, int bg); 61 | void printStatus(const char* filename, const char* status, int fg); 62 | int IsPresentCRC64(const char *file); 63 | int IsFreshCRC(const char *filename); 64 | 65 | #endif //define _CRCSUM_H 66 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 2021-11-14 2 | Version 9.0.0 3 | 4 | - Upgraded to coreutils version 9.0 5 | - Changed version numbering. x.y.z: x.y refer to coreutils version. z to securecopy increment version. 6 | 7 | 2020-06-14 8 | version 8.32 (coreutiles) - 2.1.1 (securecopy) 9 | 10 | - Upgraded coreutils 11 | - Add -e flag (ignore extension) 12 | 13 | 2020-01-26 Hans IJntema 14 | 15 | version 8.31 (coreutils) - 2.0.7 (securecopy) 16 | 17 | - Fixed 2GB limitation on 32bit systems; 18 | This is tested on 32bit x86 19 | To be tested on Raspbian for Raspberry Pi 20 | Credits to Jim Cruise for identifying the issue and testing 21 | 22 | 23 | 2020-01-19 Hans IJntema 24 | 25 | version 8.31 (coreutils) - 2.0.6 (securecopy) 26 | 27 | - Updated README 28 | 29 | 30 | 2020-01-18 Hans IJntema 31 | 32 | version 8.31 (coreutils) - 2.0.5 (securecopy) 33 | 34 | - Updated README 35 | - Verified that crcmv does work. 36 | 37 | 38 | 2020-01-17 Hans IJntema 39 | 40 | version 8.31 (coreutils) - 2.0.4 (securecopy) 41 | 42 | - Fixed -cx argument parsing; 43 | - NOTE: crcmv -cx does not work currently; crccp -cx is fine 44 | 45 | 46 | 2020-01-14 Hans IJntema 47 | 48 | version 8.31 (coreutils) - 2.0.3 (securecopy) 49 | 50 | - Fixed compiler warnings on asprintf (credits Jim Cruse) 51 | 52 | 53 | 2020-01-11 Hans IJntema 54 | 55 | version 8.31 (coreutils) - 2.0.2 (securecopy) 56 | 57 | - Added securecopy version number to reporting (eg crccp --version) 58 | 59 | 60 | 2020-01-11 Hans IJntema 61 | 62 | version 8.31 (coreutils) - 2.0.1 (securecopy) 63 | 64 | - Fix -c argument parsing; 65 | 66 | 67 | 2020-01-01 Hans IJntema 68 | 69 | version 8.31 (coreutils) - 2.0.0 (securecopy) 70 | 71 | - Upgraded to be based on coreutils 8.31 72 | - Moved from sf.net to github 73 | 74 | -------------------------------------------------------------------------------- /src/local.mk.patch: -------------------------------------------------------------------------------- 1 | --- /home/ijntema/truenas/programming/securecopy/core-utils-based/V9.7.0/coreutils-9.7/src/local.mk.org 2025-08-14 14:54:55.265733000 +0200 2 | +++ /home/ijntema/truenas/programming/securecopy/core-utils-based/V9.7.0/coreutils-9.7/src/local.mk 2025-08-14 14:54:55.290584500 +0200 3 | @@ -217,27 +217,34 @@ 4 | src_who_LDADD = $(LDADD) 5 | src_whoami_LDADD = $(LDADD) 6 | src_yes_LDADD = $(LDADD) 7 | 8 | # Synonyms. Recall that Automake transliterates '[' and '/' to '_'. 9 | src___LDADD = $(src_test_LDADD) 10 | src_dir_LDADD = $(src_ls_LDADD) 11 | src_vdir_LDADD = $(src_ls_LDADD) 12 | src_chgrp_LDADD = $(src_chown_LDADD) 13 | 14 | +src_crccp_LDADD = $(LDADD) 15 | +src_crcmv_LDADD = $(LDADD) 16 | +src_crcsum_LDADD = $(LDADD) 17 | + 18 | src_cp_LDADD += $(copy_ldadd) 19 | src_ginstall_LDADD += $(copy_ldadd) 20 | src_mv_LDADD += $(copy_ldadd) 21 | 22 | src_mv_LDADD += $(remove_ldadd) 23 | src_rm_LDADD += $(remove_ldadd) 24 | 25 | +src_crccp_LDADD += $(copy_ldadd) 26 | +src_crcmv_LDADD += $(copy_ldadd) 27 | +src_crcmv_LDADD += $(remove_ldadd) 28 | # for eaccess, euidaccess 29 | copy_ldadd += $(EUIDACCESS_LIBGEN) 30 | remove_ldadd += $(EUIDACCESS_LIBGEN) 31 | src_sort_LDADD += $(EUIDACCESS_LIBGEN) 32 | src_test_LDADD += $(EUIDACCESS_LIBGEN) 33 | 34 | # for selinux use 35 | copy_ldadd += $(LIB_SELINUX) 36 | src_chcon_LDADD += $(LIB_SELINUX) 37 | src_ginstall_LDADD += $(LIB_SELINUX) 38 | @@ -342,20 +349,22 @@ 39 | `sed -n '/.*COPYRIGHT_YEAR = \([0-9][0-9][0-9][0-9]\) };/s//\1/p' \ 40 | $(top_srcdir)/lib/version-etc.c` 41 | 42 | selinux_sources = \ 43 | src/selinux.c \ 44 | src/selinux.h 45 | 46 | copy_sources = \ 47 | src/copy.c \ 48 | src/cp-hash.c \ 49 | + src/crcsum.c \ 50 | + src/crc64.c \ 51 | src/force-link.c \ 52 | src/force-link.h 53 | 54 | # Use 'ginstall' in the definition of PROGRAMS and in dependencies to avoid 55 | # confusion with the 'install' target. The install rule transforms 'ginstall' 56 | # to install before applying any user-specified name transformations. 57 | 58 | # Don't apply prefix transformations to libstdbuf shared lib 59 | # as that's not generally needed, and we need to reference the 60 | # name directly in LD_PRELOAD etc. In general it's surprising 61 | @@ -469,20 +478,24 @@ 62 | src_libcksum_vmull_a_CFLAGS = -march=armv8-a+crypto $(AM_CFLAGS) 63 | endif 64 | 65 | src_base64_SOURCES = src/basenc.c 66 | src_base64_CPPFLAGS = -DBASE_TYPE=64 $(AM_CPPFLAGS) 67 | src_base32_SOURCES = src/basenc.c 68 | src_base32_CPPFLAGS = -DBASE_TYPE=32 $(AM_CPPFLAGS) 69 | src_basenc_SOURCES = src/basenc.c 70 | src_basenc_CPPFLAGS = -DBASE_TYPE=42 $(AM_CPPFLAGS) 71 | 72 | +src_crccp_SOURCES = src/crccp.c $(copy_sources) $(selinux_sources) 73 | +src_crcmv_SOURCES = src/crcmv.c src/remove.c $(copy_sources) $(selinux_sources) 74 | +src_crcsum_SOURCES = src/crcsum_cli.c src/crcsum.c src/crc64.c 75 | + 76 | src_expand_SOURCES = src/expand.c src/expand-common.c 77 | src_unexpand_SOURCES = src/unexpand.c src/expand-common.c 78 | 79 | src_wc_SOURCES = src/wc.c 80 | if USE_AVX2_WC_LINECOUNT 81 | noinst_LIBRARIES += src/libwc_avx2.a 82 | src_libwc_avx2_a_SOURCES = src/wc_avx2.c 83 | wc_avx2_ldadd = src/libwc_avx2.a 84 | src_wc_LDADD += $(wc_avx2_ldadd) 85 | src_libwc_avx2_a_CFLAGS = -mavx2 $(AM_CFLAGS) 86 | -------------------------------------------------------------------------------- /test/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CRCPATH="../bin" 4 | CHECKSUM="BE2C65AC2D090191" 5 | #CHECKSUM="BE2C65AC2D090192" 6 | 7 | # Ensure local path, to guarantee that xattr works 8 | # For example, a TrueNAS samba share does not work 9 | TESTFILE="/tmp/fileundertest.txt" 10 | CPFILE="/tmp/copyfile.txt" 11 | 12 | echo "##################################################################################################" 13 | echo "##################################################################################################" 14 | echo "######################### Basic test for crcsum and crccp ######################################" 15 | echo "##################################################################################################" 16 | echo "##################################################################################################" 17 | 18 | rm -f ${TESTFILE} ${CPFILE} 19 | 20 | echo "TEST: Add CRC to file and compare with expected pre-calculated CRC" 21 | echo "Create test file without checksum..." 22 | dd if=<(yes foo) of=${TESTFILE} bs=1024 count=200 > /dev/null 2>&1 23 | 24 | echo "Add checksum..." 25 | ${CRCPATH}/crcsum -a ${TESTFILE} > /dev/null 2>&1 26 | echo "Display checksum..." 27 | OUTPUT=$(${CRCPATH}/crcsum -p ${TESTFILE}) > /dev/null 2>&1 28 | 29 | echo $OUTPUT 30 | 31 | # https://www.regextester.com/107384 32 | # Extract calculated checksum 33 | [[ ${OUTPUT} =~ (\[ )(.*)(.* \]) ]] 34 | CHECKSUM_CALCULATED=(${BASH_REMATCH[2]}) 35 | 36 | if [[ "$OUTPUT" =~ .*"$CHECKSUM".* ]]; then 37 | echo "SUCCESS: Checksum calculated = ${CHECKSUM_CALCULATED}; Checksum expected = ${CHECKSUM}" 38 | else 39 | echo "FAILURE: Checksum calculated = ${CHECKSUM_CALCULATED}; Checksum expected = ${CHECKSUM}" 40 | exit 1 41 | fi 42 | 43 | echo "##################################################################################################" 44 | echo "##################################################################################################" 45 | rm ${TESTFILE} 46 | echo "TEST: Copy file (crccp -cx) without source CRC and compare with expected pre-calculated CRC" 47 | echo "Create test file without checksum..." 48 | dd if=<(yes foo) of=${TESTFILE} bs=1024 count=200 > /dev/null 2>&1 49 | 50 | echo "Copy file and test checksum of destination..." 51 | ${CRCPATH}/crccp -cx -v ${TESTFILE} ${CPFILE} 52 | 53 | echo "Display checksum..." 54 | OUTPUT=$(${CRCPATH}/crcsum -p ${CPFILE}) 55 | 56 | echo ${OUTPUT} 57 | 58 | # Extract calculated checksum 59 | [[ ${OUTPUT} =~ (\[ )(.*)(.* \]) ]] 60 | CHECKSUM_CALCULATED=(${BASH_REMATCH[2]}) 61 | 62 | if [[ "$OUTPUT" =~ .*"$CHECKSUM".* ]]; then 63 | echo "SUCCESS: Checksum calculated = ${CHECKSUM_CALCULATED}; Checksum expected = ${CHECKSUM}" 64 | else 65 | echo "FAILURE: Checksum calculated = ${CHECKSUM_CALCULATED}; Checksum expected = ${CHECKSUM}" 66 | exit 1 67 | fi 68 | 69 | echo "##################################################################################################" 70 | echo "##################################################################################################" 71 | echo "TEST: File corruption test (crcsum -c)..." 72 | rm ${TESTFILE} 73 | dd if=<(yes foo) of=${TESTFILE} bs=1024 count=200 > /dev/null 2>&1 74 | 75 | # Add checksum to testfile 76 | ${CRCPATH}/crcsum -a ${TESTFILE} > /dev/null 2>&1 77 | OUTPUT=$(${CRCPATH}/crcsum -p ${TESTFILE}) 78 | 79 | echo "Simulate that ${TESTFILE} got corrupted..." 80 | # Change content of file but restore timestamp 81 | # Store timestamp 82 | touch -r ${TESTFILE} time.stamp > /dev/null 2>&1 83 | 84 | # Add 1 character 85 | echo "a" >> ${TESTFILE} 86 | touch -r time.stamp ${TESTFILE} > /dev/null 2>&1 87 | rm time.stamp > /dev/null 2>&1 88 | 89 | OUTPUT=$(${CRCPATH}/crcsum -c -v ${TESTFILE}) 90 | 91 | echo $OUTPUT 92 | 93 | # Extract calculated checksum 94 | #regexp="\[[[:space:]]+(.*)[[:space:]]*\].*" 95 | regexp=".*(FAILED|OK).*" 96 | [[ ${OUTPUT} =~ ${regexp} ]] 97 | REMATCH=(${BASH_REMATCH[1]}) 98 | 99 | if [[ "$REMATCH" =~ .*"FAILED".* ]]; then 100 | echo "Success: corrupted file detected" 101 | else 102 | echo "Failure: corrupted file not detected" 103 | exit 1 104 | fi 105 | 106 | echo "##################################################################################################" 107 | echo "##################################################################################################" 108 | echo "TEST: Copy corrupted file (crccp -cx)..." 109 | echo "Copy file and test checksum of destination..." 110 | OUTPUT=$(${CRCPATH}/crccp -cx ${TESTFILE} ${CPFILE}) 111 | ${CRCPATH}/crccp -cx -v ${TESTFILE} ${CPFILE} 112 | 113 | regexp=".*(WARNING).*" 114 | [[ ${OUTPUT} =~ ${regexp} ]] 115 | REMATCH=(${BASH_REMATCH[1]}) 116 | 117 | if [[ "$REMATCH" =~ .*"WARNING".* ]]; then 118 | echo "Success: corrupted file while copying detected" 119 | else 120 | echo "Failure: corrupted file not detected" 121 | exit 1 122 | fi 123 | 124 | rm -f ${TESTFILE} ${CPFILE} 125 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SecureCopy 2 | `crccp` and `crcmv` are based on GNU Core Utilities and add CRC checksum functionality to cp and mv commands. 3 | Checksums can optionally be stored in the file's xattr, provided that file is not read only and that filesystem supports xattr. 4 | Checksum is generated on source file and verified against destination file 5 | 6 | The command `crcsum` can be used for further checksum analysis/verification, or for storing a checksum in the xattr of all files in a directory or filesystem. 7 | I mount my filesystems with `mount -t ext4 -o noatime -o user_xattr ` or specify user_xattr in `/etc/fstab` under options. 8 | 9 | A use case: 10 | - I run `crcsum -u -r .` on my home directory before daily backup; I have xattr enabled on my filesystems, checksums are embedded in all files and stored in the backup. 11 | - I run once a month (cronjob) `crcsum -c -r ` to verify if backup is healthy 12 | - And yes, I do run once or twice a year into a corrupt file (bit rot?); ofcourse your luck varies with quality of HD's and size of your backups 13 | 14 | ## Getting Started 15 | This package is tested on Debian Bookworm (AMD64) 16 | 17 | ### Prerequisites 18 | * Test if the plain-vanilla [GNU Core Utilities 9.7](https://ftp.gnu.org/gnu/coreutils/coreutils-9.7.tar.xz) can be build and fix any build issues 19 | * No other dependencies beyond GNU Core Utilities dependencies. 20 | 21 | ### Installing 22 | * Download securecopy sources 23 | * Adapt `TARGETDIR="/opt"` in `build-secure-copy`, point to location where `crccp`, `crcmv` and `crcsum` should be located 24 | * `sudo ./build-secure-copy`: this will download coreutils package, apply patch files, build and copy target files to "TARGETDIR" 25 | * The other GNU Core Utilities tools are not installed! 26 | * "build-secure-copy" works for Debian OS-ses; adapt for other distributions (specifically, install required packages) 27 | 28 | ## Deployment 29 | Add to `~/.bashrc:` 30 | * `alias cp='TARGETDIR/crccp -cx'` 31 | * `alias mv='TARGETDIR/crcmv -cx'` 32 | * `alias crcsum='TARGETDIR/crcsum'` 33 | * (replace TARGETDIR with actual location) 34 | 35 | ## Usage 36 | * Assumption is that aliasses have been set, otherwise use iso e.g. `cp`, `crccp`. 37 | * `cp -c sourcefile destination` # will re-use crc stored with sourcefile (if any) 38 | * `cp -cx sourcefile destination` # will re-created crc of sourcefile befie copy is started 39 | 40 | ## Versioning 41 | * V9.7.0: based on GNU Core Utilities v9.7; patches provided by [p1r473](https://github.com/p1r473). Minor changes implemented in verbose printing 42 | * V9.3.0: based on GNU Core Utilities v9.3; Added simple test 43 | * V9.0.0: based on GNU Core Utilities v9.0; First 2 decimals of version number refer to GNU Core Utilities version 44 | * V2.1: based on GNU Core Utilities v8.32 45 | * V2.0: based on GNU Core Utilities v8.31 46 | 47 | ## Authors 48 | Hans IJntema 49 | 50 | CRC64 routine: 51 | The checksum routine is the crc-64-jones created by Salvatore Sanfilippo. 52 | Coreutils authors: see coreutils documentation. 53 | 54 | ## License 55 | This project is licensed under the GNU General Public License 56 | 57 | ## Details 58 | ``` 59 | crcsum -h 60 | CRCSUM stores a CRC checksum and file mtime as an extended attribute. 61 | CRCSUM checks stored CRC checksum against actual CRC checksum 62 | CRCSUM allows for CRC updates, only when mtime has changed 63 | ZERO size files will not be checksum-ed. 64 | Directory & File SYMLINKS will not be followed. 65 | NOTE: Some programs do change file content (even w/o saving), 66 | while mtime is not update. Example, some Microsoft Excel versions 67 | This will result in false positves when checking file against CRC. 68 | ``` 69 | 70 | ``` 71 | Options : 72 | -a Calculate and Add checksum (no overwrite) 73 | -u As -a, and Update stale checksum 74 | -f As -a, Force overwrite existing checksum 75 | -c Check file against stored checksum; stale CRC's are omitted 76 | -e Assumes -c; omit files with specified extension; one extension per option; eg -e xls -e xlt 77 | -p Print CRC64 checksum; stale CRC's are omitted; Add -d to print stale and missing CRC's 78 | -v Verbose. Print more information 79 | -x Remove stored CRC64 checksum 80 | -r Recurse through directories 81 | -d Print Debug info. Implies -v 82 | ``` 83 | 84 | ``` 85 | Relevant additional or extended flags for crccp and crcmv (compared to cp, mv) 86 | -c, --crc Check whether copy was successfull with checksum comparison 87 | -cx, --crc=x As -c and checksum is stored in xattr of src and dest file (if file is writeable and if filesystem supports xattr); 88 | Stored crc can be used in a next cp/mv or integrity check with crcsum...bitrot check); 89 | Implies --preserve=all 90 | -v, --verbose Explain what is being done and display crc's created 91 | ``` 92 | After a copy or move, integrity of file can be checked (again) with `crcsum -c -r \` 93 | If a file with crc stored in its xattr has been changed afterwards, the crc is flagged as stale, and ignored (based on timestamps) 94 | Note: Microsoft Excel files (on Windows) are known to change the file content but not the timestamp if you just open the excel file for reading and close without saving. You can use `crcsum -e xls` to ignore Excel files. 95 | -------------------------------------------------------------------------------- /src/crcsum_cli.c: -------------------------------------------------------------------------------- 1 | /* 2 | This program is free software: you can redistribute it and/or modify 3 | it under the terms of the GNU General Public License as published by 4 | the Free Software Foundation, version 3 of the License. 5 | 6 | This program is distributed in the hope that it will be useful, 7 | but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | GNU General Public License for more details. 10 | 11 | You should have received a copy of the GNU General Public License 12 | along with this program. If not, see . 13 | 14 | This software is based on Checkit version 0.2.0 15 | Dennis Katsonis; E-Mail dennisk (at) netspace.net.au 16 | */ 17 | 18 | #include "config.h" 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | // Provides: 26 | // extern char *optarg; 27 | // extern int optind, opterr, optopt; 28 | 29 | #include 30 | #include "crcsum.h" 31 | 32 | void print_help(void); 33 | 34 | extern int flags; 35 | extern int failed; 36 | extern int processed; 37 | extern int ext_counter; 38 | extern char arr_ext[MAXEXT][5]; 39 | 40 | 41 | void print_help(void) 42 | { 43 | printf("CRCSUM: A file checksum utility.\tVersion : %s\n",VERSION); 44 | puts("(C) Hans IJntema (2023)"); 45 | puts(""); 46 | puts("CRCSUM stores a CRC checksum and file mtime as an extended attribute."); 47 | puts("CRCSUM checks stored CRC checksum against actual CRC checksum"); 48 | puts("CRCSUM allows for CRC updates, only when mtime has changed"); 49 | puts("ZERO size files will not be checksum-ed."); 50 | puts("Directory & File SYMLINKS will not be followed."); 51 | puts("NOTE: Some programs do change file content (even w/o saving),"); 52 | puts("while mtime is not update. Example, some Microsoft Excel versions"); 53 | puts("This will result in false positves when checking file against CRC."); 54 | puts(""); 55 | puts("Options :"); 56 | puts(" -a Calculate and Add checksum (no overwrite)"); 57 | puts(" -u As -a, and Update stale checksum"); 58 | puts(" -f As -a, Force overwrite existing checksum"); 59 | puts(" -c Check file against stored checksum; stale CRC's are omitted"); 60 | puts(" -e Assumes -c; omit extension; one extension per option; eg -c -e xls -e xlt"); 61 | puts(" -p Print CRC64 checksum; stale CRC's are omitted; Add -d to print stale and missing CRC's"); 62 | puts(" -v Verbose. Print more information"); 63 | puts(" -x Remove stored CRC64 checksum"); 64 | puts(" -r Recurse through directories"); 65 | puts(" -d Print Debug info. Implies -v"); 66 | puts(""); 67 | printf("Report errors and requests at %s\n\n", PACKAGE_BUGREPORT); 68 | } 69 | 70 | 71 | int main(int argc, char *argv[]) 72 | { 73 | int optch = 0; 74 | extern char *optarg; 75 | 76 | // get option characters (returns -1 if all are retrieved) 77 | while (( optch = getopt(argc, argv,"ahe:ufcpvxrd") ) != -1) 78 | { 79 | switch (optch) 80 | { 81 | case 'h' : 82 | print_help(); 83 | return false; 84 | break; 85 | 86 | case 'a' : 87 | flags |= STORE; 88 | break; 89 | 90 | case 'c' : 91 | flags |= CHECK; 92 | break; 93 | 94 | case 'e' : 95 | flags |= OMIT; 96 | flags |= CHECK; 97 | if (ext_counter < MAXEXT) 98 | { 99 | strcpy(arr_ext[ext_counter++], optarg); 100 | } 101 | break; 102 | 103 | case 'v' : 104 | flags |= VERBOSE; 105 | break; 106 | 107 | case 'x' : 108 | flags |= REMOVE; 109 | break; 110 | 111 | case 'u' : 112 | flags |= UPDATE; 113 | break; 114 | 115 | case 'f' : 116 | flags |= OVERWRITE; 117 | break; 118 | 119 | case 'r' : 120 | flags |= RECURSE; 121 | break; 122 | 123 | case 'p' : 124 | flags |= PRINT; 125 | break; 126 | 127 | case 'd' : 128 | flags |= VERBOSE; 129 | flags |= DEBUG; 130 | break; 131 | 132 | case '?' : 133 | puts("Unknown option."); 134 | puts(""); 135 | print_help(); 136 | break; 137 | } //switch 138 | } //while 139 | 140 | 141 | /* for (int i = 0; i < ext_counter; i++) 142 | { 143 | printf("arg=%s\n", arr_ext[i]); 144 | }*/ 145 | 146 | 147 | // no arguments are passed 148 | if (argc <=1) 149 | { 150 | print_help(); 151 | return(0); 152 | } 153 | /* Check for conflicting options */ 154 | if (((flags & CHECK) && (flags & STORE)) || ((flags & CHECK) && (flags & OVERWRITE)) || ((flags & CHECK) && (flags & UPDATE)) ) 155 | { 156 | puts("Cannot store and check CRC at same time."); 157 | return EX_USAGE; 158 | } 159 | 160 | if ((flags & STORE) && (flags & OVERWRITE) && (flags & UPDATE)) 161 | { 162 | puts("Cannot store/update and refresh at same time."); 163 | return EX_USAGE; 164 | } 165 | 166 | if ((flags & STORE) && (flags & REMOVE)) 167 | { 168 | puts("Cannot remove and store CRC at same time."); 169 | return EX_USAGE; 170 | } 171 | if ((flags & CHECK) && (flags & REMOVE)) 172 | { 173 | puts("Cannot remove and check CRC at same time."); 174 | return EX_USAGE; 175 | } 176 | 177 | //optind is the index of the next element to be processed in argv 178 | optch = optind; 179 | 180 | if (optch < argc) 181 | { 182 | do 183 | { 184 | // printf("ARGUMENT = %s\n", argv[optch]); 185 | // processFile(argv[optch]); 186 | processCmdLine(argv[optch]); 187 | } 188 | while ( ++optch < argc); 189 | } 190 | 191 | printf("%d file(s) processed.\n", processed); 192 | if (failed && processed) 193 | { 194 | printf("%d file(s) failed.\n", failed); 195 | return EXIT_FAILURE; 196 | } /* Return the number of failed checks if any errors. */ 197 | else if ( processed && (flags & CHECK) ) 198 | { 199 | printf("All file(s) OK.\n"); 200 | } 201 | return EXIT_SUCCESS; 202 | } 203 | 204 | 205 | -------------------------------------------------------------------------------- /src/crcmv.c.patch: -------------------------------------------------------------------------------- 1 | --- /home/ijntema/truenas/programming/securecopy/core-utils-based/V9.7.0/coreutils-9.7/src/mv.c 2025-01-07 18:31:45.000000000 +0100 2 | +++ /home/ijntema/truenas/programming/securecopy/core-utils-based/V9.7.0/coreutils-9.7/src/crcmv.c 2025-08-14 16:12:58.382368500 +0200 3 | @@ -28,22 +28,25 @@ 4 | #include "backupfile.h" 5 | #include "copy.h" 6 | #include "cp-hash.h" 7 | #include "filenamecat.h" 8 | #include "remove.h" 9 | #include "renameatu.h" 10 | #include "root-dev-ino.h" 11 | #include "targetdir.h" 12 | #include "priv-set.h" 13 | 14 | +/* HIJN */ 15 | +#include "crcsum.h" 16 | + 17 | /* The official name of this program (e.g., no 'g' prefix). */ 18 | -#define PROGRAM_NAME "mv" 19 | +#define PROGRAM_NAME "crcmv" 20 | 21 | #define AUTHORS \ 22 | proper_name ("Mike Parker"), \ 23 | proper_name ("David MacKenzie"), \ 24 | proper_name ("Jim Meyering") 25 | 26 | /* For long options that have no equivalent short option, use a 27 | non-character as a pseudo short option, starting with CHAR_MAX + 1. */ 28 | enum 29 | { 30 | @@ -59,20 +62,21 @@ 31 | }; 32 | static enum Update_type const update_type[] = 33 | { 34 | UPDATE_ALL, UPDATE_NONE, UPDATE_NONE_FAIL, UPDATE_OLDER, 35 | }; 36 | ARGMATCH_VERIFY (update_type_string, update_type); 37 | 38 | static struct option const long_options[] = 39 | { 40 | {"backup", optional_argument, nullptr, 'b'}, 41 | + {"crc", optional_argument, nullptr, 'c'}, 42 | {"context", no_argument, nullptr, 'Z'}, 43 | {"debug", no_argument, nullptr, DEBUG_OPTION}, 44 | {"exchange", no_argument, nullptr, EXCHANGE_OPTION}, 45 | {"force", no_argument, nullptr, 'f'}, 46 | {"interactive", no_argument, nullptr, 'i'}, 47 | {"no-clobber", no_argument, nullptr, 'n'}, /* Deprecated. */ 48 | {"no-copy", no_argument, nullptr, NO_COPY_OPTION}, 49 | {"no-target-directory", no_argument, nullptr, 'T'}, 50 | {"strip-trailing-slashes", no_argument, nullptr, 51 | STRIP_TRAILING_SLASHES_OPTION}, 52 | @@ -117,20 +121,22 @@ 53 | x->preserve_all_root = false; 54 | } 55 | 56 | static void 57 | cp_option_init (struct cp_options *x) 58 | { 59 | bool selinux_enabled = (0 < is_selinux_enabled ()); 60 | 61 | cp_options_default (x); 62 | x->copy_as_regular = false; /* FIXME: maybe make this an option */ 63 | + x->crc = false; 64 | + x->crc_with_xattr = false; 65 | x->reflink_mode = REFLINK_AUTO; 66 | x->dereference = DEREF_NEVER; 67 | x->unlink_dest_before_opening = false; 68 | x->unlink_dest_after_failed_open = false; 69 | x->hard_link = false; 70 | x->interactive = I_UNSPECIFIED; 71 | x->move_mode = true; 72 | x->install_mode = false; 73 | x->one_file_system = false; 74 | x->preserve_ownership = true; 75 | @@ -263,20 +269,28 @@ 76 | fputs (_("\ 77 | Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n\ 78 | "), stdout); 79 | 80 | emit_mandatory_arg_note (); 81 | 82 | fputs (_("\ 83 | --backup[=CONTROL] make a backup of each existing destination file\ 84 | \n\ 85 | -b like --backup but does not accept an argument\n\ 86 | + -c --crc check whether move was successfull with checksum comparison\n\ 87 | + checksum is calculated during move\n\ 88 | + checksum is not stored in xattr or compared against xattr checksum\n\ 89 | + -cx --crc=x use checksum stored in xattr or if not present or stale,\n\ 90 | + store/update checksum in xattr of src and des\n\ 91 | + See also tool in same secure copy package\n\ 92 | + implies --preserve=all\n\ 93 | + verbose flag is supported\n\ 94 | "), stdout); 95 | fputs (_("\ 96 | --debug explain how a file is copied. Implies -v\n\ 97 | "), stdout); 98 | fputs (_("\ 99 | --exchange exchange source and destination\n\ 100 | "), stdout); 101 | fputs (_("\ 102 | -f, --force do not prompt before overwriting\n\ 103 | -i, --interactive prompt before overwrite\n\ 104 | @@ -334,30 +348,58 @@ 105 | bindtextdomain (PACKAGE, LOCALEDIR); 106 | textdomain (PACKAGE); 107 | 108 | atexit (close_stdin); 109 | 110 | cp_option_init (&x); 111 | 112 | /* Try to disable the ability to unlink a directory. */ 113 | priv_set_remove_linkdir (); 114 | 115 | - while ((c = getopt_long (argc, argv, "bfint:uvS:TZ", long_options, nullptr)) 116 | + while ((c = getopt_long (argc, argv, "bc::fint:uvS:TZ", long_options, nullptr)) 117 | != -1) 118 | { 119 | switch (c) 120 | { 121 | case 'b': 122 | make_backups = true; 123 | if (optarg) 124 | version_control_string = optarg; 125 | break; 126 | + case 'c': 127 | + if (optarg) 128 | + { 129 | + if ( strcmp( optarg, "x") == 0 ) 130 | + { 131 | +// x.crc = false; 132 | +// x.crc_with_xattr = true; 133 | + x.crc = true; 134 | + x.crc_with_xattr = false; 135 | + 136 | + x.preserve_mode = true; 137 | + x.preserve_timestamps = true; 138 | + x.preserve_ownership = true; 139 | + x.preserve_links = true; 140 | + x.explicit_no_preserve_mode = false; 141 | + if (selinux_enabled) 142 | + x.preserve_security_context = true; 143 | + x.preserve_xattr = true; 144 | + } 145 | + } 146 | + else 147 | + { 148 | +// x.crc = true; 149 | +// x.crc_with_xattr = false; 150 | + x.crc = false; 151 | + x.crc_with_xattr = true; 152 | + } 153 | + break; 154 | case 'f': 155 | x.interactive = I_ALWAYS_YES; 156 | break; 157 | case 'i': 158 | x.interactive = I_ASK_USER; 159 | break; 160 | case 'n': 161 | x.interactive = I_ALWAYS_SKIP; 162 | break; 163 | case DEBUG_OPTION: 164 | -------------------------------------------------------------------------------- /src/crccp.c.patch: -------------------------------------------------------------------------------- 1 | --- /home/ijntema/truenas/programming/securecopy/core-utils-based/V9.7.0/coreutils-9.7/src/cp.c 2025-01-07 18:31:45.000000000 +0100 2 | +++ /home/ijntema/truenas/programming/securecopy/core-utils-based/V9.7.0/coreutils-9.7/src/crccp.c 2025-08-14 16:12:33.103554700 +0200 3 | @@ -30,26 +30,28 @@ 4 | #include "cp-hash.h" 5 | #include "filenamecat.h" 6 | #include "ignore-value.h" 7 | #include "quote.h" 8 | #include "stat-time.h" 9 | #include "targetdir.h" 10 | #include "utimens.h" 11 | #include "acl.h" 12 | 13 | /* The official name of this program (e.g., no 'g' prefix). */ 14 | -#define PROGRAM_NAME "cp" 15 | +/* HIJN */ 16 | +#define PROGRAM_NAME "crccp" 17 | 18 | #define AUTHORS \ 19 | proper_name_lite ("Torbjorn Granlund", "Torbj\303\266rn Granlund"), \ 20 | proper_name ("David MacKenzie"), \ 21 | - proper_name ("Jim Meyering") 22 | + proper_name ("Jim Meyering"), \ 23 | + proper_name ("Checksum functionality only: Hans IJntema") 24 | 25 | /* Used by do_copy, make_dir_parents_private, and re_protect 26 | to keep a list of leading directories whose protections 27 | need to be fixed after copying. */ 28 | struct dir_attr 29 | { 30 | struct stat st; 31 | bool restore_mode; 32 | size_t slash_offset; 33 | struct dir_attr *next; 34 | @@ -111,20 +113,22 @@ 35 | UPDATE_ALL, UPDATE_NONE, UPDATE_NONE_FAIL, UPDATE_OLDER, 36 | }; 37 | ARGMATCH_VERIFY (update_type_string, update_type); 38 | 39 | static struct option const long_opts[] = 40 | { 41 | {"archive", no_argument, nullptr, 'a'}, 42 | {"attributes-only", no_argument, nullptr, ATTRIBUTES_ONLY_OPTION}, 43 | {"backup", optional_argument, nullptr, 'b'}, 44 | {"copy-contents", no_argument, nullptr, COPY_CONTENTS_OPTION}, 45 | + /* HIJN */ 46 | + {"crc", optional_argument, nullptr, 'c'}, 47 | {"debug", no_argument, nullptr, DEBUG_OPTION}, 48 | {"dereference", no_argument, nullptr, 'L'}, 49 | {"force", no_argument, nullptr, 'f'}, 50 | {"interactive", no_argument, nullptr, 'i'}, 51 | {"link", no_argument, nullptr, 'l'}, 52 | {"no-clobber", no_argument, nullptr, 'n'}, /* Deprecated. */ 53 | {"no-dereference", no_argument, nullptr, 'P'}, 54 | {"no-preserve", required_argument, nullptr, NO_PRESERVE_ATTRIBUTES_OPTION}, 55 | {"no-target-directory", no_argument, nullptr, 'T'}, 56 | {"one-file-system", no_argument, nullptr, 'x'}, 57 | @@ -169,20 +173,28 @@ 58 | 59 | emit_mandatory_arg_note (); 60 | 61 | fputs (_("\ 62 | -a, --archive same as -dR --preserve=all\n\ 63 | --attributes-only don't copy the file data, just the attributes\n\ 64 | --backup[=CONTROL] make a backup of each existing destination file\ 65 | \n\ 66 | -b like --backup but does not accept an argument\n\ 67 | --copy-contents copy contents of special files when recursive\n\ 68 | + -c --crc check whether copy was successfull with checksum comparison\n\ 69 | + checksum is calculated during copy\n\ 70 | + checksum is not stored in xattr or compared against xattr checksum\n\ 71 | + -cx --crc=x use checksum stored in xattr or if not present or stale,\n\ 72 | + store/update checksum in xattr of src and des\n\ 73 | + See also tool in same secure copy package\n\ 74 | + implies --preserve=all\n\ 75 | + verbose flag is supported\n\ 76 | -d same as --no-dereference --preserve=links\n\ 77 | "), stdout); 78 | fputs (_("\ 79 | --debug explain how a file is copied. Implies -v\n\ 80 | "), stdout); 81 | fputs (_("\ 82 | -f, --force if an existing destination file cannot be\n\ 83 | opened, remove it and try again (this option\n\ 84 | is ignored when the -n option is also used)\n\ 85 | -i, --interactive prompt before overwrite (overrides a previous -n\ 86 | @@ -823,20 +835,24 @@ 87 | } 88 | 89 | return ok; 90 | } 91 | 92 | static void 93 | cp_option_init (struct cp_options *x) 94 | { 95 | cp_options_default (x); 96 | x->copy_as_regular = true; 97 | + /* HIJN */ 98 | + x->crc = false; 99 | + x->crc_with_xattr = false; 100 | + /* END HIJN */ 101 | x->dereference = DEREF_UNDEFINED; 102 | x->unlink_dest_before_opening = false; 103 | x->unlink_dest_after_failed_open = false; 104 | x->hard_link = false; 105 | x->interactive = I_UNSPECIFIED; 106 | x->move_mode = false; 107 | x->install_mode = false; 108 | x->one_file_system = false; 109 | x->reflink_mode = REFLINK_AUTO; 110 | 111 | @@ -989,21 +1005,22 @@ 112 | set_program_name (argv[0]); 113 | setlocale (LC_ALL, ""); 114 | bindtextdomain (PACKAGE, LOCALEDIR); 115 | textdomain (PACKAGE); 116 | 117 | atexit (close_stdin); 118 | 119 | selinux_enabled = (0 < is_selinux_enabled ()); 120 | cp_option_init (&x); 121 | 122 | - while ((c = getopt_long (argc, argv, "abdfHilLnprst:uvxPRS:TZ", 123 | + /* HIJN */ 124 | + while ((c = getopt_long (argc, argv, "abc::dfHilLnprst:uvxPRS:TZ", 125 | long_opts, nullptr)) 126 | != -1) 127 | { 128 | switch (c) 129 | { 130 | case SPARSE_OPTION: 131 | x.sparse_mode = XARGMATCH ("--sparse", optarg, 132 | sparse_type_string, sparse_type); 133 | break; 134 | 135 | @@ -1029,20 +1046,50 @@ 136 | x.reduce_diagnostics = true; 137 | x.recursive = true; 138 | break; 139 | 140 | case 'b': 141 | make_backups = true; 142 | if (optarg) 143 | version_control_string = optarg; 144 | break; 145 | 146 | + /* HIJN */ 147 | + case 'c': 148 | + if (optarg) 149 | + { 150 | + if ( strcmp( optarg, "x") == 0 ) 151 | + { 152 | + // flag: -cx 153 | + x.crc = false; 154 | + x.crc_with_xattr = true; 155 | + 156 | + // will this stick if --no-preserve=xattr? 157 | + x.preserve_mode = true; 158 | + x.preserve_timestamps = true; 159 | + x.preserve_ownership = true; 160 | + x.preserve_links = true; 161 | + x.explicit_no_preserve_mode = false; 162 | + if (selinux_enabled) 163 | + x.preserve_security_context = true; 164 | + x.preserve_xattr = true; 165 | + } 166 | + } 167 | + else // commandline option = -cx 168 | + { 169 | + // flag: -c 170 | + x.crc = true; 171 | + x.crc_with_xattr = false; 172 | + } 173 | + break; 174 | + /* END HIJN */ 175 | + 176 | case ATTRIBUTES_ONLY_OPTION: 177 | x.data_copy_required = false; 178 | break; 179 | 180 | case DEBUG_OPTION: 181 | x.debug = x.verbose = true; 182 | break; 183 | 184 | case COPY_CONTENTS_OPTION: 185 | copy_contents = true; 186 | -------------------------------------------------------------------------------- /src/copy.c.patch: -------------------------------------------------------------------------------- 1 | --- /home/ijntema/truenas/programming/securecopy/core-utils-based/V9.7.0/coreutils-9.7/src/copy.c.org 2025-08-14 14:54:55.275265300 +0200 2 | +++ /home/ijntema/truenas/programming/securecopy/core-utils-based/V9.7.0/coreutils-9.7/src/copy.c 2025-08-14 14:54:55.275035200 +0200 3 | @@ -55,20 +55,22 @@ 4 | #include "same.h" 5 | #include "savedir.h" 6 | #include "stat-size.h" 7 | #include "stat-time.h" 8 | #include "utimecmp.h" 9 | #include "utimens.h" 10 | #include "write-any-file.h" 11 | #include "areadlink.h" 12 | #include "yesno.h" 13 | #include "selinux.h" 14 | +/* HIJN */ 15 | +#include "crcsum.h" 16 | 17 | #ifndef USE_XATTR 18 | # define USE_XATTR false 19 | #endif 20 | 21 | #if USE_XATTR 22 | # include 23 | # include 24 | # include 25 | #endif 26 | @@ -1241,28 +1243,116 @@ 27 | int dest_desc; 28 | int dest_errno; 29 | int source_desc; 30 | mode_t extra_permissions; 31 | struct stat sb; 32 | struct stat src_open_sb; 33 | union scan_inference scan_inference; 34 | bool return_val = true; 35 | bool data_copy_required = x->data_copy_required; 36 | bool preserve_xattr = USE_XATTR & x->preserve_xattr; 37 | + /* HIJN */ 38 | + t_crc64 crc_src = 0; 39 | 40 | copy_debug.offload = COPY_DEBUG_UNKNOWN; 41 | copy_debug.reflink = x->reflink_mode ? COPY_DEBUG_UNKNOWN : COPY_DEBUG_NO; 42 | copy_debug.sparse_detection = COPY_DEBUG_UNKNOWN; 43 | 44 | source_desc = open (src_name, 45 | (O_RDONLY | O_BINARY 46 | | (x->dereference == DEREF_NEVER ? O_NOFOLLOW : 0))); 47 | + 48 | +/* HIJN */ 49 | +// Checksum 50 | + 51 | + // Verbose print 52 | + if ( ( (x->crc) || (x->crc_with_xattr) ) && (x->verbose) ) 53 | + { 54 | + printf("SOURCE: [%s]\n", src_name); 55 | + } 56 | + 57 | + if (x->crc) 58 | + { 59 | + // flag: -c; calculate checksum w/o xattr 60 | + crc_src = CalculateCRC64(src_name); 61 | + 62 | + if (x->verbose) 63 | + { 64 | + char* szCRC; 65 | + int ret; 66 | + ret = asprintf(&szCRC, "%016llX", crc_src); 67 | + 68 | + if ( ret != -1) 69 | + { 70 | + printf("SOURCE: CRC [%s] is freshly calculated\n", szCRC); 71 | + } 72 | + } 73 | + } 74 | + 75 | + if (x->crc_with_xattr) 76 | + { 77 | + // flag: -cx; use xattr (if possible) 78 | + 79 | + // Check if xattr CRC is fresh 80 | + // Update CRC in xattr when current one is stale or not present 81 | + if ( !IsFreshCRC(src_name) ) 82 | + { 83 | + // CRC is stale or non-existent 84 | + // suppress printing errors by bPutCRC when writing CRC in source xattr, 85 | + // as file can be read-only or filesystem might not support xattr 86 | + // which is a legitimate use case. 87 | + bool bError = false; 88 | + 89 | + // Get new CRC and store in xattr 90 | + crc_src = 0; 91 | + if ( !bPutCRC(src_name, &crc_src, bError) ) 92 | + { 93 | + // bPutCRC was not successfull 94 | + // Don't print as error, but as warning as source file 95 | + // can be a read only file or file system does not support 96 | + printStatus(src_name, "WARNING: FAILED to store CRC in xattr. File is read-only or filesystem does not support xattr?", YELLOW); 97 | + } 98 | + else if (x->verbose) 99 | + { 100 | + char* szCRC; 101 | + int ret; 102 | + ret = asprintf(&szCRC, "%016llX", crc_src); 103 | + 104 | + if ( ret != -1) 105 | + { 106 | + textcolor(BRIGHT,YELLOW,BLACK); 107 | + printf("SOURCE: CRC [%s] is stored in xattr\n", szCRC); 108 | + RESET_TEXT(); 109 | + } 110 | + } 111 | + } 112 | + 113 | + // CRC is available in xattr and is fresh 114 | + else 115 | + { 116 | + crc_src = getCRC(src_name); 117 | + 118 | + if (x->verbose) 119 | + { 120 | + char* szCRC; 121 | + int ret; 122 | + ret = asprintf(&szCRC, "%016llX", crc_src); 123 | + 124 | + textcolor(BRIGHT,YELLOW,BLACK); 125 | + printf("SOURCE: CRC [%s] is retrieved from xattr\n", szCRC); 126 | + RESET_TEXT(); 127 | + } 128 | + } 129 | + 130 | + } 131 | + /* END HIJN */ 132 | + 133 | if (source_desc < 0) 134 | { 135 | error (0, errno, _("cannot open %s for reading"), quoteaf (src_name)); 136 | return false; 137 | } 138 | 139 | if (fstat (source_desc, &src_open_sb) != 0) 140 | { 141 | error (0, errno, _("cannot fstat %s"), quoteaf (src_name)); 142 | return_val = false; 143 | @@ -1712,20 +1802,172 @@ 144 | error (0, errno, _("failed to close %s"), quoteaf (dst_name)); 145 | return_val = false; 146 | } 147 | close_src_desc: 148 | if (close (source_desc) < 0) 149 | { 150 | error (0, errno, _("failed to close %s"), quoteaf (src_name)); 151 | return_val = false; 152 | } 153 | 154 | + /* HIJN */ 155 | + // Checksum 156 | + // Execute only if copy action itself is successfull 157 | + 158 | + // Verbose print 159 | + if (((x->crc) || (x->crc_with_xattr)) && (x->verbose)) 160 | + { 161 | + printf("DESTINATION: [%s]\n", dst_name); 162 | + } 163 | + 164 | + if ( ( return_val != false ) && ( ( x->crc ) || (x->crc_with_xattr) ) ) 165 | + { 166 | + t_crc64 crc_des = 0; 167 | + 168 | + // ONLY for testing 169 | + // 170 | + // printf("WAIT\n"); 171 | + // getchar(); 172 | + 173 | + // Calculate CRC of destination file 174 | + crc_des = CalculateCRC64(dst_name); 175 | + 176 | + if (x->verbose) 177 | + { 178 | + char* szCRC; 179 | + int ret; 180 | + ret = asprintf(&szCRC, "%016llX", crc_des); 181 | + if ( ret != -1) 182 | + { 183 | + printf("DESTINATION: CRC [%s] is freshly calculated\n", szCRC); 184 | + } 185 | + } 186 | + 187 | + // Compare CRC destination and source 188 | + if (crc_des != crc_src) 189 | + { 190 | + // CRC destination <> source 191 | + // But, we will do some extra checking before raising an error 192 | + 193 | + if (!x->verbose) 194 | + { 195 | + printf("%s]\n", src_name); 196 | + } 197 | + 198 | + // when using crc from stored xattr, it can happen that in very specific cases 199 | + // that src_file CRC is not flagged as stale, but file did change. 200 | + // Either by disk faults (file corruption --> error) 201 | + // or file has been changed w/o updating mtime (irritating, but file is not corrupt) 202 | + // Example: Office 2003 is known to update Excel files when reading, even when they are not explicitely saved 203 | + // Office 2003 does update an internal atime in the excel file w/o updating file's mtime 204 | + // In case crc used from xattr and CRC src <> des, recalculate source CRC 205 | + if (x->crc_with_xattr) 206 | + { 207 | + crc_src = CalculateCRC64(src_name); 208 | + 209 | + // Initially, there was a crc mismatch based on stored crc in xattr. 210 | + // We have re-calculated the source crc 211 | + if (crc_des == crc_src) 212 | + { 213 | + 214 | + if (x->verbose) 215 | + { 216 | + char* szCRC; 217 | + int ret; 218 | + ret = asprintf(&szCRC, "%016llX", crc_src); 219 | + if ( ret != -1) 220 | + { 221 | + textcolor(BRIGHT,RED,BLACK); 222 | + printf("SOURCE: CRC [%s] is freshly calculated\n", szCRC); 223 | + } 224 | + } 225 | + 226 | + textcolor(BRIGHT,YELLOW,BLACK); 227 | + printf("WARNING: CRC SOURCE == CRC DESTINATION (based on freshly calculated CRC's)\n"); 228 | + printf("WARNING: But xattr CRC of SOURCE file does not match freshly calculated CRC of SOURCE file\n"); 229 | + printf("WARNING: SRC file is potentially corrupted OR changed w/o updating mtime, eg MS Excel is prone to this.\n"); 230 | + RESET_TEXT(); 231 | + 232 | + // As SOURCE = DESTINATION, we can set to success 233 | + // return_val = true; (not requires to set, as that is starting point, cp or mv itself is already successfull) 234 | + } 235 | + else 236 | + // Recalculating SOURCE CRC did not resolve mismatch in CRC 237 | + { 238 | + textcolor(BRIGHT,RED,BLACK); 239 | + printf(":Checksum FAILED\n"); 240 | + 241 | + char* szCRC1; 242 | + char* szCRC2; 243 | + int ret1, ret2; 244 | + ret1 = asprintf(&szCRC1, "%016llX", crc_src); 245 | + ret2 = asprintf(&szCRC2, "%016llX", crc_des); 246 | + if (( ret1 != -1) && ( ret2 != -1)) 247 | + { 248 | + printf("SOURCE CRC [%s], DESTINATION CRC [%s]\n", szCRC1, szCRC2); 249 | + } 250 | + RESET_TEXT(); 251 | + 252 | + return_val = false; 253 | + } 254 | + } 255 | + else 256 | + // x->crc=true, nothing to be done, CRC error! 257 | + { 258 | + textcolor(BRIGHT,RED,BLACK); 259 | + printf(":Checksum FAILED\n"); 260 | + 261 | + char* szCRC1; 262 | + char* szCRC2; 263 | + int ret1, ret2; 264 | + ret1 = asprintf(&szCRC1, "%016llX", crc_src); 265 | + ret2 = asprintf(&szCRC2, "%016llX", crc_des); 266 | + if (( ret1 != -1) && ( ret2 != -1)) 267 | + { 268 | + printf("SOURCE CRC [%s], DESTINATION CRC [%s]\n", szCRC1, szCRC2); 269 | + } 270 | + RESET_TEXT(); 271 | + 272 | + return_val = false; 273 | + } 274 | + } 275 | + 276 | + // Check if CRC is stored in DESTINATION xattr (as SOURCE might be read only, or filesystem does not support xattr) 277 | + if ( return_val == true && x->crc_with_xattr ) 278 | + { 279 | + // Check if DESTINATION has a CRC stored in xattr 280 | + if ( !IsFreshCRC(dst_name) ) 281 | + { 282 | + // Suppress errors writing CRC in DESTINATION xattr, as DESTINATION filesystem might not support xattr 283 | + bool bError = false; 284 | + 285 | + // CRC is not available in DESTINATION xattr 286 | + if ( !bPutCRC(dst_name, &crc_des, bError) ) 287 | + { 288 | + if (x->verbose) 289 | + { 290 | + // as this is not a fatal error, mark it as yellow 291 | + printStatus(dst_name, "INFO: Cannot store/update DESTINATION CRC in xattr; xattr not supported on filesystem?", YELLOW); 292 | + RESET_TEXT(); 293 | + } 294 | + } 295 | + } 296 | + } 297 | + } 298 | + 299 | + // Verbose print 300 | + if (((x->crc) || (x->crc_with_xattr)) && (x->verbose)) 301 | + { 302 | + printf("\n"); 303 | + } 304 | + /* END HIJN */ 305 | + 306 | /* Output debug info for data copying operations. */ 307 | if (x->debug) 308 | emit_debug (x); 309 | 310 | alignfree (buf); 311 | return return_val; 312 | } 313 | 314 | /* Return whether it's OK that two files are the "same" by some measure. 315 | The first file is SRC_NAME and has status SRC_SB. 316 | -------------------------------------------------------------------------------- /src/crc64.c: -------------------------------------------------------------------------------- 1 | /* Redis uses the CRC64 variant with "Jones" coefficients and init value of 0. 2 | * 3 | * Specification of this CRC64 variant follows: 4 | * Name: crc-64-jones 5 | * Width: 64 bites 6 | * Poly: 0xad93d23594c935a9 7 | * Reflected In: True 8 | * Xor_In: 0xffffffffffffffff 9 | * Reflected_Out: True 10 | * Xor_Out: 0x0 11 | * Check("123456789"): 0xe9c6d914c4b8d9ca 12 | * 13 | * Copyright (c) 2012, Salvatore Sanfilippo 14 | * All rights reserved. 15 | * 16 | * Redistribution and use in source and binary forms, with or without 17 | * modification, are permitted provided that the following conditions are met: 18 | * 19 | * * Redistributions of source code must retain the above copyright notice, 20 | * this list of conditions and the following disclaimer. 21 | * * Redistributions in binary form must reproduce the above copyright 22 | * notice, this list of conditions and the following disclaimer in the 23 | * documentation and/or other materials provided with the distribution. 24 | * * Neither the name of Redis nor the names of its contributors may be used 25 | * to endorse or promote products derived from this software without 26 | * specific prior written permission. 27 | * 28 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 29 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 32 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 33 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 34 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 35 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 36 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 37 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 38 | * POSSIBILITY OF SUCH DAMAGE. */ 39 | 40 | 41 | #include 42 | #include "crc64.h" 43 | 44 | static const uint64_t crc64_tab[256] = { 45 | UINT64_C(0x0000000000000000), UINT64_C(0x7ad870c830358979), 46 | UINT64_C(0xf5b0e190606b12f2), UINT64_C(0x8f689158505e9b8b), 47 | UINT64_C(0xc038e5739841b68f), UINT64_C(0xbae095bba8743ff6), 48 | UINT64_C(0x358804e3f82aa47d), UINT64_C(0x4f50742bc81f2d04), 49 | UINT64_C(0xab28ecb46814fe75), UINT64_C(0xd1f09c7c5821770c), 50 | UINT64_C(0x5e980d24087fec87), UINT64_C(0x24407dec384a65fe), 51 | UINT64_C(0x6b1009c7f05548fa), UINT64_C(0x11c8790fc060c183), 52 | UINT64_C(0x9ea0e857903e5a08), UINT64_C(0xe478989fa00bd371), 53 | UINT64_C(0x7d08ff3b88be6f81), UINT64_C(0x07d08ff3b88be6f8), 54 | UINT64_C(0x88b81eabe8d57d73), UINT64_C(0xf2606e63d8e0f40a), 55 | UINT64_C(0xbd301a4810ffd90e), UINT64_C(0xc7e86a8020ca5077), 56 | UINT64_C(0x4880fbd87094cbfc), UINT64_C(0x32588b1040a14285), 57 | UINT64_C(0xd620138fe0aa91f4), UINT64_C(0xacf86347d09f188d), 58 | UINT64_C(0x2390f21f80c18306), UINT64_C(0x594882d7b0f40a7f), 59 | UINT64_C(0x1618f6fc78eb277b), UINT64_C(0x6cc0863448deae02), 60 | UINT64_C(0xe3a8176c18803589), UINT64_C(0x997067a428b5bcf0), 61 | UINT64_C(0xfa11fe77117cdf02), UINT64_C(0x80c98ebf2149567b), 62 | UINT64_C(0x0fa11fe77117cdf0), UINT64_C(0x75796f2f41224489), 63 | UINT64_C(0x3a291b04893d698d), UINT64_C(0x40f16bccb908e0f4), 64 | UINT64_C(0xcf99fa94e9567b7f), UINT64_C(0xb5418a5cd963f206), 65 | UINT64_C(0x513912c379682177), UINT64_C(0x2be1620b495da80e), 66 | UINT64_C(0xa489f35319033385), UINT64_C(0xde51839b2936bafc), 67 | UINT64_C(0x9101f7b0e12997f8), UINT64_C(0xebd98778d11c1e81), 68 | UINT64_C(0x64b116208142850a), UINT64_C(0x1e6966e8b1770c73), 69 | UINT64_C(0x8719014c99c2b083), UINT64_C(0xfdc17184a9f739fa), 70 | UINT64_C(0x72a9e0dcf9a9a271), UINT64_C(0x08719014c99c2b08), 71 | UINT64_C(0x4721e43f0183060c), UINT64_C(0x3df994f731b68f75), 72 | UINT64_C(0xb29105af61e814fe), UINT64_C(0xc849756751dd9d87), 73 | UINT64_C(0x2c31edf8f1d64ef6), UINT64_C(0x56e99d30c1e3c78f), 74 | UINT64_C(0xd9810c6891bd5c04), UINT64_C(0xa3597ca0a188d57d), 75 | UINT64_C(0xec09088b6997f879), UINT64_C(0x96d1784359a27100), 76 | UINT64_C(0x19b9e91b09fcea8b), UINT64_C(0x636199d339c963f2), 77 | UINT64_C(0xdf7adabd7a6e2d6f), UINT64_C(0xa5a2aa754a5ba416), 78 | UINT64_C(0x2aca3b2d1a053f9d), UINT64_C(0x50124be52a30b6e4), 79 | UINT64_C(0x1f423fcee22f9be0), UINT64_C(0x659a4f06d21a1299), 80 | UINT64_C(0xeaf2de5e82448912), UINT64_C(0x902aae96b271006b), 81 | UINT64_C(0x74523609127ad31a), UINT64_C(0x0e8a46c1224f5a63), 82 | UINT64_C(0x81e2d7997211c1e8), UINT64_C(0xfb3aa75142244891), 83 | UINT64_C(0xb46ad37a8a3b6595), UINT64_C(0xceb2a3b2ba0eecec), 84 | UINT64_C(0x41da32eaea507767), UINT64_C(0x3b024222da65fe1e), 85 | UINT64_C(0xa2722586f2d042ee), UINT64_C(0xd8aa554ec2e5cb97), 86 | UINT64_C(0x57c2c41692bb501c), UINT64_C(0x2d1ab4dea28ed965), 87 | UINT64_C(0x624ac0f56a91f461), UINT64_C(0x1892b03d5aa47d18), 88 | UINT64_C(0x97fa21650afae693), UINT64_C(0xed2251ad3acf6fea), 89 | UINT64_C(0x095ac9329ac4bc9b), UINT64_C(0x7382b9faaaf135e2), 90 | UINT64_C(0xfcea28a2faafae69), UINT64_C(0x8632586aca9a2710), 91 | UINT64_C(0xc9622c4102850a14), UINT64_C(0xb3ba5c8932b0836d), 92 | UINT64_C(0x3cd2cdd162ee18e6), UINT64_C(0x460abd1952db919f), 93 | UINT64_C(0x256b24ca6b12f26d), UINT64_C(0x5fb354025b277b14), 94 | UINT64_C(0xd0dbc55a0b79e09f), UINT64_C(0xaa03b5923b4c69e6), 95 | UINT64_C(0xe553c1b9f35344e2), UINT64_C(0x9f8bb171c366cd9b), 96 | UINT64_C(0x10e3202993385610), UINT64_C(0x6a3b50e1a30ddf69), 97 | UINT64_C(0x8e43c87e03060c18), UINT64_C(0xf49bb8b633338561), 98 | UINT64_C(0x7bf329ee636d1eea), UINT64_C(0x012b592653589793), 99 | UINT64_C(0x4e7b2d0d9b47ba97), UINT64_C(0x34a35dc5ab7233ee), 100 | UINT64_C(0xbbcbcc9dfb2ca865), UINT64_C(0xc113bc55cb19211c), 101 | UINT64_C(0x5863dbf1e3ac9dec), UINT64_C(0x22bbab39d3991495), 102 | UINT64_C(0xadd33a6183c78f1e), UINT64_C(0xd70b4aa9b3f20667), 103 | UINT64_C(0x985b3e827bed2b63), UINT64_C(0xe2834e4a4bd8a21a), 104 | UINT64_C(0x6debdf121b863991), UINT64_C(0x1733afda2bb3b0e8), 105 | UINT64_C(0xf34b37458bb86399), UINT64_C(0x8993478dbb8deae0), 106 | UINT64_C(0x06fbd6d5ebd3716b), UINT64_C(0x7c23a61ddbe6f812), 107 | UINT64_C(0x3373d23613f9d516), UINT64_C(0x49aba2fe23cc5c6f), 108 | UINT64_C(0xc6c333a67392c7e4), UINT64_C(0xbc1b436e43a74e9d), 109 | UINT64_C(0x95ac9329ac4bc9b5), UINT64_C(0xef74e3e19c7e40cc), 110 | UINT64_C(0x601c72b9cc20db47), UINT64_C(0x1ac40271fc15523e), 111 | UINT64_C(0x5594765a340a7f3a), UINT64_C(0x2f4c0692043ff643), 112 | UINT64_C(0xa02497ca54616dc8), UINT64_C(0xdafce7026454e4b1), 113 | UINT64_C(0x3e847f9dc45f37c0), UINT64_C(0x445c0f55f46abeb9), 114 | UINT64_C(0xcb349e0da4342532), UINT64_C(0xb1eceec59401ac4b), 115 | UINT64_C(0xfebc9aee5c1e814f), UINT64_C(0x8464ea266c2b0836), 116 | UINT64_C(0x0b0c7b7e3c7593bd), UINT64_C(0x71d40bb60c401ac4), 117 | UINT64_C(0xe8a46c1224f5a634), UINT64_C(0x927c1cda14c02f4d), 118 | UINT64_C(0x1d148d82449eb4c6), UINT64_C(0x67ccfd4a74ab3dbf), 119 | UINT64_C(0x289c8961bcb410bb), UINT64_C(0x5244f9a98c8199c2), 120 | UINT64_C(0xdd2c68f1dcdf0249), UINT64_C(0xa7f41839ecea8b30), 121 | UINT64_C(0x438c80a64ce15841), UINT64_C(0x3954f06e7cd4d138), 122 | UINT64_C(0xb63c61362c8a4ab3), UINT64_C(0xcce411fe1cbfc3ca), 123 | UINT64_C(0x83b465d5d4a0eece), UINT64_C(0xf96c151de49567b7), 124 | UINT64_C(0x76048445b4cbfc3c), UINT64_C(0x0cdcf48d84fe7545), 125 | UINT64_C(0x6fbd6d5ebd3716b7), UINT64_C(0x15651d968d029fce), 126 | UINT64_C(0x9a0d8ccedd5c0445), UINT64_C(0xe0d5fc06ed698d3c), 127 | UINT64_C(0xaf85882d2576a038), UINT64_C(0xd55df8e515432941), 128 | UINT64_C(0x5a3569bd451db2ca), UINT64_C(0x20ed197575283bb3), 129 | UINT64_C(0xc49581ead523e8c2), UINT64_C(0xbe4df122e51661bb), 130 | UINT64_C(0x3125607ab548fa30), UINT64_C(0x4bfd10b2857d7349), 131 | UINT64_C(0x04ad64994d625e4d), UINT64_C(0x7e7514517d57d734), 132 | UINT64_C(0xf11d85092d094cbf), UINT64_C(0x8bc5f5c11d3cc5c6), 133 | UINT64_C(0x12b5926535897936), UINT64_C(0x686de2ad05bcf04f), 134 | UINT64_C(0xe70573f555e26bc4), UINT64_C(0x9ddd033d65d7e2bd), 135 | UINT64_C(0xd28d7716adc8cfb9), UINT64_C(0xa85507de9dfd46c0), 136 | UINT64_C(0x273d9686cda3dd4b), UINT64_C(0x5de5e64efd965432), 137 | UINT64_C(0xb99d7ed15d9d8743), UINT64_C(0xc3450e196da80e3a), 138 | UINT64_C(0x4c2d9f413df695b1), UINT64_C(0x36f5ef890dc31cc8), 139 | UINT64_C(0x79a59ba2c5dc31cc), UINT64_C(0x037deb6af5e9b8b5), 140 | UINT64_C(0x8c157a32a5b7233e), UINT64_C(0xf6cd0afa9582aa47), 141 | UINT64_C(0x4ad64994d625e4da), UINT64_C(0x300e395ce6106da3), 142 | UINT64_C(0xbf66a804b64ef628), UINT64_C(0xc5bed8cc867b7f51), 143 | UINT64_C(0x8aeeace74e645255), UINT64_C(0xf036dc2f7e51db2c), 144 | UINT64_C(0x7f5e4d772e0f40a7), UINT64_C(0x05863dbf1e3ac9de), 145 | UINT64_C(0xe1fea520be311aaf), UINT64_C(0x9b26d5e88e0493d6), 146 | UINT64_C(0x144e44b0de5a085d), UINT64_C(0x6e963478ee6f8124), 147 | UINT64_C(0x21c640532670ac20), UINT64_C(0x5b1e309b16452559), 148 | UINT64_C(0xd476a1c3461bbed2), UINT64_C(0xaeaed10b762e37ab), 149 | UINT64_C(0x37deb6af5e9b8b5b), UINT64_C(0x4d06c6676eae0222), 150 | UINT64_C(0xc26e573f3ef099a9), UINT64_C(0xb8b627f70ec510d0), 151 | UINT64_C(0xf7e653dcc6da3dd4), UINT64_C(0x8d3e2314f6efb4ad), 152 | UINT64_C(0x0256b24ca6b12f26), UINT64_C(0x788ec2849684a65f), 153 | UINT64_C(0x9cf65a1b368f752e), UINT64_C(0xe62e2ad306bafc57), 154 | UINT64_C(0x6946bb8b56e467dc), UINT64_C(0x139ecb4366d1eea5), 155 | UINT64_C(0x5ccebf68aecec3a1), UINT64_C(0x2616cfa09efb4ad8), 156 | UINT64_C(0xa97e5ef8cea5d153), UINT64_C(0xd3a62e30fe90582a), 157 | UINT64_C(0xb0c7b7e3c7593bd8), UINT64_C(0xca1fc72bf76cb2a1), 158 | UINT64_C(0x45775673a732292a), UINT64_C(0x3faf26bb9707a053), 159 | UINT64_C(0x70ff52905f188d57), UINT64_C(0x0a2722586f2d042e), 160 | UINT64_C(0x854fb3003f739fa5), UINT64_C(0xff97c3c80f4616dc), 161 | UINT64_C(0x1bef5b57af4dc5ad), UINT64_C(0x61372b9f9f784cd4), 162 | UINT64_C(0xee5fbac7cf26d75f), UINT64_C(0x9487ca0fff135e26), 163 | UINT64_C(0xdbd7be24370c7322), UINT64_C(0xa10fceec0739fa5b), 164 | UINT64_C(0x2e675fb4576761d0), UINT64_C(0x54bf2f7c6752e8a9), 165 | UINT64_C(0xcdcf48d84fe75459), UINT64_C(0xb71738107fd2dd20), 166 | UINT64_C(0x387fa9482f8c46ab), UINT64_C(0x42a7d9801fb9cfd2), 167 | UINT64_C(0x0df7adabd7a6e2d6), UINT64_C(0x772fdd63e7936baf), 168 | UINT64_C(0xf8474c3bb7cdf024), UINT64_C(0x829f3cf387f8795d), 169 | UINT64_C(0x66e7a46c27f3aa2c), UINT64_C(0x1c3fd4a417c62355), 170 | UINT64_C(0x935745fc4798b8de), UINT64_C(0xe98f353477ad31a7), 171 | UINT64_C(0xa6df411fbfb21ca3), UINT64_C(0xdc0731d78f8795da), 172 | UINT64_C(0x536fa08fdfd90e51), UINT64_C(0x29b7d047efec8728), 173 | }; 174 | 175 | uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l) { 176 | uint64_t j; 177 | 178 | for (j = 0; j < l; j++) { 179 | uint8_t byte = s[j]; 180 | crc = crc64_tab[(uint8_t)crc ^ byte] ^ (crc >> 8); 181 | } 182 | return crc; 183 | } 184 | 185 | 186 | #ifdef TEST_MAIN 187 | #include 188 | int main(void) { 189 | printf("e9c6d914c4b8d9ca == %016llx\n", 190 | (unsigned long long) crc64(0,(unsigned char*)"123456789",9)); 191 | return 0; 192 | } 193 | #endif 194 | -------------------------------------------------------------------------------- /src/crcsum.c: -------------------------------------------------------------------------------- 1 | /* 2 | This program is free software: you can redistribute it and/or modify 3 | it under the terms of the GNU General Public License as published by 4 | the Free Software Foundation, version 3 of the License. 5 | 6 | This program is distributed in the hope that it will be useful, 7 | but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | GNU General Public License for more details. 10 | 11 | You should have received a copy of the GNU General Public License 12 | along with this program. If not, see . 13 | 14 | This software is based on Checkit version 0.2.0 15 | Dennis Katsonis; E-Mail dennisk (at) netspace.net.au 16 | */ 17 | 18 | #define _GNU_SOURCE // asprintf 19 | 20 | // This caused build issues on Debian Jessie. Not sure why. Not sure if disabling is an issue too 21 | //#define _XOPEN_SOURCE 500 // nftw 22 | 23 | 24 | // to overcome issue with file size & stat limitations on 32 bit systems 25 | #define _FILE_OFFSET_BITS 64 26 | 27 | #include "config.h" 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #include "crc64.h" 47 | #include "crcsum.h" 48 | 49 | int flags = 0; 50 | int processed = 0; 51 | int failed = 0; 52 | int ext_counter = 0; 53 | char arr_ext[MAXEXT][5]; 54 | 55 | 56 | 57 | //////////////////////////////////////////////////////////// 58 | // Check if CRC64 attribute is present. 59 | // Returns 60 | // true: success 61 | // false: if requested attribute is not present 62 | //////////////////////////////////////////////////////////// 63 | int IsPresentCRC64(const char *file) 64 | { 65 | char buf[4096]; 66 | char* current_attr; 67 | int x; 68 | 69 | // size = listxattr(file, buff, 0); 70 | // use to dynamically allocate buf size 71 | 72 | x = listxattr(file, buf, 4096); 73 | current_attr = buf; 74 | 75 | if (x > 0) 76 | { 77 | do 78 | { 79 | if (strcmp(current_attr, "user.crc64") == 0) 80 | { 81 | return true; 82 | } 83 | else 84 | { 85 | current_attr += (strlen(current_attr) + 1); 86 | } 87 | } 88 | while ((current_attr - buf) < x); 89 | } 90 | else if (x == -1) 91 | { 92 | perror("IsPresentCRC64:listxattr"); 93 | } 94 | return false; 95 | } 96 | 97 | 98 | //////////////////////////////////////////////////////////// 99 | // Check if CRC is fresh 100 | // RETURN 101 | // true: fresh 102 | // false: stale or CRC not present or on any stat(file) error 103 | //////////////////////////////////////////////////////////// 104 | int IsFreshCRC(const char *filename) 105 | { 106 | //int attribute_format; 107 | struct s_crc64 crc; //crc, t 108 | struct stat stat_file; 109 | 110 | if ( stat(filename, &stat_file) == -1 ) 111 | { 112 | printStatus(filename, "ERROR", RED); 113 | perror("IsFreshCRC:stat"); 114 | return false; 115 | } 116 | 117 | //attribute_format = IsPresentCRC64(filename); 118 | 119 | if ( IsPresentCRC64(filename) ) 120 | { 121 | if ((getxattr(filename, "user.crc64", &crc, sizeof(crc)) == -1)) 122 | { 123 | //printStatus(filename, "ERROR", RED); 124 | //perror("IsFreshCRC:getxattr"); 125 | return false; 126 | } 127 | else 128 | { 129 | return (crc.t == stat_file.st_mtime); 130 | } 131 | } 132 | return 0; 133 | } 134 | 135 | 136 | //////////////////////////////////////////////////////////// 137 | // Removes the CRC 138 | //////////////////////////////////////////////////////////// 139 | int removeCRC(const char *filename) 140 | { 141 | if ( IsPresentCRC64(filename) ) 142 | { 143 | if ((removexattr(filename, "user.crc64")) == -1) 144 | { 145 | printStatus(filename, "ERROR", RED); 146 | perror("removeCRC:removexattr"); 147 | failed++; 148 | return false; 149 | } 150 | else 151 | { 152 | if (flags & VERBOSE) 153 | { 154 | printStatus(filename,"CRC removed", GREEN); 155 | } 156 | } 157 | } 158 | processed++; 159 | return true; 160 | } 161 | 162 | //////////////////////////////////////////////////////////// 163 | // Write CRC - depending on flags 164 | // 165 | // filename: file under subject 166 | // *crc : pointer to a t_crc64 where crc value will be stored; pointer can be NULL (and value will not be stored) 167 | // if *crc != null (crc value), the provided crc value will be stored; otherwise it will be calculated 168 | // bError : ignore errors writing to xattr (in case of read only files) 169 | // 170 | // return: 171 | // false : failed 172 | // true : success 173 | int bPutCRC(const char *filename, t_crc64 *crc, bool bError) 174 | { 175 | struct stat stat_file; 176 | struct s_crc64 s_crc; //crc, t 177 | 178 | if ( stat(filename, &stat_file) == -1 ) 179 | { 180 | printStatus(filename, "ERROR", RED); 181 | perror("bPutCRC:stat"); 182 | return false; 183 | } 184 | 185 | if (crc != NULL && *crc != 0) 186 | { 187 | // pointer to a crc value is there AND a non zero crc value is provided 188 | // use provided crc value 189 | s_crc.crc = *crc; 190 | s_crc.t = stat_file.st_mtime; 191 | } 192 | 193 | if (crc != NULL && *crc == 0) 194 | { 195 | // pointer to a crc value is there AND a zero crc value is provided 196 | // Calculate crc value & return value 197 | s_crc.crc = CalculateCRC64(filename); 198 | s_crc.t = stat_file.st_mtime; 199 | *crc = s_crc.crc; 200 | } 201 | 202 | if (crc == NULL) 203 | { 204 | // Calculate crc value 205 | s_crc.crc = CalculateCRC64(filename); 206 | s_crc.t = stat_file.st_mtime; 207 | } 208 | 209 | if ( (setxattr(filename, "user.crc64", &s_crc, sizeof(s_crc), 0 )) == -1 ) 210 | { 211 | // print error when requested. In some cases (in expected read only file) it is OK to ignore error 212 | if (bError) 213 | { 214 | printStatus(filename, "ERROR", RED); 215 | perror("bPutCRC:setxattr"); 216 | } 217 | return false; 218 | } 219 | else 220 | { 221 | processed++; 222 | return true; 223 | } 224 | 225 | processed++; 226 | return true; 227 | } 228 | 229 | // This retreives the CRC, first by checking for an extended attribute 230 | // then by looking for a hidden file. Returns 0 if unsuccessful, otherwise 231 | // return the checksum.*/ 232 | t_crc64 getCRC(const char *filename) 233 | { 234 | //int attribute_format; 235 | struct s_crc64 crc; //crc, t 236 | 237 | //attribute_format = IsPresentCRC64(filename); 238 | 239 | if ( !IsPresentCRC64(filename) ) 240 | { 241 | //printf("%s: No CRC stored\n", filename); 242 | return false; 243 | } 244 | 245 | if ( IsPresentCRC64(filename) ) 246 | { 247 | if ((getxattr(filename, "user.crc64", &crc, sizeof(crc)) == -1)) 248 | { 249 | printStatus(filename, "ERROR", RED); 250 | perror("getCRC:getxattr"); 251 | return false; 252 | } 253 | else 254 | { 255 | return crc.crc; 256 | } 257 | } 258 | return false; 259 | } 260 | 261 | //////////////////////////////////////////////////////////// 262 | // Open file and calculate CRC 263 | //////////////////////////////////////////////////////////// 264 | t_crc64 CalculateCRC64(const char *filename) 265 | { 266 | unsigned char buf[MAX_BUF_LEN]; 267 | size_t bufread = MAX_BUF_LEN; 268 | int cont = 1; 269 | int fd; 270 | uint64_t tot = 0; 271 | uint64_t temp = 0; 272 | 273 | if ((fd = open(filename,O_RDONLY)) == -1) 274 | { 275 | printStatus(filename, "ERROR", RED); 276 | perror("CalculateCRC64:open"); 277 | return 0; 278 | } 279 | 280 | while (cont) 281 | { 282 | bufread = read(fd, buf, bufread); 283 | if (bufread == -1) 284 | { 285 | printStatus(filename, "ERROR", RED); 286 | perror("CalculateCRC64:read"); 287 | close(fd); 288 | return 0; 289 | } 290 | 291 | temp = (t_crc64) crc64(temp, buf, (unsigned int)bufread); 292 | tot = tot + bufread; 293 | 294 | if (bufread < MAX_BUF_LEN) 295 | { 296 | cont = 0; 297 | } 298 | } 299 | 300 | close(fd); 301 | return temp; 302 | } 303 | 304 | 305 | //////////////////////////////////////////////////////////// 306 | // Return: 307 | // 0: Continue next in tree walk on nftw() 308 | // !=0: Terminate tree walk 309 | //////////////////////////////////////////////////////////// 310 | int nftw_callback(const char *fpath, const struct stat *sb, int tflag, __attribute__((unused)) struct FTW *ftwbuf) 311 | { 312 | // FTW_F also includes PIPES; not sure about SOCKETS etc 313 | // Hence, use stat information to check whether it is a regular file 314 | // if ( tflag == FTW_F ) 315 | if S_ISREG(sb->st_mode) 316 | { 317 | //Process File 318 | // TODO return value 319 | processFile(fpath); 320 | return 0; 321 | } 322 | else 323 | { 324 | if (flags & VERBOSE) 325 | { 326 | switch (sb->st_mode & S_IFMT) 327 | { 328 | case S_IFBLK: printStatus(fpath, "Block Devioce", YELLOW); break; 329 | case S_IFCHR: printStatus(fpath, "Char Deviced", YELLOW); break; 330 | case S_IFDIR: printStatus(fpath, "Directory", RED); break; 331 | case S_IFIFO: printStatus(fpath, "FIFO/pipe", YELLOW); break; 332 | case S_IFLNK: printStatus(fpath, "Symlink", YELLOW); break; 333 | case S_IFSOCK: printStatus(fpath, "Socket", YELLOW); break; 334 | case S_ISVTX: printStatus(fpath, "S_ISVTX", YELLOW); break; 335 | default: printStatus(fpath, "Unknown?", RED); printf("%04X", (sb->st_mode & S_IFMT)); break; 336 | } 337 | } 338 | } 339 | 340 | // continue tree walk 341 | return 0; 342 | } 343 | 344 | 345 | //////////////////////////////////////////////////////////// 346 | // First entry after option flag processing for cmdline arguments procession 347 | // Separate files from directories 348 | //////////////////////////////////////////////////////////// 349 | int processCmdLine(const char *filename) 350 | { 351 | struct stat statbuf; 352 | 353 | if ( lstat(filename, &statbuf) == -1 ) 354 | { 355 | printStatus(filename, "ERROR", RED); 356 | perror("processCmdLine:stat"); 357 | return false; 358 | } 359 | 360 | // check if we are a directory AND need to process recursively 361 | if ( S_ISDIR(statbuf.st_mode) && (flags & RECURSE) ) 362 | { 363 | // Process Directory 364 | int typeflag = FTW_PHYS | FTW_MOUNT; //Symbolic links are not followed; stay in same FS 365 | const int maxDirs = 100; 366 | 367 | if ( nftw( filename, nftw_callback, maxDirs, typeflag) == -1 ) 368 | { 369 | printStatus(filename, "ERROR", RED); 370 | perror("processCmdLine:nftw"); 371 | return false; 372 | } 373 | 374 | return true; 375 | } 376 | 377 | // Check if we are a Regular file (symbolic links are excluded) 378 | if ( S_ISREG(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode) ) 379 | { 380 | // Process File 381 | processFile(filename); 382 | return true; 383 | } 384 | else 385 | { 386 | if (flags & VERBOSE) 387 | { 388 | switch (statbuf.st_mode & S_IFMT) 389 | { 390 | case S_IFBLK: printStatus(filename, "Block Devioce", YELLOW); break; 391 | case S_IFCHR: printStatus(filename, "Char Deviced", YELLOW); break; 392 | case S_IFDIR: printStatus(filename, "Directory", RED); break; 393 | case S_IFIFO: printStatus(filename, "FIFO/pipe", YELLOW); break; 394 | case S_IFLNK: printStatus(filename, "Symlink", YELLOW); break; 395 | case S_IFSOCK: printStatus(filename, "Socket", YELLOW); break; 396 | case S_ISVTX: printStatus(filename, "S_ISVTX", YELLOW); break; 397 | default: printStatus(filename, "Unknown?", RED); printf("%04X", (statbuf.st_mode & S_IFMT)); break; 398 | } 399 | } 400 | } 401 | 402 | return false; 403 | } 404 | 405 | //////////////////////////////////////////////////////////// 406 | // Expects a Regular File 407 | //////////////////////////////////////////////////////////// 408 | int processFile(const char *filename) 409 | { 410 | struct stat statbuf; 411 | 412 | if ( lstat(filename, &statbuf) == -1 ) 413 | { 414 | printStatus(filename, "ERROR", RED); 415 | perror("processFile:lstat"); 416 | return false; 417 | } 418 | 419 | // Print CRC64 420 | // Red: CRC = stale 421 | // Green: CRC = fresh 422 | if (flags & PRINT) 423 | { 424 | t_crc64 crc; 425 | 426 | // print entries with fresh CRC in green text 427 | if ( (crc = getCRC(filename)) ) 428 | { 429 | if ( IsFreshCRC(filename) ) 430 | { 431 | char* szCRC; 432 | asprintf(&szCRC, "%016llX", crc); 433 | printStatus(filename, szCRC, GREEN); 434 | free(szCRC); szCRC = NULL; 435 | return true; 436 | } 437 | else if (flags & DEBUG) 438 | { 439 | char* szCRC; 440 | asprintf(&szCRC, "%016llX", crc); 441 | printStatus(filename, szCRC, RED); 442 | free(szCRC); szCRC = NULL; 443 | return true; 444 | } 445 | } 446 | 447 | // print entries without CRC in white text 448 | else if (flags & DEBUG) 449 | { 450 | if (statbuf.st_size == 0) 451 | { 452 | printStatus(filename," Zero File Size ", YELLOW); 453 | } 454 | else 455 | { 456 | printStatus(filename,"No CRC available", YELLOW); 457 | } 458 | return false; 459 | } 460 | else 461 | { 462 | return false; 463 | } 464 | } 465 | 466 | // STORE CRC but do not OVERWRITE or UPDATE existing CRC 467 | if ( (flags & STORE) && (!(flags & OVERWRITE)) && (!(flags & UPDATE)) ) 468 | { 469 | if(IsPresentCRC64(filename)) 470 | { 471 | if (flags & DEBUG) 472 | { 473 | printStatus(filename,"CRC already stored", YELLOW); 474 | } 475 | return false; 476 | } 477 | else if ( !bPutCRC(filename, NULL, true) ) 478 | { 479 | fprintf(stderr, "%s: Failed to store CRC.\n", filename); 480 | return false; 481 | } 482 | else if (flags & VERBOSE) 483 | { 484 | printStatus(filename, " CRC is saved ", GREEN); 485 | } 486 | return true; 487 | } 488 | 489 | // OVERWRITE CRC 490 | if (flags & OVERWRITE) 491 | { 492 | if ( !bPutCRC(filename, NULL, true) ) 493 | { 494 | //fprintf(stderr, "%s: Failed to overwrite CRC.\n", filename); 495 | return false; 496 | } 497 | else if (flags & VERBOSE) 498 | { 499 | printStatus(filename, " CRC is overwritten ", GREEN); 500 | } 501 | return true; 502 | } 503 | 504 | // UPDATE CRC when stale 505 | if (flags & UPDATE) 506 | { 507 | if ( !IsFreshCRC(filename) ) 508 | { 509 | // CRC is stale 510 | if ( !bPutCRC(filename, NULL, true) ) 511 | { 512 | //printStatus(filename, "FAILED to update CRC", RED); 513 | return false; 514 | } 515 | else if (flags & VERBOSE) 516 | { 517 | printStatus(filename, " Updated CRC ", GREEN); 518 | return true; 519 | } 520 | } 521 | else if (flags & DEBUG) 522 | { 523 | printStatus(filename, "No CRC Update", YELLOW); 524 | } 525 | return true; 526 | } 527 | 528 | // Check CRC only if CRC is not stale 529 | if (flags & CHECK) 530 | { 531 | if ( IsFreshCRC(filename) ) 532 | { 533 | // check if extension to be ommitted 534 | if (flags & OMIT) 535 | { 536 | // copy filename 537 | char *buffer; 538 | buffer = malloc(strlen(filename) + 1); 539 | strcpy(buffer, filename); 540 | 541 | char* p = NULL; 542 | char* prev_p = NULL; 543 | p = strtok(buffer, "."); 544 | while (p != NULL) 545 | { 546 | if (prev_p != NULL) 547 | free(prev_p); 548 | prev_p = NULL; 549 | 550 | prev_p = malloc(strlen(p) + 1); 551 | strcpy(prev_p, p); 552 | p = strtok (NULL, "."); 553 | } 554 | free(buffer); 555 | buffer = NULL; 556 | 557 | for (int i = 0; i < ext_counter; i++) 558 | { 559 | if (strcmp(arr_ext[i], prev_p) == 0) 560 | { 561 | if (flags & VERBOSE) 562 | { 563 | printStatus(filename, " OMIT ", GREEN); 564 | } 565 | processed++; 566 | free(prev_p); 567 | prev_p = NULL; 568 | return true; 569 | } 570 | } 571 | free(prev_p); 572 | prev_p = NULL; 573 | } 574 | if (CalculateCRC64(filename) == getCRC(filename)) 575 | { 576 | if (flags & VERBOSE) 577 | { 578 | printStatus(filename, " OK ", GREEN); 579 | } 580 | } 581 | else 582 | { 583 | printStatus(filename, "FAILED", RED); 584 | failed++; 585 | } 586 | processed++; 587 | } 588 | } // End of Check CRC routine 589 | 590 | // Remove CRC 591 | if (flags & REMOVE) 592 | { 593 | if ( removeCRC(filename) ) 594 | { 595 | return true; 596 | } 597 | else 598 | { 599 | char path[PATH_MAX]; 600 | getcwd(path, PATH_MAX); 601 | printf("%s/%s: Remove CRC failed\n", path, filename); 602 | return false; 603 | } 604 | } // End of Remove CRC routine 605 | return true; 606 | } 607 | 608 | 609 | // Print status message 610 | void printStatus(const char* filename, const char* status, int fg) 611 | { 612 | printf("[ "); 613 | textcolor(BRIGHT,fg,BLACK); 614 | printf("%s", status); 615 | RESET_TEXT(); 616 | printf(" ] "); 617 | printf(" %-20s\n", filename); 618 | } 619 | 620 | // Change text colour 621 | void textcolor(int attr, int fg, int bg) 622 | { 623 | char command[13]; 624 | 625 | // Command is the control command to the terminal 626 | sprintf(command, "%c[%d;%d;%dm", 0x1B, attr, fg + 30, bg + 40); 627 | printf("%s", command); 628 | } 629 | --------------------------------------------------------------------------------