#
#  Makefile
#
#  Written by Jari Ruusu, September 28 2004
#
#  Copyright 2001,2002,2003,2004 by Jari Ruusu.
#  Redistribution of this file is permitted under the GNU Public License.
#
#  To compile and install, use commands:  make clean; make
#  This Makefile tries to locate running kernel source directory and
#  steal definitions from kernel Makefile. Note: kernel must be properly
#  configured and compiled in order for this to work.
#  You can override the kernel source directory by defining LINUX_SOURCE
#  like this:  make LINUX_SOURCE=/usr/src/linux-2.2.20aa1
#
#  Both LINUX_SOURCE and KBUILD_OUTPUT must be defined when
#  compiling for 2.6.x kernel with separate object directory.
#

KR:=$(shell uname -r)
ifdef LINUX_SOURCE
	LS:=$(LINUX_SOURCE)
else
	LS:=$(shell  if [ -f /lib/modules/$(KR)/source/Makefile ]; then cd /lib/modules/$(KR)/source && /bin/pwd; \
		else if [ -f /lib/modules/$(KR)/build/include/linux/version.h ]; then cd /lib/modules/$(KR)/build && /bin/pwd; \
		else if [ -f /usr/src/linux/include/linux/version.h ]; then cd /usr/src/linux && /bin/pwd; \
		else if [ -f /usr/src/linux-$(KR)/include/linux/version.h ]; then cd /usr/src/linux-$(KR) && /bin/pwd; \
		else if [ -f /usr/src/kernel-source-$(KR)/include/linux/version.h ]; then cd /usr/src/kernel-source-$(KR) && /bin/pwd; \
		else echo unable-to-guess-source-dir; fi; fi; fi; fi; fi)
endif
ifdef KBUILD_OUTPUT
	OD1:=$(KBUILD_OUTPUT)
else
ifdef LINUX_SOURCE
	OD1:=$(LS)
else
	OD1:=$(shell if [ -f /lib/modules/$(KR)/build/include/linux/version.h ]; then cd /lib/modules/$(KR)/build && /bin/pwd; else echo $(LS); fi)
endif
endif
OD2:=
ifneq ($(LS),$(OD1))
	OD2:= O=$(OD1)
endif
TD:=$(shell /bin/pwd)

USE_KBUILD:=n
MODINST:=y
KEYSCRUB:=n

ifeq ($(USE_KBUILD),y)
	VERSION=$(shell grep '^VERSION' $(LS)/Makefile | head -n 1 | tr -d -c '0-9')
	PATCHLEVEL=$(shell grep '^PATCHLEVEL' $(LS)/Makefile | head -n 1 | tr -d -c '0-9')
endif

PATCHNAME:=loop.c-$(VERSION).$(PATCHLEVEL).diff
ORIGNAME:=loop.c-$(VERSION).$(PATCHLEVEL).original
PREPATCHED:=loop.c-$(VERSION).$(PATCHLEVEL).patched

EF:=
LF:=-r
CP1:=
PP1:=
VM1:=
ifeq ($(shell if [ "$(VERSION)$(PATCHLEVEL)0" -lt 260 ]; then echo y; fi),y)
	OD1:=$(LS)
endif
ifeq ($(VERSION).$(PATCHLEVEL),2.0)
	# Use optimized assembler implementation if target is x86 processor
	X86_ASM:=$(shell if grep -q -s "define CONFIG_M[3456]86" $(LS)/include/linux/autoconf.h; then echo y; fi)
	# kernel v2.0 doesn't have u_int32_t and u_int64_t
	EF += -Du_int32_t=__u32 -Du_int64_t=__u64
ifndef KERNELRELEASE
	# some older 2.0 kernels don't define KERNELRELEASE
	KERNELRELEASE:=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)
endif
endif
ifeq ($(VERSION).$(PATCHLEVEL),2.2)
	# Use optimized assembler implementation if target is x86 processor
	X86_ASM:=$(shell if grep -q -s "define CONFIG_M[3456]86" $(LS)/include/linux/autoconf.h; then echo y; fi)
