// File: /workspace/github_c_makefile_star2_O1/kevinp89[P]Operating-System-EXT2-file-system-with-commands/ext2_ln.c
// Function: main
int main(int argc, char **argv){
    //arguments check
    if(argc < 4){
        fprintf(stderr, "Usage: %s ./ext2_ln [disk] [Src Path] [target "
                        "path]\n",
                argv[0]);
        exit(1);
    }

    /* get args */
    char *disk_img = NULL; //disk img path
    char *src_path = NULL; //src path in virtual disk
    char *target_path = NULL; //target path in virtual disk
    int s_link_flag=0; //symlink flag

    /* get the args and store in vars */
    int c;
    if ((c = getopt(argc, argv, "s")) != -1) {
            if (c == 's') {
                s_link_flag = 1;
            }
        int a;
        for(a=optind; a<argc; a++){

            switch (a) {
                case 2:
                    disk_img = argv[a];
                    break;
                case 3:
                    src_path = argv[a];
                    break;
                case 4:
                    target_path = argv[a];
                    break;
                default:
                    break;
            }
        }
    } else {
        disk_img = argv[1];
        src_path = argv[2];
        target_path = argv[3];
    }


    //opening the disk
    int fd = open(disk_img, O_RDWR);
    if(!fd){
        fprintf(stderr, "Disk image '%s' not found\n", argv[1]);
        exit(1);
    }

    /**check if paths are absolute */
    //check if virtual path 1 is valid
    int check = check_path(src_path);
    if(check == 1){
        fprintf(stderr, "Please provide absolute path for virtual path 1");
        exit(1);
    }

    //check virtual path 2 is absolute or not.
    int check2 = check_path(target_path);
    if(check2 == 1){
        fprintf(stderr, "Please provide absolute path for virtual path");
        exit(1);
    }

    // mmap the disk
    disk = mmap(NULL, 128*1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (disk == MAP_FAILED) {
        perror("mmap");
        exit(1);
    }

    // Grabbing super block and block descriptor
    sb = (struct ext2_super_block *)(disk + 1024);
    desc = (struct ext2_group_desc *)(disk + 2*1024);

    unsigned char *bmap = (unsigned char *)(disk + 1024 * desc->bg_block_bitmap);

    //check if the file paths exists, if so get the parent
    int parent_index_1 = check_parent(disk, src_path);
    if(parent_index_1 == -1){
        fprintf(stderr, "'%s' No such file or directory\n", src_path);
        exit(ENOENT);
    }
    //get the parent inode
    struct ext2_inode *parent_node1 = (struct ext2_inode *) (
            (disk+EXT2_BLOCK_SIZE * desc->bg_inode_table) +
            (sb->s_inode_size * parent_index_1));


    int parent_index_2 = check_parent(disk, target_path);
    if(parent_index_2 == -1){
        fprintf(stderr, "No such file or directory");
        exit(ENOENT);
    }

    /** get the last names from both files */

    char *file_name1 = get_last_file_name(src_path);
    char *file_name2 = get_last_file_name(target_path);

    //check if first file is a directory or not
    int check_file = search_directories(disk, parent_node1, file_name1, 1);
    if(check_file != -1){
        fprintf(stderr, "Give file: '%s' is a directory", file_name1);
        exit(EISDIR);
    }

    //for second one, get parent to check if file exists
    //get the parent node
    struct ext2_inode *parent_node2 = (struct ext2_inode *) (
            (disk+EXT2_BLOCK_SIZE * desc->bg_inode_table) +
            (sb->s_inode_size * parent_index_2));

    int check_dir_exists = search_directories(disk,parent_node2, file_name2, 1);
    if(check_dir_exists != -1){
        fprintf(stderr, "File: '%s' already exists", file_name2);
        exit(EEXIST);
    }

    int check_file_exists = search_directories(disk,parent_node2, file_name2, 0);
    if(check_file_exists != -1){
        fprintf(stderr, "File: '%s' already exists", file_name2);
        exit(EEXIST);
    }

    //get the inode for first file name
    unsigned int inode_indx1 = search_directories(disk, parent_node1,
                                                  file_name1, 0);
    struct ext2_inode *inode1 = (struct ext2_inode *) ((disk +
            EXT2_BLOCK_SIZE * desc->bg_inode_bitmap) +
            (sb->s_inode_size * inode_indx1));

    /* if -s is provided, create new inode */
    int inode;
    struct ext2_inode *new_inode;
    if(s_link_flag){
        // Finding a free inode and allocating (set to 1)
        unsigned char *imap = (unsigned char *)(disk + 1024 *
                                                       desc->bg_inode_bitmap);

        for (inode = 11; inode < sb->s_inodes_count; inode++) {
            if (check_node(inode, imap) == 0) {
                break;
            }
        }
        if (inode == sb->s_inodes_count) { // Maximum reached, no more inodes
            fprintf(stderr, "No more inodes available.");
            exit(1);
        }
        // Finding a free block and allocating (set to 1)
        int bnode;
        for (bnode = 22; bnode < sb->s_blocks_count; bnode++) {
            if (check_node(bnode, bmap) == 0) {
                break;
            }
        }
        if (bnode == sb->s_blocks_count) {
            fprintf(stderr, "No more blocks available.");
            exit(1);
        }
        char *data_block;
        // Actual allocation.
        set_node(inode, imap);
        set_node(bnode, bmap);

        adjust_free_blocks(-1, sb, desc);
        adjust_free_inodes(-1, sb, desc);
        // Writing data to the ext2_inode struct in the inode index.
        new_inode = (struct ext2_inode *)((disk + 1024 * desc->bg_inode_table)
                                                             + (sb->s_inode_size * (inode)));
        new_inode->i_mode = EXT2_S_IFLNK;
        new_inode->i_uid = 0;
        new_inode->i_size = strlen(src_path); //size is the same as src node
        new_inode->i_ctime = (unsigned int) time(0);
        new_inode->i_dtime = 0;
        new_inode->i_gid = 0;
        new_inode->i_blocks = 2;
        new_inode->osd1 = 0;
        new_inode->i_block[0] = bnode; //change??
        new_inode->i_generation = 0;
        new_inode->i_file_acl = 0;
        new_inode->i_dir_acl = 0;
        new_inode->i_faddr = 0;
        new_inode->i_links_count = 2; // Itself and from parent.
        parent_node2->i_links_count++; // Parent gets one more link from parent dir in new directory. ???

//        read in src path into data block
        data_block = (char*)(disk + EXT2_BLOCK_SIZE *
                                          new_inode->i_block[0]);
        strcpy(data_block, src_path);

    }

    // Adding new inode/directory entry into the parent block.
    int i;
    int par_block_index;
    for (i = 0; parent_node2->i_block[i] != 0; i++) {
        par_block_index = parent_node2->i_block[i];
    }
    int total_rec_len;
    struct ext2_dir_entry *curdir = (struct ext2_dir_entry *) (disk + (1024 * par_block_index));
    // When this for loop exits, curdir should be the last directory
    for (total_rec_len = 0; total_rec_len + curdir->rec_len < 1024;) {
        total_rec_len += curdir->rec_len;
        curdir = (struct ext2_dir_entry *) (disk + (1024 * par_block_index) + total_rec_len);
    }
    // Check there's enough space to place the new dir entry in the current block
    // Reuse total_rec_len to find remaining bytes if curdir was reduced to proper size
    struct ext2_dir_entry *newdir;
    int remaining_rec_len = sizeof(curdir) + curdir->name_len; // Actual size of curdir
    remaining_rec_len += 4 - (remaining_rec_len % 4); // Add padding
    remaining_rec_len = 1024 - remaining_rec_len - total_rec_len; // Remaining space
    if (remaining_rec_len < (8 + strlen(file_name2))) { // Not enough space
        // Allocate new block
        int newblock;
        for (newblock = 22; newblock < sb->s_blocks_count; newblock++) {
            if (check_node(newblock, bmap) == 0) {
                break;
            }
        }
        if (newblock == sb->s_blocks_count) {
            fprintf(stderr, "No more blocks available.");
            exit(1);
        }
        set_node(newblock, bmap);
        sb->s_free_blocks_count--;
        parent_node2->i_block[i] = newblock; // i should still hold the first block space unused by parent
        parent_node2->i_blocks += 2; // One more block being added
        newdir = (struct ext2_dir_entry *) (disk + (1024 * newblock));
        newdir->rec_len = 1024; // Takes up the whole of the new block.
    } else { // Enough space
        // Change curdir, add new directory
        curdir->rec_len = sizeof(curdir) + curdir->name_len;
        curdir->rec_len += 4 - (curdir->rec_len % 4);
        total_rec_len += curdir->rec_len;
        newdir = (struct ext2_dir_entry *) (disk + (1024 *
                par_block_index) + total_rec_len);
        newdir->rec_len = 1024 - total_rec_len; // Takes up the rest of the block.
    }
    if(s_link_flag){
        newdir->inode = inode + 1;
        newdir->name_len = strlen(file_name2);
        newdir->file_type = EXT2_FT_SYMLINK;
        strncpy(newdir->name, file_name2, newdir->name_len);
        //no increase in the link count
    } else {
        //for the hard link
        newdir->inode = inode_indx1 + 1; // set the index to the linking index
        newdir->name_len = strlen(file_name2); //set the new name length
        newdir->file_type = EXT2_FT_REG_FILE; //set file type
        strncpy(newdir->name, file_name2,
                newdir->name_len); //set the new name
        inode1->i_links_count++; //increment the link count
    }
    return 0;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x48, %rsp
movq %rsi, %r14
cmpl $0x3, %edi
jle 0x1d0b
movl %edi, %ebx
leaq 0x992(%rip), %rdx       # 0x2154
movq %r14, %rsi
callq 0x1100
cmpl $-0x1, %eax
je 0x1829
cmpl $0x73, %eax
setne 0x7(%rsp)
movq 0x27fa(%rip), %rax      # 0x3fd8
movslq (%rax), %rsi
cmpl %ebx, %esi
jge 0x183c
movl %ebx, %eax
movl %esi, %ecx
leaq (%r14,%rsi,8), %rdx
subq %rsi, %rax
xorl %esi, %esi
xorl %edi, %edi
xorl %r15d, %r15d
xorl %ebx, %ebx
leal (%rcx,%rsi), %r8d
cmpl $0x4, %r8d
je 0x181b
cmpl $0x3, %r8d
je 0x1815
cmpl $0x2, %r8d
jne 0x181f
movq (%rdx,%rsi,8), %rdi
jmp 0x181f
movq (%rdx,%rsi,8), %r15
jmp 0x181f
movq (%rdx,%rsi,8), %rbx
incq %rsi
cmpq %rsi, %rax
jne 0x17f9
jmp 0x1843
movq 0x8(%r14), %rdi
movq 0x10(%r14), %r15
movq 0x18(%r14), %rbx
movb $0x1, 0x7(%rsp)
jmp 0x1843
xorl %ebx, %ebx
xorl %r15d, %r15d
xorl %edi, %edi
movl $0x2, %esi
xorl %eax, %eax
callq 0x10e0
testl %eax, %eax
je 0x1d13
cmpb $0x2f, (%r15)
jne 0x1d1b
cmpb $0x2f, (%rbx)
jne 0x1d20
movl $0x20000, %esi          # imm = 0x20000
xorl %edi, %edi
movl $0x3, %edx
movl $0x1, %ecx
movl %eax, %r8d
xorl %r9d, %r9d
callq 0x1070
movq %rax, 0x282b(%rip)      # 0x40b8
cmpq $-0x1, %rax
je 0x1d25
movq %rax, %rdi
addq $0x400, %rax            # imm = 0x400
movq %rax, 0x2819(%rip)      # 0x40c0
leaq 0x800(%rdi), %rax
movq %rax, 0x2813(%rip)      # 0x40c8
movl 0x800(%rdi), %eax
movq %rax, 0x10(%rsp)
movq %rdi, 0x30(%rsp)
movq %r15, 0x8(%rsp)
movq %r15, %rsi
callq 0x15ea
cmpl $-0x1, %eax
je 0x1d2a
movl %eax, %ebp
movq 0x27d4(%rip), %r15      # 0x40b8
movq 0x27dd(%rip), %rax      # 0x40c8
movl 0x8(%rax), %r13d
movq 0x27ca(%rip), %rax      # 0x40c0
movzwl 0x58(%rax), %r14d
movq %r15, %rdi
movq %rbx, %rsi
callq 0x15ea
cmpl $-0x1, %eax
je 0x1d34
movl %eax, %r12d
shll $0xa, %r13d
addq %r13, %r15
movslq %ebp, %r13
imulq %r14, %r13
addq %r15, %r13
movq 0x8(%rsp), %rdi
callq 0x172b
movq %rax, %r15
movq %rbx, %rdi
callq 0x172b
movq %rax, %r14
movq 0x2776(%rip), %rbp      # 0x40b8
movq %rbp, %rdi
movq %r13, %rsi
movq %r15, %rdx
movl $0x1, %ecx
callq 0x1502
cmpl $-0x1, %eax
jne 0x1d39
movq 0x2763(%rip), %rbx      # 0x40c8
movl 0x8(%rbx), %eax
shll $0xa, %eax
addq %rbp, %rax
movq 0x274b(%rip), %rcx      # 0x40c0
movq %rcx, 0x40(%rsp)
movzwl 0x58(%rcx), %edx
movslq %r12d, %r12
movq %rdx, 0x28(%rsp)
imulq %rdx, %r12
addq %rax, %r12
movq %rbp, %rdi
movq %r12, %rsi
movq %r14, %rdx
movl $0x1, %ecx
callq 0x1502
cmpl $-0x1, %eax
jne 0x1d41
movq %rbp, %rdi
movq %r12, 0x20(%rsp)
movq %r12, %rsi
movq %r14, %rdx
xorl %ecx, %ecx
callq 0x1502
cmpl $-0x1, %eax
movq 0x30(%rsp), %r12
jne 0x1d49
movq 0x10(%rsp), %rax
shll $0xa, %eax
addq %rax, %r12
movq %rbp, %rdi
movq %r13, %rsi
movq %r15, %rdx
xorl %ecx, %ecx
callq 0x1502
movl %eax, 0x10(%rsp)
movl 0x4(%rbx), %esi
shll $0xa, %esi
addq %rbp, %rsi
cmpb $0x0, 0x7(%rsp)
movq %r14, 0x18(%rsp)
movq %rsi, 0x38(%rsp)
jne 0x1b50
movq 0x40(%rsp), %rdi
movl (%rdi), %eax
movl $0xb, %ebp
cmpl $0xc, %eax
jb 0x1a35
movl %ebp, %ecx
shrl $0x3, %ecx
movzbl (%rsi,%rcx), %ecx
movl %ebp, %edx
andl $0x7, %edx
btl %edx, %ecx
jae 0x1a35
incl %ebp
cmpl %ebp, %eax
jne 0x1a1a
movl %eax, %ebp
cmpl %eax, %ebp
je 0x1d51
movl 0x4(%rdi), %eax
movl $0x16, %r13d
cmpl $0x17, %eax
jb 0x1a72
movl $0x16, %r13d
movl %r13d, %ecx
shrl $0x3, %ecx
movzbl (%r12,%rcx), %ecx
movl %r13d, %edx
andl $0x7, %edx
btl %edx, %ecx
jae 0x1a72
incl %r13d
cmpl %r13d, %eax
jne 0x1a51
movl %eax, %r13d
cmpl %eax, %r13d
je 0x1d5b
movl %ebp, %ecx
andb $0x7, %cl
movl $0x1, %eax
shll %cl, %eax
movl %ebp, %ecx
shrl $0x3, %ecx
xorb %al, (%rsi,%rcx)
movl $0x1, %eax
movl %r13d, %ecx
andb $0x7, %cl
shll %cl, %eax
movl %r13d, %ecx
shrl $0x3, %ecx
xorb %al, (%r12,%rcx)
movq 0x2613(%rip), %rax      # 0x40c0
movq 0x2614(%rip), %rcx      # 0x40c8
decl 0xc(%rax)
decw 0xc(%rcx)
decl 0x10(%rax)
decw 0xe(%rcx)
movl 0x8(%rcx), %ebx
shll $0xa, %ebx
addq 0x25e9(%rip), %rbx      # 0x40b8
movl %ebp, %ecx
movzwl 0x58(%rax), %r14d
imulq %rcx, %r14
movl $0xa000, (%r14,%rbx)    # imm = 0xA000
movq 0x8(%rsp), %r15
movq %r15, %rdi
callq 0x1060
movl %eax, 0x4(%r14,%rbx)
xorl %edi, %edi
callq 0x10b0
movl %eax, 0xc(%r14,%rbx)
movabsq $0x2000000000000, %rax  # imm = 0x2000000000000
movq %rax, 0x14(%r14,%rbx)
movl $0x2, 0x1c(%r14,%rbx)
movl $0x0, 0x24(%r14,%rbx)
movl %r13d, 0x28(%r14,%rbx)
xorps %xmm0, %xmm0
movups %xmm0, 0x64(%r14,%rbx)
movq 0x20(%rsp), %rax
incw 0x1a(%rax)
shll $0xa, %r13d
addq 0x2575(%rip), %r13      # 0x40b8
movq %r13, %rdi
movq %r15, %rsi
callq 0x1050
incl %ebp
movq %r12, 0x30(%rsp)
movq 0x28(%rsp), %rax
imull 0x10(%rsp), %eax
movq %rax, 0x28(%rsp)
movq 0x20(%rsp), %rax
leaq 0x28(%rax), %r13
movl %ecx, %eax
movl (%r13), %ecx
addq $0x4, %r13
testl %ecx, %ecx
jne 0x1b6d
movq 0x2536(%rip), %rcx      # 0x40b8
cltq
shlq $0xa, %rax
leaq (%rcx,%rax), %rbx
movzwl 0x4(%rcx,%rax), %eax
xorl %r12d, %r12d
movq %rbx, %r15
cmpl $0x3ff, %eax            # imm = 0x3FF
ja 0x1bb7
movl %eax, %r12d
movl %eax, %r15d
movzwl 0x4(%rbx,%r15), %eax
addl %r12d, %eax
cmpl $0x400, %eax            # imm = 0x400
jb 0x1b9e
addq %rbx, %r15
movzbl 0x6(%r15), %r14d
addq $0x8, %r14
movl %r14d, %eax
andl $-0x4, %eax
addl %r12d, %eax
movl $0x3fc, %ecx            # imm = 0x3FC
subl %eax, %ecx
movslq %ecx, %rax
movq %rax, 0x8(%rsp)
movq 0x18(%rsp), %rdi
callq 0x1060
addq $0x8, %rax
cmpq 0x8(%rsp), %rax
jbe 0x1c72
movq 0x24c8(%rip), %rax      # 0x40c0
movl 0x4(%rax), %eax
movl $0x16, %ebx
cmpl $0x17, %eax
movq 0x18(%rsp), %r14
movq 0x30(%rsp), %rsi
movq 0x20(%rsp), %rdi
jb 0x1c34
movl $0x16, %ebx
movl %ebx, %ecx
shrl $0x3, %ecx
movzbl (%rsi,%rcx), %ecx
movl %ebx, %edx
andl $0x7, %edx
btl %edx, %ecx
jae 0x1c34
incl %ebx
cmpl %ebx, %eax
jne 0x1c19
movl %eax, %ebx
cmpl %eax, %ebx
je 0x1d56
movl %ebx, %ecx
andb $0x7, %cl
movl $0x1, %eax
shll %cl, %eax
movl %ebx, %ecx
shrl $0x3, %ecx
xorb %al, (%rsi,%rcx)
movq 0x2469(%rip), %rax      # 0x40c0
decl 0xc(%rax)
movl %ebx, -0x4(%r13)
addl $0x2, 0x1c(%rdi)
shll $0xa, %ebx
addq 0x244c(%rip), %rbx      # 0x40b8
movw $0x400, %ax             # imm = 0x400
jmp 0x1c92
andl $-0x4, %r14d
addl $0x4, %r14d
movw %r14w, 0x4(%r15)
addl %r14d, %r12d
addq %r12, %rbx
movl $0x400, %eax            # imm = 0x400
subl %r12d, %eax
movq 0x18(%rsp), %r14
movw %ax, 0x4(%rbx)
cmpb $0x0, 0x7(%rsp)
je 0x1cd7
movl 0x10(%rsp), %eax
incl %eax
movl %eax, (%rbx)
movq %r14, %rdi
callq 0x1060
movb %al, 0x6(%rbx)
movb $0x1, 0x7(%rbx)
addq $0x8, %rbx
movzbl %al, %edx
movq %rbx, %rdi
movq %r14, %rsi
callq 0x1040
movq 0x28(%rsp), %rax
movq 0x38(%rsp), %rcx
incw 0x1a(%rcx,%rax)
jmp 0x1cfa
movl %ebp, (%rbx)
movq %r14, %rdi
callq 0x1060
movb %al, 0x6(%rbx)
movb $0x7, 0x7(%rbx)
addq $0x8, %rbx
movzbl %al, %edx
movq %rbx, %rdi
movq %r14, %rsi
callq 0x1040
xorl %eax, %eax
addq $0x48, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq %r14, %rdi
callq 0x1345
movq %r14, %rdi
callq 0x131d
callq 0x119a
callq 0x11bf
callq 0x1308
movq 0x8(%rsp), %rdi
callq 0x12e4
callq 0x12bf
movq %r15, %rdi
callq 0x11e4
movq %r14, %rdi
callq 0x1208
movq %r14, %rdi
callq 0x122c
callq 0x1275
callq 0x129a
callq 0x1250