// File: /workspace/github_c_makefile_star2_O0/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
subq $0x1a0, %rsp            # imm = 0x1A0
movl $0x0, -0x4(%rbp)
movl %edi, -0x8(%rbp)
movq %rsi, -0x10(%rbp)
cmpl $0x4, -0x8(%rbp)
je 0x23d1
movq 0x2c42(%rip), %rax      # 0x4ff8
movq (%rax), %rdi
leaq 0xcc2(%rip), %rsi       # 0x3082
movb $0x0, %al
callq 0x10c0
movl $0x1, %edi
callq 0x1120
movq -0x10(%rbp), %rax
movq 0x18(%rax), %rdi
callq 0x20b0
movq %rax, -0x18(%rbp)
movq -0x10(%rbp), %rax
movq 0x18(%rax), %rdi
movl $0x2f, %esi
callq 0x1090
movq %rax, -0x20(%rbp)
cmpq $0x0, -0x20(%rbp)
jne 0x2423
movq 0x2bf2(%rip), %rax      # 0x4ff8
movq (%rax), %rdi
leaq 0xcc8(%rip), %rsi       # 0x30d8
movb $0x0, %al
callq 0x10c0
movl $0x2, -0x4(%rbp)
jmp 0x2c70
movq -0x10(%rbp), %rax
movq 0x8(%rax), %rdi
movl $0x2, %esi
movb $0x0, %al
callq 0x1100
movl %eax, -0x24(%rbp)
movl -0x24(%rbp), %r8d
xorl %eax, %eax
movl %eax, %r9d
movl $0x20000, %esi          # imm = 0x20000
movl $0x3, %edx
movl $0x1, %ecx
movq %r9, %rdi
callq 0x1070
movq %rax, 0x2c4f(%rip)      # 0x50b0
movq $-0x1, %rax
cmpq %rax, 0x2c41(%rip)      # 0x50b0
jne 0x2487
leaq 0xc85(%rip), %rdi       # 0x30fd
callq 0x1110
movl $0x1, %edi
callq 0x1120
movq 0x2c22(%rip), %rax      # 0x50b0
addq $0x400, %rax            # imm = 0x400
movq %rax, -0x30(%rbp)
movq 0x2c11(%rip), %rax      # 0x50b0
addq $0x800, %rax            # imm = 0x800
movq -0x30(%rbp), %rcx
movzwl 0x5e(%rcx), %ecx
shlq $0x5, %rcx
addq %rcx, %rax
movq %rax, -0x38(%rbp)
movq -0x10(%rbp), %rax
movq 0x10(%rax), %rdi
leaq -0xc8(%rbp), %rsi
callq 0x10d0
cmpl $-0x1, %eax
jne 0x24f0
movq -0x18(%rbp), %rdi
callq 0x1030
leaq 0xc21(%rip), %rdi       # 0x3102
callq 0x1110
movl $0x1, %edi
callq 0x1120
movq -0x20(%rbp), %rax
movq -0x20(%rbp), %rsi
movq -0x10(%rbp), %rcx
movq 0x18(%rcx), %rdi
xorl %ecx, %ecx
movl $0x1, %edx
cmpq %rdi, %rsi
cmovel %edx, %ecx
movslq %ecx, %rcx
movb $0x0, (%rax,%rcx)
movq -0x10(%rbp), %rax
movq 0x18(%rax), %rax
movq %rax, -0x130(%rbp)
movq 0x2b86(%rip), %rdi      # 0x50b0
movq -0x38(%rbp), %rax
movl 0x8(%rax), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x17f0
movq -0x130(%rbp), %rdi
movq %rax, %rsi
movq -0x18(%rbp), %rdx
callq 0x13a0
cmpl $0x0, %eax
je 0x2580
movq -0x18(%rbp), %rdi
callq 0x1030
movq 0x2a95(%rip), %rax      # 0x4ff8
movq (%rax), %rdi
leaq 0xbb0(%rip), %rsi       # 0x311d
movb $0x0, %al
callq 0x10c0
movl $0xffffffff, -0x4(%rbp) # imm = 0xFFFFFFFF
jmp 0x2c70
movq -0x98(%rbp), %rax
movl %eax, -0xcc(%rbp)
cmpl $0x0, -0xcc(%rbp)
jbe 0x25c5
movl -0xcc(%rbp), %eax
subl $0x1, %eax
shrl $0xa, %eax
addl $0x1, %eax
movl -0xcc(%rbp), %esi
xorl %ecx, %ecx
movl $0x1, %edx
cmpl $0x3000, %esi           # imm = 0x3000
cmoval %edx, %ecx
addl %ecx, %eax
movl %eax, -0x134(%rbp)
jmp 0x25cf
xorl %eax, %eax
movl %eax, -0x134(%rbp)
jmp 0x25cf
movl -0x134(%rbp), %eax
movl %eax, -0xd0(%rbp)
movq 0x2ace(%rip), %rdi      # 0x50b0
movq -0x38(%rbp), %rax
movl 0x4(%rax), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x17f0
movq %rax, %rdi
xorl %esi, %esi
movl $0x80, %edx
callq 0x1b80
movl %eax, -0xd4(%rbp)
cmpl $-0x1, %eax
jne 0x2638
movq -0x18(%rbp), %rdi
callq 0x1030
movq 0x29db(%rip), %rax      # 0x4ff8
movq (%rax), %rdi
leaq 0xb06(%rip), %rsi       # 0x312d
movb $0x0, %al
callq 0x10c0
movl $0x1, %edi
callq 0x1120
movq -0x38(%rbp), %rax
movzwl 0xe(%rax), %eax
cmpl $0x0, %eax
jne 0x2670
movq -0x18(%rbp), %rdi
callq 0x1030
movq 0x29a3(%rip), %rax      # 0x4ff8
movq (%rax), %rdi
leaq 0xaf3(%rip), %rsi       # 0x3152
movb $0x0, %al
callq 0x10c0
movl $0x1, %edi
callq 0x1120
movl $0x0, -0xd8(%rbp)
movl $0x0, -0xdc(%rbp)
movl $0x0, -0xe0(%rbp)
movl -0xd0(%rbp), %eax
movq %rsp, %rcx
movq %rcx, -0xf0(%rbp)
leaq 0xf(,%rax,4), %rdx
andq $-0x10, %rdx
movq %rsp, %rcx
subq %rdx, %rcx
movq %rcx, -0x140(%rbp)
movq %rcx, %rsp
movq %rax, -0xf8(%rbp)
movl -0xe0(%rbp), %eax
cmpl -0xd0(%rbp), %eax
jge 0x276b
movq 0x29d6(%rip), %rdi      # 0x50b0
movq -0x38(%rbp), %rax
movl (%rax), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x17f0
movq %rax, %rdi
movl -0xdc(%rbp), %esi
movl $0x80, %edx
callq 0x1b80
movl %eax, -0xd8(%rbp)
cmpl $-0x1, %eax
jne 0x2733
movq -0x18(%rbp), %rdi
callq 0x1030
movq 0x28e0(%rip), %rax      # 0x4ff8
movq (%rax), %rdi
leaq 0xa53(%rip), %rsi       # 0x3175
movb $0x0, %al
callq 0x10c0
movl $0x1, %edi
callq 0x1120
movq -0x140(%rbp), %rax
movl -0xd8(%rbp), %edx
movl -0xe0(%rbp), %ecx
movl %ecx, %esi
addl $0x1, %esi
movl %esi, -0xe0(%rbp)
movslq %ecx, %rcx
movl %edx, (%rax,%rcx,4)
movl -0xd8(%rbp), %eax
addl $0x1, %eax
movl %eax, -0xdc(%rbp)
jmp 0x26c1
movl -0xe0(%rbp), %eax
cmpl -0xd0(%rbp), %eax
je 0x27a4
movq -0x18(%rbp), %rdi
callq 0x1030
movq 0x286f(%rip), %rax      # 0x4ff8
movq (%rax), %rdi
leaq 0x9e2(%rip), %rsi       # 0x3175
movb $0x0, %al
callq 0x10c0
movl $0x1, %edi
callq 0x1120
movq -0x18(%rbp), %rdi
callq 0x1060
movl %eax, -0xfc(%rbp)
movl -0xfc(%rbp), %eax
addl $0x8, %eax
movl -0xfc(%rbp), %edx
addl $0x8, %edx
andl $0x3, %edx
movl $0x4, %ecx
subl %edx, %ecx
andl $0x3, %ecx
addl %ecx, %eax
movl %eax, -0x100(%rbp)
movl -0x100(%rbp), %eax
movl %eax, %edi
callq 0x10f0
movq %rax, -0x108(%rbp)
cmpq $0x0, -0x108(%rbp)
jne 0x280e
leaq 0x825(%rip), %rdi       # 0x3024
callq 0x1110
movl $0x1, %edi
callq 0x1120
movl -0xd4(%rbp), %ecx
addl $0x1, %ecx
movq -0x108(%rbp), %rax
movl %ecx, (%rax)
movq -0x18(%rbp), %rdi
callq 0x1060
movb %al, %cl
movq -0x108(%rbp), %rax
movb %cl, 0x6(%rax)
movl -0x100(%rbp), %eax
movw %ax, %cx
movq -0x108(%rbp), %rax
movw %cx, 0x4(%rax)
movq -0x108(%rbp), %rax
movb $0x1, 0x7(%rax)
movq -0x108(%rbp), %rax
addq $0x8, %rax
movq %rax, -0x158(%rbp)
movq -0x18(%rbp), %rax
movq %rax, -0x150(%rbp)
movq -0x18(%rbp), %rdi
callq 0x1060
movq -0x158(%rbp), %rdi
movq -0x150(%rbp), %rsi
movq %rax, %rdx
callq 0x10e0
movq -0x38(%rbp), %rax
movzwl 0xc(%rax), %eax
subl -0xd0(%rbp), %eax
movl %eax, -0x10c(%rbp)
movq -0x10(%rbp), %rax
movq 0x18(%rax), %rax
movq %rax, -0x148(%rbp)
movq 0x27f6(%rip), %rdi      # 0x50b0
movq -0x38(%rbp), %rax
movl 0x8(%rax), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x17f0
movq -0x148(%rbp), %rdi
movq %rax, %rsi
callq 0x13f0
movq %rax, -0x114(%rbp)
cmpl $0xb, -0x110(%rbp)
jae 0x292a
cmpl $0x2, -0x110(%rbp)
je 0x292a
movq -0x18(%rbp), %rdi
callq 0x1030
movq 0x26f5(%rip), %rax      # 0x4ff8
movq (%rax), %rdi
leaq 0x88a(%rip), %rsi       # 0x3197
movb $0x0, %al
callq 0x10c0
movq -0x108(%rbp), %rdi
callq 0x1030
movl $0x1, %edi
callq 0x1120
movsbl -0x114(%rbp), %eax
cmpl $0x64, %eax
je 0x296d
movq -0x18(%rbp), %rdi
callq 0x1030
movq 0x26b2(%rip), %rax      # 0x4ff8
movq (%rax), %rdi
leaq 0x85b(%rip), %rsi       # 0x31ab
movb $0x0, %al
callq 0x10c0
movq -0x108(%rbp), %rdi
callq 0x1030
movl $0x1, %edi
callq 0x1120
movq 0x273c(%rip), %rdi      # 0x50b0
movq -0x38(%rbp), %rax
movl 0x8(%rax), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x17f0
movq %rax, -0x170(%rbp)
movl -0x110(%rbp), %eax
movl %eax, -0x168(%rbp)
movl -0x10c(%rbp), %eax
movl %eax, -0x164(%rbp)
movq -0x108(%rbp), %rax
movq %rax, -0x160(%rbp)
movq 0x26f7(%rip), %rdi      # 0x50b0
movq -0x38(%rbp), %rax
movl (%rax), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x17f0
movq -0x170(%rbp), %rdi
movl -0x168(%rbp), %esi
movl -0x164(%rbp), %edx
movq -0x160(%rbp), %rcx
movq %rax, %r8
callq 0x1e90
movl %eax, -0x10c(%rbp)
cmpl $-0x1, %eax
jne 0x2a21
movq -0x18(%rbp), %rdi
callq 0x1030
movq 0x25f2(%rip), %rax      # 0x4ff8
movq (%rax), %rdi
leaq 0x7be(%rip), %rsi       # 0x31ce
movb $0x0, %al
callq 0x10c0
movl $0xffffffff, %edi       # imm = 0xFFFFFFFF
callq 0x1120
movq -0x108(%rbp), %rdi
callq 0x1030
movq -0x38(%rbp), %rax
movw 0xe(%rax), %cx
addw $-0x1, %cx
movw %cx, 0xe(%rax)
movq 0x266c(%rip), %rdi      # 0x50b0
movq -0x38(%rbp), %rax
movl 0x8(%rax), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x17f0
movq %rax, %rdi
movl -0xd4(%rbp), %esi
addl $0x1, %esi
callq 0x17d0
movq %rax, -0x190(%rbp)
movl -0xd4(%rbp), %eax
movl %eax, -0x184(%rbp)
movl -0xcc(%rbp), %eax
movl %eax, -0x180(%rbp)
movl -0xd0(%rbp), %eax
movl %eax, -0x17c(%rbp)
movq 0x2618(%rip), %rdi      # 0x50b0
movq -0x38(%rbp), %rax
movl (%rax), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x17f0
movq %rax, -0x178(%rbp)
movq 0x25fa(%rip), %rdi      # 0x50b0
movq -0x38(%rbp), %rax
movl 0x4(%rax), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x17f0
movq -0x190(%rbp), %rdi
movl -0x184(%rbp), %esi
movl -0x180(%rbp), %edx
movq -0x140(%rbp), %rcx
movl -0x17c(%rbp), %r8d
movq -0x178(%rbp), %r9
subq $0x10, %rsp
movq %rax, (%rsp)
movl $0x8000, 0x8(%rsp)      # imm = 0x8000
callq 0x1c80
addq $0x10, %rsp
movl -0x10c(%rbp), %eax
movw %ax, %cx
movq -0x38(%rbp), %rax
movw %cx, 0xc(%rax)
movq -0x10(%rbp), %rax
movq 0x10(%rax), %rdi
xorl %esi, %esi
movb $0x0, %al
callq 0x1100
movl %eax, -0x118(%rbp)
cmpl $-0x1, -0x118(%rbp)
jne 0x2b45
leaq 0x6af(%rip), %rdi       # 0x31ef
callq 0x1110
movl $0x0, -0x11c(%rbp)
movl $0x0, -0xe4(%rbp)
movl -0xe4(%rbp), %eax
movl %eax, -0x194(%rbp)
cmpl $0xc, -0xd0(%rbp)
jg 0x2b7c
movl -0xd0(%rbp), %eax
movl %eax, -0x198(%rbp)
jmp 0x2b8b
movl -0xd0(%rbp), %eax
subl $0x1, %eax
movl %eax, -0x198(%rbp)
movl -0x194(%rbp), %eax
movl -0x198(%rbp), %ecx
cmpl %ecx, %eax
jge 0x2c33
movq 0x250a(%rip), %rdi      # 0x50b0
movq -0x38(%rbp), %rax
movl 0x8(%rax), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x17f0
movq %rax, %rdi
movl -0xd4(%rbp), %esi
addl $0x1, %esi
movl -0xe4(%rbp), %edx
callq 0x1890
movq %rax, -0x128(%rbp)
movl -0x118(%rbp), %edi
movq -0x128(%rbp), %rsi
movl $0x400, %edx            # imm = 0x400
callq 0x10b0
movl %eax, -0x120(%rbp)
cmpl $0x0, %eax
jg 0x2c0d
leaq 0x60b(%rip), %rdi       # 0x3209
callq 0x1110
movl $0x1, %edi
callq 0x1120
movl -0x120(%rbp), %eax
addl -0x11c(%rbp), %eax
movl %eax, -0x11c(%rbp)
movl -0xe4(%rbp), %eax
addl $0x1, %eax
movl %eax, -0xe4(%rbp)
jmp 0x2b59
movq -0x18(%rbp), %rsi
movl -0x11c(%rbp), %edx
leaq 0x5ca(%rip), %rdi       # 0x320e
movb $0x0, %al
callq 0x1080
movl -0x118(%rbp), %edi
callq 0x10a0
movq -0x18(%rbp), %rdi
callq 0x1030
movl $0x0, -0x4(%rbp)
movq -0xf0(%rbp), %rax
movq %rax, %rsp
movl -0x4(%rbp), %eax
movq %rbp, %rsp
popq %rbp
retq