endif
ifeq ($(VERSION).$(PATCHLEVEL),2.4)
	EF += $(kbuild_2_4_nostdinc)
	# Use optimized assembler implementation if target is x86 processor
	X86_ASM:=$(shell if grep -q -s "define CONFIG_X86 1" $(LS)/include/linux/autoconf.h; then if ! grep -q -s "define CONFIG_X86_64" $(LS)/include/linux/autoconf.h; then echo y; fi; fi)
	# Use optimized assembler implementation if target is AMD64 or compatible processor
	AMD64_ASM:=$(shell if grep -q -s "define CONFIG_X86_64" $(LS)/include/linux/autoconf.h; then echo y; fi)
	# some older 2.4 kernels don't have struct block_device_operations.owner
	EF += $(shell if ! grep -q -s "owner.*THIS_MODULE" $(LS)/drivers/block/loop.c; then echo "-DNO_BLOCK_DEVICE_OPERATIONS_OWNER"; fi)
	# some older 2.4 kernels don't have reparent_to_init()
	EF += $(shell if ! grep -q -s "extern void reparent_to_init" $(LS)/include/linux/sched.h; then echo "-DNO_REPARENT_TO_INIT"; fi)
	# some 2.4 kernels don't have struct task_struct.sigmask_lock
	EF += $(shell if ! grep -q -s "spinlock_t sigmask_lock" $(LS)/include/linux/sched.h; then if grep -q -s 'struct sighand_struct \*sighand;' $(LS)/include/linux/sched.h; then echo '-DNO_TASK_STRUCT_SIGMASK_LOCK=1'; else echo '-DNO_TASK_STRUCT_SIGMASK_LOCK=2'; fi; fi)
endif
ifeq ($(shell if [ "$(VERSION)$(PATCHLEVEL)0" -ge 260 ]; then echo y; fi),y)
ifneq ($(USE_KBUILD),y)
	EF += $(CFLAGS_MODULE) $(NOSTDINC_FLAGS)
ifeq ($(shell if ! echo " $(CFLAGS) " | grep -q -s "D__KERNEL__"; then echo y; fi),y)
	CFLAGS := $(CPPFLAGS) $(CFLAGS)
endif
	LF:=$(LDFLAGS) $(EXTRA_LDFLAGS) $(LDFLAGS_MODULE)
	CP1:=cd $(OD1) &&
	PP1:=$(TD)/
	MP1:=$(shell if test -e $(OD1)/scripts/mod/modpost; then echo "./scripts/mod/modpost"; else echo "./scripts/modpost"; fi)
	VM1:=k
endif
	# Use optimized assembler implementation if target is x86 processor
	X86_ASM:=$(shell if grep -q -s "define CONFIG_X86 1" $(OD1)/include/linux/autoconf.h; then if ! grep -q -s "define CONFIG_X86_64" $(OD1)/include/linux/autoconf.h; then echo y; fi; fi)
	# Use optimized assembler implementation if target is AMD64 or compatible processor
	AMD64_ASM:=$(shell if grep -q -s "define CONFIG_X86_64" $(OD1)/include/linux/autoconf.h; then echo y; fi)
	# some 2.6 kernels have different block driver interface
	EF += $(shell if grep -q -s "^static int lo_ioctl.struct block_device " $(LS)/drivers/block/loop.c; then echo "-DNEW_BLOCK_DRIVER_INTERFACE"; fi)
	# some older 2.6 kernels have different request_module interface
	EF += $(shell if ! grep -q -s "request_module.*block-major-.*MAJOR.*MINOR" $(LS)/drivers/block/genhd.c; then echo "-DOLD_REQUEST_MODULE_INTERFACE"; fi)
	# some older 2.6 kernels have per thread resource limits
	EF += $(shell if grep -q -s "current->rlim" $(LS)/kernel/sys.c; then echo "-DOLD_PER_THREAD_RLIMITS"; fi)
endif

ifeq ($(KEYSCRUB),y)
ifeq ($(shell if [ "$(VERSION)$(PATCHLEVEL)0" -ge 240 ]; then echo y; fi),y)
	EF += -DCONFIG_BLK_DEV_LOOP_KEYSCRUB=1
endif
endif

# Some distros include "" characters in KERNELRELEASE string
KERNELRELEASE_clean:=$(shell echo $(KERNELRELEASE))

AES_OBJ_CODE:=aes.o
MD5_OBJ_CODE:=md5.o
ifeq ($(X86_ASM),y)
	AES_OBJ_CODE:=aes-x86.o
	MD5_OBJ_CODE:=md5-x86.o
	EF += -DX86_ASM
endif
ifeq ($(AMD64_ASM),y)
	AES_OBJ_CODE:=aes-amd64.o
	MD5_OBJ_CODE:=md5-amd64.o
	EF += -DAMD64_ASM
endif

# check if depmod supports -F and -b options
ifeq ($(shell if [ "$(VERSION)$(PATCHLEVEL)0" -ge 260 ]; then echo y; fi),y)
	DMOK:=y
