// File: /workspace/github_c_makefile_star2_O2/Sidd0107[P]EXT2-File-System/ext2_cp.c
// Function: main
int main(int argc, char **argv) {
    //Checks that valid no of arguments are provided
    if(argc != 4) {
        fprintf(stderr, "Usage: ext2_cp <image file name> <file path on local disk> <file path on ext2 image>\n");
        exit(1);
    }
    // Gets details from command args
	char *dest_filename = extract_filename(argv[3]);
	char *dest_directory = strrchr(argv[3], '/');
	if (!dest_directory){
	  fprintf(stderr, "destination directory does not exist");
        return ENOENT;
	}
    //Open's file to be copied
	int fd = open(argv[1], O_RDWR);
    disk = mmap(NULL, 128 * BLOCK_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if(disk == MAP_FAILED) {
		perror("mmap");
		exit(1);
    }
    //Gets super block and group descriptor
	struct ext2_super_block *sb = (struct ext2_super_block *)(disk + BLOCK_SIZE);
	struct ext2_group_desc *gd = (struct ext2_group_desc *)  (disk + 2048 + ((sb -> s_block_group_nr) *  sizeof(struct ext2_group_desc)));
	//Gets file info
	struct stat fileinfo;
	if (stat(argv[2], &fileinfo) == -1){
		free(dest_filename);
		perror("stat for input file failed");
		exit(1);
	}
	
	dest_directory[(dest_directory == argv[3])? 1: 0] = '\0';
	//Checks if destination exists
	if(file_exists(argv[3], get_block(disk, gd -> bg_inode_table, BLOCK_SIZE), dest_filename)){
		free(dest_filename);
		fprintf(stderr, "cp: file exists");
		return -1;
	}
    
	//should be safe 
	unsigned int file_size = fileinfo.st_size;
	//equivalent to ceiling of file_size / 1024
	int num_blocks_required = (file_size > 0) ? ((file_size-1) >> 10) + 1 + ((file_size > BLOCK_SIZE * 12)? 1:0)  : 0;
	//CHECK FOR INODE TO RESERVE
	int free_inode;
	if((free_inode = find_first_free_bit(get_block(disk, gd -> bg_inode_bitmap, BLOCK_SIZE), 0, 128)) == -1){
		free(dest_filename);
		fprintf(stderr, "cp: operation failed: No free inodes");
		exit(1);
	}
	//CHECK FOR BLOCKS TO RESERVE
	if(gd -> bg_free_inodes_count == 0){
		free(dest_filename);
		fprintf(stderr, "Insufficient free inodes for file.");
		exit(1);
	}
    //Loops through iblocks of directory to make sure there is enough space
	int next_free = 0, free_block_counter = 0, num_free_blocks = 0, i;
	unsigned int inodes_to_reserve[num_blocks_required];
	while(num_free_blocks < num_blocks_required){
		if((next_free = find_first_free_bit(get_block(disk, gd -> bg_block_bitmap, BLOCK_SIZE),
												free_block_counter, 128)) == -1){
			free(dest_filename);
			fprintf(stderr, "Insufficient free space for file.");
			exit(1);
		}
		inodes_to_reserve[num_free_blocks++] = next_free;
		free_block_counter = next_free + 1;
	}
	if(num_free_blocks != num_blocks_required){
		free(dest_filename);
		fprintf(stderr, "Insufficient free space for file.");
		exit(1);
	}
	
	unsigned int filename_length = strlen(dest_filename);
	unsigned int dir_entry_size = (8 + filename_length) + (4 - (8 + filename_length) % 4) % 4;

	
	//CHECK FOR DIRECTORY TO RESERVE
	struct ext2_dir_entry_2  * new_file = malloc(dir_entry_size);
	if(!new_file){
		perror("malloc");
		exit(1);
	}
	new_file -> inode = free_inode + 1;
	new_file -> name_len = strlen(dest_filename);
	new_file -> rec_len = dir_entry_size;
	new_file -> file_type = 1;
	memcpy(new_file -> name, dest_filename, strlen(dest_filename));
	int new_capacity = gd -> bg_free_blocks_count - num_blocks_required;

    
	struct basic_fileinfo destination = find_file(argv[3], get_block(disk, gd -> bg_inode_table, BLOCK_SIZE));
    //Checks to see if destination is suitable
    //(directory, correct inode num(not 2 and >11))
	if(destination.inode < 11 && destination.inode != 2){
		free(dest_filename);
		fprintf(stderr, "Invalid destination");
		free(new_file);
		exit(1);
	}
	if(destination.type != 'd'){
		free(dest_filename);
		fprintf(stderr, "cp: destination is not a directory");
		free(new_file);
		exit(1);
	}
    // Reserves spot for and adds this entry into the directory
    // for the new file(error if not enough space)
	if ((new_capacity = reserve_directory_entry(get_block(disk, gd -> bg_inode_table, BLOCK_SIZE), destination.inode, new_capacity, new_file, get_block(disk, gd -> bg_block_bitmap, BLOCK_SIZE))) == -1){
		free(dest_filename);
		fprintf(stderr, "cp: Not enough capacity for file");
		exit(-1);
	}
	
	free(new_file);
	//reduces free inode count as new file takes up a new inode
	gd -> bg_free_inodes_count --;
	//Sets inode details for new file.
	set_inode(get_inode(get_block(disk, gd -> bg_inode_table, BLOCK_SIZE), free_inode + 1),
	free_inode, file_size, inodes_to_reserve, 
	num_blocks_required, 
	get_block(disk, gd -> bg_block_bitmap, BLOCK_SIZE),
	get_block(disk, gd -> bg_inode_bitmap, BLOCK_SIZE),
	EXT2_S_IFREG);
	gd -> bg_free_blocks_count = new_capacity;
    //Opens file for copying
	int copyfd = open(argv[2], O_RDONLY);
	if(copyfd == -1){
		perror("failed to open input file");
	}
    //Copies the file content into the new destination file
	int bytes_read = 0, current_bytes_read;
	for(i = 0; i < ((num_blocks_required <= 12)? num_blocks_required : num_blocks_required - 1); i++){
		char *current_block = get_block_from_inode(get_block(disk, gd -> bg_inode_table, BLOCK_SIZE), free_inode + 1, i);
		if ((current_bytes_read = read(copyfd, current_block, BLOCK_SIZE)) <= 0){
			perror("read");
			exit(1);
		}
		bytes_read += current_bytes_read;
	}
	printf("Success copying %s: copied %d bytes\n", dest_filename, bytes_read);
	close(copyfd);
	free(dest_filename);
	return 0;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0xd8, %rsp
cmpl $0x4, %edi
jne 0x219c
movq %rsi, %r13
movq 0x18(%rsi), %rdi
callq 0x1ad9
movq %rax, %r14
movq 0x18(%r13), %rdi
pushq $0x2f
popq %rsi
callq 0x10a0
testq %rax, %rax
je 0x2104
movq %rax, %rbx
movq 0x8(%r13), %rdi
pushq $0x2
popq %rsi
xorl %eax, %eax
callq 0x1100
pushq $0x3
popq %rdx
pushq $0x1
popq %rcx
movl $0x20000, %esi          # imm = 0x20000
xorl %edi, %edi
movl %eax, %r8d
xorl %r9d, %r9d
callq 0x1080
movq %rax, 0x339f(%rip)      # 0x50b8
cmpq $-0x1, %rax
je 0x21b1
movq %rax, %r15
movzwl 0x45e(%rax), %r12d
movq 0x10(%r13), %rdi
leaq -0x100(%rbp), %rsi
callq 0x10d0
cmpl $-0x1, %eax
je 0x21ba
shll $0x5, %r12d
xorl %eax, %eax
cmpq 0x18(%r13), %rbx
sete %al
movb $0x0, (%rbx,%rax)
movq 0x18(%r13), %rbx
movq 0x3355(%rip), %rdi      # 0x50b8
movl 0x808(%r15,%r12), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x15f2
movq %rbx, %rdi
movq %rax, %rsi
movq %r14, %rdx
callq 0x12e7
testl %eax, %eax
jne 0x2126
movl -0xd0(%rbp), %eax
testl %eax, %eax
movq %rax, -0x70(%rbp)
je 0x1dab
leal -0x1(%rax), %ebx
shrl $0xa, %ebx
cmpl $0x3001, %eax           # imm = 0x3001
sbbl $-0x1, %ebx
incl %ebx
jmp 0x1dad
xorl %ebx, %ebx
movq 0x3304(%rip), %rdi      # 0x50b8
movl 0x804(%r15,%r12), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x15f2
movq %rax, %rdi
xorl %esi, %esi
movl $0x80, %edx
callq 0x17c7
movl %eax, -0x2c(%rbp)
cmpl $-0x1, %eax
je 0x21cb
movq %r12, %rax
movq %r13, -0x60(%rbp)
movq %r14, -0x38(%rbp)
cmpw $0x0, 0x80e(%r15,%r12)
je 0x21e8
movq %r15, %r14
movq %rax, %r12
movq %rsp, -0x68(%rbp)
movl -0x2c(%rbp), %eax
movl %eax, -0x54(%rbp)
movl %ebx, -0x3c(%rbp)
movl %ebx, %r13d
movq %rsp, %rcx
leaq 0xf(,%r13,4), %rax
andq $-0x10, %rax
subq %rax, %rcx
movq %rcx, -0x48(%rbp)
movq %rcx, %rsp
xorl %ebx, %ebx
xorl %r15d, %r15d
cmpq %rbx, %r13
je 0x1e7a
movq 0x327c(%rip), %rdi      # 0x50b8
movl 0x800(%r14,%r12), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x15f2
movq %rax, %rdi
movl %r15d, %esi
movl $0x80, %edx
callq 0x17c7
cmpl $-0x1, %eax
je 0x2161
movl %eax, %r15d
movq -0x48(%rbp), %rax
movl %r15d, (%rax,%rbx,4)
incq %rbx
incl %r15d
jmp 0x1e30
movq -0x38(%rbp), %rbx
movq %rbx, %rdi
callq 0x1070
movl %eax, %ecx
negl %ecx
andl $0x3, %ecx
leal (%rax,%rcx), %r13d
addl $0x8, %r13d
movq %r13, %rdi
callq 0x10f0
testq %rax, %rax
je 0x2209
movq %rax, %r15
movl -0x2c(%rbp), %eax
incl %eax
movl %eax, -0x2c(%rbp)
movl %eax, (%r15)
movq %rbx, %rdi
callq 0x1070
movb %al, 0x6(%r15)
movw %r13w, 0x4(%r15)
movb $0x1, 0x7(%r15)
movq %r15, %r13
addq $0x8, %r13
movq %rbx, %rdi
callq 0x1070
movq %r13, %rdi
movq %rbx, %rsi
movq %rax, %rdx
callq 0x10e0
movzwl 0x80c(%r14,%r12), %ebx
movq -0x60(%rbp), %rax
movq 0x18(%rax), %r13
movq 0x31b9(%rip), %rdi      # 0x50b8
movl 0x808(%r14,%r12), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x15f2
movq %r13, %rdi
movq %rax, %rsi
callq 0x131d
movq %rax, %r13
shrq $0x20, %r13
cmpq $0xb, %r13
setae %cl
cmpq $0x2, %r13
sete %dl
orb %cl, %dl
je 0x2215
cmpb $0x64, %al
movl -0x3c(%rbp), %eax
jne 0x2233
subl %eax, %ebx
movq 0x316b(%rip), %rdi      # 0x50b8
movl 0x808(%r14,%r12), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x15f2
movq %rax, -0x50(%rbp)
movq 0x314e(%rip), %rdi      # 0x50b8
movl 0x800(%r14,%r12), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x15f2
movq -0x50(%rbp), %rdi
movl %r13d, %esi
movl %ebx, %edx
movq %r15, %rcx
movq %rax, %r8
callq 0x1972
movl %eax, -0x50(%rbp)
cmpl $-0x1, %eax
je 0x2265
movq %r15, %rdi
callq 0x1030
decw 0x80e(%r14,%r12)
movq 0x3104(%rip), %rdi      # 0x50b8
movl 0x808(%r14,%r12), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x15f2
movq %rax, %rdi
movl -0x2c(%rbp), %esi
callq 0x15e2
movq %rax, %r15
movq 0x30dd(%rip), %rdi      # 0x50b8
movl 0x800(%r14,%r12), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x15f2
movq %rax, %r13
movq 0x30c1(%rip), %rdi      # 0x50b8
movl 0x804(%r14,%r12), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x15f2
movq %r15, %rdi
movl -0x54(%rbp), %esi
movq -0x70(%rbp), %rdx
movq -0x48(%rbp), %rcx
movl -0x3c(%rbp), %ebx
movl %ebx, %r8d
movq %r13, %r9
pushq $0x8000                 # imm = 0x8000
pushq %rax
callq 0x1821
popq %rax
popq %rcx
movq %r12, -0x48(%rbp)
movl -0x50(%rbp), %ecx
movw %cx, 0x80c(%r14,%r12)
movq -0x60(%rbp), %rax
movq 0x10(%rax), %rdi
xorl %r15d, %r15d
xorl %esi, %esi
xorl %eax, %eax
callq 0x1100
movl %ebx, %r12d
movl %eax, %ebx
cmpl $-0x1, %eax
je 0x2150
xorl %eax, %eax
cmpl $0xd, %r12d
setge %al
subl %eax, %r12d
testl %r12d, %r12d
cmovlel %r15d, %r12d
xorl %r13d, %r13d
cmpl %r15d, %r12d
je 0x20c4
movq 0x3037(%rip), %rdi      # 0x50b8
movq -0x48(%rbp), %rax
movl 0x808(%r14,%rax), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x15f2
movq %rax, %rdi
movl -0x2c(%rbp), %esi
movl %r15d, %edx
callq 0x1627
movl $0x400, %edx            # imm = 0x400
movl %ebx, %edi
movq %rax, %rsi
callq 0x10c0
testl %eax, %eax
jle 0x2188
addl %eax, %r13d
incl %r15d
jmp 0x2075
leaq 0x1120(%rip), %rdi      # 0x31eb
xorl %r14d, %r14d
movq -0x38(%rbp), %r15
movq %r15, %rsi
movl %r13d, %edx
xorl %eax, %eax
callq 0x1090
movl %ebx, %edi
callq 0x10b0
movq %r15, %rdi
callq 0x1030
movq -0x68(%rbp), %rsp
movl %r14d, %eax
leaq -0x28(%rbp), %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
pushq $0x2
popq %r14
movq 0x2ee9(%rip), %rax      # 0x4ff8
movq (%rax), %rcx
leaq 0xf9c(%rip), %rdi       # 0x30b5
pushq $0x24
popq %rsi
pushq $0x1
popq %rdx
callq 0x1130
jmp 0x20f2
movq %r14, %rdi
callq 0x1030
movq 0x2ec3(%rip), %rax      # 0x4ff8
movq (%rax), %rcx
leaq 0xfbb(%rip), %rdi       # 0x30fa
pushq $0xf
popq %rsi
pushq $0x1
popq %rdx
callq 0x1130
pushq $-0x1
popq %r14
jmp 0x20f2
leaq 0x1075(%rip), %rdi      # 0x31cc
callq 0x1110
jmp 0x205f
movq -0x38(%rbp), %rdi
callq 0x1030
movq 0x2e87(%rip), %rax      # 0x4ff8
movq (%rax), %rcx
leaq 0xfd7(%rip), %rdi       # 0x3152
pushq $0x21
popq %rsi
pushq $0x1
popq %rdx
callq 0x1130
jmp 0x2194
leaq 0x1057(%rip), %rdi      # 0x31e6
callq 0x1110
pushq $0x1
popq %rdi
callq 0x1120
movq 0x2e55(%rip), %rax      # 0x4ff8
movq (%rax), %rcx
leaq 0xeb2(%rip), %rdi       # 0x305f
pushq $0x55
jmp 0x217d
leaq 0xf22(%rip), %rdi       # 0x30da
jmp 0x218f
movq %r14, %rdi
callq 0x1030
leaq 0xf16(%rip), %rdi       # 0x30df
jmp 0x218f
movq %r14, %rdi
callq 0x1030
movq 0x2e1e(%rip), %rax      # 0x4ff8
movq (%rax), %rcx
leaq 0xf26(%rip), %rdi       # 0x310a
pushq $0x24
jmp 0x217d
movq -0x38(%rbp), %rdi
callq 0x1030
movq 0x2e00(%rip), %rax      # 0x4ff8
movq (%rax), %rcx
leaq 0xf2d(%rip), %rdi       # 0x312f
pushq $0x22
jmp 0x217d
leaq 0xdf4(%rip), %rdi       # 0x3004
jmp 0x218f
movq -0x38(%rbp), %rdi
callq 0x1030
movq 0x2dd3(%rip), %rax      # 0x4ff8
movq (%rax), %rcx
leaq 0xf45(%rip), %rdi       # 0x3174
pushq $0x13
jmp 0x224f
movq -0x38(%rbp), %rdi
callq 0x1030
movq 0x2db5(%rip), %rax      # 0x4ff8
movq (%rax), %rcx
leaq 0xf3b(%rip), %rdi       # 0x3188
pushq $0x22
popq %rsi
pushq $0x1
popq %rdx
callq 0x1130
movq %r15, %rdi
callq 0x1030
jmp 0x2194
movq -0x38(%rbp), %rdi
callq 0x1030
movq 0x2d83(%rip), %rax      # 0x4ff8
movq (%rax), %rcx
leaq 0xf2c(%rip), %rdi       # 0x31ab
pushq $0x20
popq %rsi
pushq $0x1
popq %rdx
callq 0x1130
pushq $-0x1
jmp 0x2196