// File: /workspace/github_c_makefile_star2_O2/sheheryarbutt[P]Ext2-File-System/ext2_cp.c
// Function: main
int main(int argc, char **argv) {
	unsigned char data[EXT2_BLOCK_SIZE];
	FILE * source;
	struct stat source_stat;


    if(argc != 4) {
        fprintf(stderr, "Usage: ext2_cp <image file name> <source path> <dest path>\n");
        exit(1);
    }
    int fd = open(argv[1], O_RDWR);

    disk = mmap(NULL, 128 * 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if(disk == MAP_FAILED) {
	perror("mmap");
	exit(1);
    }
    // setting global vars
   	sb = (struct ext2_super_block *)(disk + EXT2_BLOCK_SIZE);
    gd = (struct ext2_group_desc *)(disk + (2*EXT2_BLOCK_SIZE));
	inode_table = (struct ext2_inode *)(disk + (5*EXT2_BLOCK_SIZE));

	char * path= argv[2];
	char * dest= argv[3];


	source = fopen(path,"rb");

	if (source==NULL){
		// source path does not exist
		fprintf(stderr, "ERROR: %s\n", strerror(ENOENT));     
		exit(ENOENT);
	}

	if(lstat(path,&source_stat)<0){
		fclose(source);
		fprintf(stderr, "ERROR: %s\n", strerror(EXIT_FAILURE));     
		exit(EXIT_FAILURE);
	}

	// need for pathTraversal function
	unsigned int * parent_block= malloc(sizeof(unsigned int *));

	int dest_inode=pathTraversal(dest,disk,parent_block);

	if (dest_inode!=0){
		// already file/directory exist in destination path
		free(parent_block);
		fclose(source);
		fprintf(stderr, "ERROR: %s\n", strerror(EEXIST));     
		exit(EEXIST);
	}
	
	char *name;

	free(parent_block);

	char *cpyPath=malloc((unsigned int)strlen(dest) + 1);
	strcpy(cpyPath,dest);

	parent_block= malloc(sizeof(unsigned int *));
	
	name=basename(cpyPath);
	if (strlen(name)>EXT2_NAME_LEN){
		fclose(source);
		fprintf(stderr, "ERROR: %s\n", strerror(ENAMETOOLONG));     
		exit(ENAMETOOLONG);
	}

	dest[strlen(dest)-(strlen(name))]='\0';

	dest_inode=pathTraversal(dest,disk,parent_block);
	free(parent_block);

	if (dest_inode==0){
		//destination path does not exist
		fclose(source);
		fprintf(stderr, "ERROR: %s\n", strerror(ENOENT));     
		exit(ENOENT);
	}

	int source_size = (int)source_stat.st_size;
 	int blocks_needed = ceil((double)source_size/EXT2_BLOCK_SIZE);
	//no space to copy file

	if ((gd->bg_free_blocks_count<1+blocks_needed) || (gd->bg_free_inodes_count<1) ){
		fclose(source);
		fprintf(stderr, "ERROR: %s\n", strerror(ENOSPC));     
		exit(ENOSPC);
	}
		//can only copy regular files
	if(!(S_ISREG(source_stat.st_mode))){
		fclose(source);
		fprintf(stderr, "ERROR: %s\n", strerror(EPERM));     
		exit(EPERM);
	}
	else {
			// inode for new file and datablock for dir entry for new file
		int avail_ib;
		avail_ib= set_avail_ib();
		int avail_db;
		avail_db= set_avail_db();
			//find next block pointer to use for new dir entry
		int block_pointer=0; 
		while(inode_table[dest_inode-1].i_block[block_pointer]!=0){
			block_pointer++;
		}
			//update dir info
		inode_table[dest_inode-1].i_block[block_pointer]=avail_db;
		inode_table[dest_inode-1].i_blocks+=2;
		inode_table[dest_inode-1].i_size+=EXT2_BLOCK_SIZE;
			//create dir entry for new file
		create_entry((EXT2_BLOCK_SIZE*avail_db),(unsigned int)avail_ib,(unsigned short)EXT2_BLOCK_SIZE,(unsigned char)strlen(name),(unsigned char)EXT2_FT_REG_FILE,name);

			// copy data from source file into new datablocks

		unsigned int * blocks=malloc(sizeof(unsigned int)*15);
		int read_so_far;
		for (int i=0;i<12;i++){

			if (blocks_needed!=0){
				int new_db = set_avail_db();
				blocks[i]=new_db;
				read_so_far = fread(data, 1, EXT2_BLOCK_SIZE, source);
				memcpy(disk+(EXT2_BLOCK_SIZE*new_db),data,read_so_far);
				blocks_needed--;
			}else{
				blocks[i]=0;
			}

		}
			// copy data from source file using indirection
		if (blocks_needed!=0){
			int indirect_block = set_avail_db();
				// new indirect block pointer for new file
			unsigned int * block_num_table = malloc(EXT2_BLOCK_SIZE);

			for(int i=0;i<(EXT2_BLOCK_SIZE/sizeof(unsigned int));i++){ 
				if (blocks_needed != 0){
					unsigned int new_db = (unsigned int)set_avail_db();
					read_so_far = fread(data, 1, EXT2_BLOCK_SIZE, source);
					memcpy(disk+(EXT2_BLOCK_SIZE*new_db),data,read_so_far);
					block_num_table[i] =(unsigned int)new_db;
					blocks_needed--;

				}else{
					block_num_table[i]=(unsigned int)0;
				}
			}
				// store block array at indirect block pointer for new file
			memcpy(disk+(EXT2_BLOCK_SIZE*indirect_block),block_num_table,EXT2_BLOCK_SIZE);
			blocks[12]=indirect_block;

		}
			// create inode for new file
		fclose(source);
		blocks_needed = ceil((double)source_size/EXT2_BLOCK_SIZE);

		create_inode(avail_ib,EXT2_S_IFREG,source_size,1,blocks_needed*2,blocks);

	}
	return 0;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x4b8, %rsp            # imm = 0x4B8
cmpl $0x4, %edi
jne 0x1634
movq %rsi, %rbx
movq 0x8(%rsi), %rdi
pushq $0x2
popq %rsi
xorl %eax, %eax
callq 0x1110
pushq $0x3
popq %rdx
pushq $0x1
popq %rcx
movl $0x20000, %esi          # imm = 0x20000
xorl %edi, %edi
movl %eax, %r8d
xorl %r9d, %r9d
callq 0x1090
movq %rax, 0x2e0f(%rip)      # 0x40d8
cmpq $-0x1, %rax
je 0x1652
leaq 0x400(%rax), %rcx
movq %rcx, 0x2dff(%rip)      # 0x40e0
leaq 0x800(%rax), %rcx
movq %rcx, 0x2df9(%rip)      # 0x40e8
addq $0x1400, %rax           # imm = 0x1400
movq %rax, 0x2df4(%rip)      # 0x40f0
movq 0x10(%rbx), %r15
movq 0x18(%rbx), %r14
leaq 0xd46(%rip), %rsi       # 0x2051
movq %r15, %rdi
callq 0x1120
movq %rax, -0x30(%rbp)
testq %rax, %rax
je 0x169d
leaq -0xd8(%rbp), %rsi
movq %r15, %rdi
callq 0x10a0
testl %eax, %eax
js 0x1603
pushq $0x8
popq %rdi
callq 0x1100
movq %rax, %r15
movq 0x2d8f(%rip), %rsi      # 0x40d8
movq %r14, %rdi
movq %rax, %rdx
callq 0x1801
movl %eax, %r12d
movq %r15, %rdi
callq 0x1030
testl %r12d, %r12d
jne 0x1666
movq %r14, %rdi
callq 0x1080
leal 0x1(%rax), %edi
callq 0x1100
movq %rax, %r12
movq %rax, %rdi
movq %r14, %rsi
callq 0x1050
pushq $0x8
popq %rdi
callq 0x1100
movq %rax, %r15
movq %r12, %rdi
callq 0x10e0
movq %rax, %r12
movq %rax, %rdi
callq 0x1080
cmpq $0x100, %rax            # imm = 0x100
jae 0x167d
movq %rax, %r13
movq %r14, %rdi
callq 0x1080
subq %r13, %rax
movb $0x0, (%r14,%rax)
movq 0x2d0e(%rip), %rsi      # 0x40d8
movq %r14, %rdi
movq %r15, %rdx
callq 0x1801
movl %eax, %r14d
movq %r15, %rdi
callq 0x1030
testl %r14d, %r14d
je 0x1694
movl -0xa8(%rbp), %ebx
cvtsi2sd %ebx, %xmm0
mulsd 0xc0d(%rip), %xmm0      # 0x2008
callq 0x1170
cvttsd2si %xmm0, %edx
movq 0x2cdd(%rip), %rax      # 0x40e8
movzwl 0xc(%rax), %ecx
movl %edx, -0x34(%rbp)
cmpl %ecx, %edx
jge 0x161d
cmpw $0x0, 0xe(%rax)
je 0x161d
movl $0xf000, %eax           # imm = 0xF000
andl -0xc0(%rbp), %eax
cmpl $0x8000, %eax           # imm = 0x8000
jne 0x1603
movl %ebx, -0x40(%rbp)
xorl %eax, %eax
callq 0x1a6d
movl %eax, %ebx
xorl %eax, %eax
callq 0x1a00
movl %eax, %r15d
movq 0x2c98(%rip), %rax      # 0x40f0
movslq %r14d, %rcx
decq %rcx
shlq $0x7, %rcx
leaq (%rcx,%rax), %rdx
addq $0x28, %rdx
cmpl $0x0, (%rdx)
leaq 0x4(%rdx), %rdx
jne 0x146a
movl %r15d, -0x4(%rdx)
addl $0x2, 0x1c(%rax,%rcx)
addl $0x400, 0x4(%rax,%rcx)  # imm = 0x400
shll $0xa, %r15d
movq %r12, %rdi
callq 0x1080
movzbl %al, %ecx
pushq $0x1
popq %r8
movl %r15d, %edi
movl %ebx, -0x3c(%rbp)
movl %ebx, %esi
movl $0x400, %edx            # imm = 0x400
movq %r12, %r9
callq 0x1b85
pushq $0x3c
popq %rdi
callq 0x1100
movq %rax, %r12
leaq -0x4e0(%rbp), %r14
xorl %r15d, %r15d
movl -0x34(%rbp), %ebx
cmpq $0xc, %r15
je 0x1519
testl %ebx, %ebx
je 0x150d
xorl %eax, %eax
callq 0x1a00
movl %eax, %r13d
movl %eax, (%r12,%r15,4)
movl $0x400, %edx            # imm = 0x400
movq %r14, %rdi
pushq $0x1
popq %rsi
movq -0x30(%rbp), %rcx
callq 0x1060
shll $0xa, %r13d
movslq %r13d, %rdi
addq 0x2bda(%rip), %rdi      # 0x40d8
movslq %eax, %rdx
movq %r14, %rsi
callq 0x10d0
decl %ebx
jmp 0x1514
andl $0x0, (%r12,%r15,4)
xorl %ebx, %ebx
incq %r15
jmp 0x14c4
testl %ebx, %ebx
je 0x15c7
xorl %eax, %eax
callq 0x1a00
movl %eax, -0x38(%rbp)
movl $0x400, %edi            # imm = 0x400
callq 0x1100
movq %rax, -0x48(%rbp)
xorl %r15d, %r15d
cmpq $0x100, %r15            # imm = 0x100
je 0x15a2
testl %ebx, %ebx
je 0x1590
xorl %eax, %eax
callq 0x1a00
movl %eax, %r13d
movl $0x400, %edx            # imm = 0x400
movq %r12, %r14
leaq -0x4e0(%rbp), %r12
movq %r12, %rdi
pushq $0x1
popq %rsi
movq -0x30(%rbp), %rcx
callq 0x1060
movl %r13d, %edi
shll $0xa, %edi
addq 0x2b5a(%rip), %rdi      # 0x40d8
movslq %eax, %rdx
movq %r12, %rsi
movq %r14, %r12
callq 0x10d0
decl %ebx
jmp 0x1595
xorl %r13d, %r13d
xorl %ebx, %ebx
movq -0x48(%rbp), %rax
movl %r13d, (%rax,%r15,4)
incq %r15
jmp 0x153c
movl -0x38(%rbp), %ebx
movl %ebx, %eax
shll $0xa, %eax
movslq %eax, %rdi
addq 0x2b24(%rip), %rdi      # 0x40d8
movl $0x400, %edx            # imm = 0x400
movq -0x48(%rbp), %rsi
callq 0x10d0
movl %ebx, 0x30(%r12)
movq -0x30(%rbp), %rdi
callq 0x1070
movl -0x34(%rbp), %eax
addl %eax, %eax
movzwl %ax, %r8d
pushq $0x1
popq %rcx
movl -0x3c(%rbp), %edi
movl $0x8000, %esi           # imm = 0x8000
movl -0x40(%rbp), %edx
movq %r12, %r9
callq 0x1ac6
xorl %eax, %eax
addq $0x4b8, %rsp            # imm = 0x4B8
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq -0x30(%rbp), %rdi
callq 0x1070
movq 0x29e5(%rip), %rax      # 0x3ff8
movq (%rax), %rbx
pushq $0x1
jmp 0x16a9
movq -0x30(%rbp), %rdi
callq 0x1070
movq 0x29cb(%rip), %rax      # 0x3ff8
movq (%rax), %rbx
pushq $0x1c
jmp 0x16a9
movq 0x29bd(%rip), %rax      # 0x3ff8
movq (%rax), %rcx
leaq 0x9cb(%rip), %rdi       # 0x2010
pushq $0x3b
popq %rsi
pushq $0x1
popq %rdx
callq 0x1150
jmp 0x165e
leaq 0x9f3(%rip), %rdi       # 0x204c
callq 0x1130
pushq $0x1
popq %rdi
callq 0x1140
movq -0x30(%rbp), %rdi
callq 0x1070
movq 0x2982(%rip), %rax      # 0x3ff8
movq (%rax), %rbx
pushq $0x11
jmp 0x16a9
movq -0x30(%rbp), %rdi
callq 0x1070
movq 0x296b(%rip), %rax      # 0x3ff8
movq (%rax), %rbx
pushq $0x24
jmp 0x16a9
movq -0x30(%rbp), %rdi
callq 0x1070
movq 0x2954(%rip), %rax      # 0x3ff8
movq (%rax), %rbx
pushq $0x2
popq %r14
movl %r14d, %edi
callq 0x1160
leaq 0x99a(%rip), %rsi       # 0x2054
movq %rbx, %rdi
movq %rax, %rdx
xorl %eax, %eax
callq 0x10c0
movl %r14d, %edi
callq 0x1140
nop