else
	DMOK:=$(shell if [ `/sbin/insmod -V 2>&1 | head -1 | awk '/^insmod version /{split($$3, a, /\./); printf "%d%03d%03d\n", a[1], a[2], a[3];}'`0 -ge 20020020 ]; then echo y; fi)
endif
ifndef DEPMOD
	DEPMOD:=/sbin/depmod
endif

# check if kernel source has System.map
SYSM:=$(shell if [ -r $(OD1)/System.map ]; then echo y; fi)

RUNDM:=
DMOPTS:=
ifeq ($(DMOK),y)
ifneq "$(strip $(INSTALL_MOD_PATH))" ""
	DMOPTS += -b $(INSTALL_MOD_PATH)
endif
ifeq ($(SYSM),y)
	RUNDM:=y
	DMOPTS += -F $(OD1)/System.map $(KERNELRELEASE_clean)
endif
endif
ifeq ($(KERNELRELEASE_clean),$(KR))
ifeq "$(strip $(INSTALL_MOD_PATH))" ""
	RUNDM:=y
endif
endif

all:
ifeq ($(LS),unable-to-guess-source-dir)
	@echo "Unable to guess linux kernel source directory. Please specify"
	@echo "directory like this:  make LINUX_SOURCE=/usr/src/linux-2.2.20aa1"
	@exit 1
endif
ifeq ($(USE_KBUILD),y)
	rm -r -f tmp-d-kbuild
	mkdir tmp-d-kbuild
	cd tmp-d-kbuild && ln -s ../loop.c-$(VERSION).$(PATCHLEVEL).patched patched-loop.c && ln -s ../glue.c glue.c && ln -s ../aes.h aes.h && ln -s ../md5.h md5.h
ifeq ($(X86_ASM),y)
	cd tmp-d-kbuild && ln -s ../aes-x86.S aes-x86.S && ln -s ../md5-x86.S md5-x86.S
	echo 'loop-y:=patched-loop.o glue.o aes-x86.o md5-x86.o' >>tmp-d-kbuild/Makefile
else
ifeq ($(AMD64_ASM),y)
	cd tmp-d-kbuild && ln -s ../aes-amd64.S aes-amd64.S && ln -s ../md5-amd64.S md5-amd64.S
	echo 'loop-y:=patched-loop.o glue.o aes-amd64.o md5-amd64.o' >>tmp-d-kbuild/Makefile
else
	cd tmp-d-kbuild && ln -s ../aes.c aes.c && ln -s ../md5.c md5.c
	echo 'loop-y:=patched-loop.o glue.o aes.o md5.o' >>tmp-d-kbuild/Makefile
	echo 'CFLAGS_aes.o := -DDATA_ALWAYS_ALIGNED=1' >>tmp-d-kbuild/Makefile
endif
endif
	echo 'EXTRA_CFLAGS:= $(EF)' >>tmp-d-kbuild/Makefile
	echo 'obj-m:=loop.o' >>tmp-d-kbuild/Makefile
	cd $(LS) && make M=$(TD)/tmp-d-kbuild modules $(OD2)
ifeq ($(MODINST),y)
	cd $(LS) && make M=$(TD)/tmp-d-kbuild modules_install $(OD2)
endif
else
	cd $(LS) && make SUBDIRS=$(TD) modules Q='@cd $(TD) && if [ "$$@" = "modules" ]; then make modules; fi; # ' $(OD2)
endif

modules: clean loop.$(VM1)o
ifeq ($(MODINST),y)
	mkdir -p $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE_clean)/block
	cp -p loop.$(VM1)o $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE_clean)/block
ifeq ($(shell if [ "$(VERSION)$(PATCHLEVEL)0" -ge 240 ]; then echo y; fi),y)
	rm -f $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE_clean)/kernel/drivers/block/loop.{,k}o
endif
endif
ifeq ($(RUNDM),y)
	$(DEPMOD) -a $(DMOPTS)
endif
	sync
	@echo "Currently running kernel is" $(KR)
	@echo "Module was built for kernel" $(KERNELRELEASE_clean)
ifneq ($(KERNELRELEASE_clean),$(KR))
	@echo "WARNING - THIS MODULE WILL NOT WORK WITH CURRENTLY RUNNING KERNEL"
endif
loop.$(VM1)o: patched-loop.o glue.o $(AES_OBJ_CODE) $(MD5_OBJ_CODE)
	$(LD) $(LF) patched-loop.o glue.o $(AES_OBJ_CODE) $(MD5_OBJ_CODE) -o loop.o
ifneq "$(VM1)" ""
	$(CP1) $(MP1) vmlinux $(PP1)loop.o >/dev/null 2>&1
	$(CP1) $(CC) $(CFLAGS) $(EF) -DKBUILD_BASENAME=loop_mod -DKBUILD_MODNAME=loop -c $(PP1)loop.mod.c -o $(PP1)loop.mod.o
	$(LD) $(LF) loop.o loop.mod.o -o loop.$(VM1)o
	rm -f loop.o loop.mod.[co]
endif
patched-loop.o:
	rm -f patched-loop.[ch]
ifeq ($(shell if [ "$(VERSION)$(PATCHLEVEL)0" -ge 240 ]; then echo y; fi),y)
	cp $(PREPATCHED) patched-loop.c
else
	cp $(LS)/drivers/block/loop.c patched-loop.c
	@echo "*** BEGINNING OF SECTION TO IGNORE PATCH ERRORS ***"
	patch -p0 -l -f --dry-run < $(PATCHNAME) || cp $(ORIGNAME) patched-loop.c
	@echo "*** END OF SECTION TO IGNORE PATCH ERRORS ***"
	patch -p0 -l -f < $(PATCHNAME)
endif
	$(CP1) $(CC) $(CFLAGS) $(EF) -DKBUILD_BASENAME=patched_loop -DKBUILD_MODNAME=loop -DEXPORT_SYMTAB -c $(PP1)patched-loop.c -o $(PP1)patched-loop.o
glue.o: glue.c aes.h md5.h
	$(CP1) $(CC) $(CFLAGS) $(EF) -DKBUILD_BASENAME=glue -DKBUILD_MODNAME=loop -c $(PP1)glue.c -o $(PP1)glue.o
aes.o: aes.c aes.h
	$(CP1) $(CC) $(CFLAGS) $(EF) -DKBUILD_BASENAME=aes -DKBUILD_MODNAME=loop -DDATA_ALWAYS_ALIGNED=1 -c $(PP1)aes.c -o $(PP1)aes.o
aes-x86.o: aes-x86.S
	$(CP1) $(CC) $(AFLAGS) -c $(PP1)aes-x86.S -o $(PP1)aes-x86.o
aes-amd64.o: aes-amd64.S
	$(CP1) $(CC) $(AFLAGS) -c $(PP1)aes-amd64.S -o $(PP1)aes-amd64.o
md5.o: md5.c md5.h
	$(CP1) $(CC) $(CFLAGS) $(EF) -DKBUILD_BASENAME=md5 -DKBUILD_MODNAME=loop -c $(PP1)md5.c -o $(PP1)md5.o
md5-x86.o: md5-x86.S
	$(CP1) $(CC) $(AFLAGS) -c $(PP1)md5-x86.S -o $(PP1)md5-x86.o
md5-amd64.o: md5-amd64.S
	$(CP1) $(CC) $(AFLAGS) -c $(PP1)md5-amd64.S -o $(PP1)md5-amd64.o

clean:
	rm -f *.o *.ko *.orig *.rej *.mod.c patched-loop.[ch] test-file[1234]
	rm -f -r test-dir1 tmp-d-kbuild

# tests can be run after loop.o and losetup are compiled and installed
TLD:=/dev/loop7
TEST_GPG_TYPES:=yes
TEST_UNHASHED_AES_TYPES:=yes
TEST_PARTITION_TO_TRASH:=none
tests:
	dd if=/dev/zero of=test-file1 bs=1024 count=33
	cp test-file1 test-file3
	echo 09876543210987654321 | /sbin/losetup -p 0 -e AES128 $(TLD) test-file3
	dd if=/dev/zero of=$(TLD) bs=1024 count=33 conv=notrunc
	/sbin/losetup -d $(TLD)
	make test-part2 CT=XOR    ITER=0  HF=sha256 GK= MD=d28220a1737763260f6e0109f141814a TF=test-file1 PSW=12345678901234567890
	make test-part2 CT=NONE   ITER=0  HF=sha256 GK= MD=0b08ceeb8b609b0885471ba25a23f5a5 TF=test-file1 PSW=12345678901234567890
	make test-part2 CT=AES128 ITER=0  HF=sha256 GK= MD=7c1cfd4fdd0d7cc847dd0942a2d48e48 TF=test-file1 PSW=12345678901234567890
	make test-part2 CT=AES192 ITER=0  HF=sha384 GK= MD=51c91bcc04ee2a4ca00310b519b3228c TF=test-file1 PSW=12345678901234567890
	make test-part2 CT=AES256 ITER=0  HF=sha512 GK= MD=1bf92ee337b653cdb32838047dec00fc TF=test-file1 PSW=12345678901234567890
	make test-part2 CT=AES256 ITER=0  HF=rmd160 GK= MD=c85eb59da18876ae71ebd838675c6ef4 TF=test-file1 PSW=12345678901234567890
	make test-part2 CT=AES256 ITER=10 HF=sha512 GK= MD=dadad48a6d3d9b9914199626ed7b710c TF=test-file1 PSW=12345678901234567890
ifeq ($(TEST_GPG_TYPES),yes)
	mkdir test-dir1
	make test-part2 CT=AES128 ITER=0 HF=sha256 GK="-K gpgkey1.asc -G test-dir1" MD=fa5c9a84bc8f6257830c3cbe60294c69 TF=test-file1 PSW=12345678901234567890
	make test-part2 CT=AES192 ITER=0 HF=sha384 GK="-K gpgkey1.asc -G test-dir1" MD=ddec9544a36100156aef353ec2bf9740 TF=test-file1 PSW=12345678901234567890
	make test-part2 CT=AES256 ITER=0 HF=sha512 GK="-K gpgkey1.asc -G test-dir1" MD=cb38b603f96f0deac1891d423983d69c TF=test-file1 PSW=12345678901234567890
	make test-part2 CT=AES128 ITER=0 HF=sha256 GK="-K gpgkey2.asc -G test-dir1" MD=f9825b79873f5c439ae9371c1a929a6c TF=test-file1 PSW=12345678901234567890
	make test-part2 CT=AES192 ITER=0 HF=sha384 GK="-K gpgkey2.asc -G test-dir1" MD=489991b779213f60219f09c575c08247 TF=test-file1 PSW=12345678901234567890
	make test-part2 CT=AES256 ITER=0 HF=sha512 GK="-K gpgkey2.asc -G test-dir1" MD=2a1d0d3fce83fbe5f3edcca95fbab3b7 TF=test-file1 PSW=12345678901234567890
endif
ifeq ($(TEST_UNHASHED_AES_TYPES),yes)
	make test-part2 CT=AES ITER=0 HF=unhashed1 GK= MD=293b09053055af7ca5235dc6a5bc0b74 TF=test-file1 PSW=1234567890123456789012345678901
	make test-part2 CT=AES ITER=0 HF=unhashed1 GK= MD=6b157917570250ef4370bf9acae49279 TF=test-file1 PSW=12345678901234567890123456789012
	make test-part2 CT=AES ITER=0 HF=unhashed1 GK= MD=6b157917570250ef4370bf9acae49279 TF=test-file1 PSW=123456789012345678901234567890123456789012
	make test-part2 CT=AES ITER=0 HF=unhashed1 GK= MD=e12fd55fbae9fc0e03517593e253e239 TF=test-file1 PSW=1234567890123456789012345678901234567890123
endif
ifneq ($(TEST_PARTITION_TO_TRASH),none)
	make test-part2 CT=AES128 ITER=0 HF=sha256 GK= MD=7c1cfd4fdd0d7cc847dd0942a2d48e48 TF=$(TEST_PARTITION_TO_TRASH) PSW=12345678901234567890
	make test-part2 CT=NONE   ITER=0 HF=sha256 GK= MD=0b08ceeb8b609b0885471ba25a23f5a5 TF=$(TEST_PARTITION_TO_TRASH) PSW=12345678901234567890
endif
	rm -f -r test-file[1234] test-dir1
	@echo "*** Test results ok ***"

test-part2:
	echo $(PSW) | /sbin/losetup -p 0 -e $(CT) -H $(HF) -C $(ITER) $(GK) $(TLD) $(TF)
	dd if=test-file3 of=$(TLD) bs=1024 count=33 conv=notrunc
	/sbin/losetup -d $(TLD)
	echo $(PSW) | /sbin/losetup -p 0 -e $(CT) -H $(HF) -C $(ITER) $(GK) $(TLD) $(TF)
	dd if=$(TLD) of=test-file4 bs=33792 count=1
	/sbin/losetup -d $(TLD)
ifneq ($(TF),test-file1)
	dd if=$(TF) of=test-file1 bs=33792 count=1
endif
	md5sum test-file1 >test-file2
	echo "$(MD)  test-file1" | cmp test-file2 -
	cmp test-file3 test-file4

.PHONY: all clean tests test-part2
