// File: /workspace/github_c_makefile_star2_O2/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 0x1afe
movl %edi, %ebp
leaq 0xbb6(%rip), %rdx       # 0x2154
movq %r14, %rsi
callq 0x1100
cmpl $-0x1, %eax
je 0x15ec
movq 0x2a26(%rip), %rcx      # 0x3fd8
movl (%rcx), %ecx
xorl %ebx, %ebx
xorl %r15d, %r15d
xorl %edi, %edi
cmpl %ebp, %ecx
jge 0x15e2
cmpl $0x4, %ecx
je 0x15da
cmpl $0x3, %ecx
je 0x15d4
cmpl $0x2, %ecx
jne 0x15de
movq 0x10(%r14), %rdi
jmp 0x15de
movq 0x18(%r14), %r15
jmp 0x15de
movq 0x20(%r14), %rbx
incl %ecx
jmp 0x15bb
cmpl $0x73, %eax
setne 0x7(%rsp)
jmp 0x15fd
movq 0x8(%r14), %rdi
movq 0x10(%r14), %r15
movq 0x18(%r14), %rbx
movb $0x1, 0x7(%rsp)
pushq $0x2
popq %rsi
xorl %eax, %eax
callq 0x10e0
testl %eax, %eax
je 0x1b14
cmpb $0x2f, (%r15)
jne 0x1b35
cmpb $0x2f, (%rbx)
jne 0x1b4d
pushq $0x3
popq %rdx
pushq $0x1
popq %rcx
movl $0x20000, %esi          # imm = 0x20000
xorl %edi, %edi
movl %eax, %r8d
xorl %r9d, %r9d
callq 0x1070
movq %rax, 0x2a77(%rip)      # 0x40b8
cmpq $-0x1, %rax
je 0x1b65
movq %rax, %rdi
addq $0x400, %rax            # imm = 0x400
movq %rax, 0x2a65(%rip)      # 0x40c0
leaq 0x800(%rdi), %rax
movq %rax, 0x2a5f(%rip)      # 0x40c8
movl 0x800(%rdi), %eax
movq %rax, 0x18(%rsp)
movq %rdi, 0x30(%rsp)
movq %r15, %r14
movq %r15, %rsi
callq 0x13a9
cmpl $-0x1, %eax
je 0x1b76
movl %eax, %ebp
movq 0x2a22(%rip), %r15      # 0x40b8
movq 0x2a2b(%rip), %rax      # 0x40c8
movl 0x8(%rax), %r13d
movq 0x2a18(%rip), %rax      # 0x40c0
movzwl 0x58(%rax), %r12d
movq %r15, %rdi
movq %rbx, %rsi
callq 0x13a9
movl %eax, 0x8(%rsp)
cmpl $-0x1, %eax
je 0x1b93
shll $0xa, %r13d
addq %r13, %r15
movslq %ebp, %r13
imulq %r12, %r13
addq %r15, %r13
movq %r14, 0x10(%rsp)
movq %r14, %rdi
callq 0x1500
movq %rax, %rbp
movq %rbx, %rdi
callq 0x1500
movq %rax, %r14
movq 0x29c0(%rip), %rbx      # 0x40b8
pushq $0x1
popq %rcx
movq %rbx, %rdi
movq %r13, %rsi
movq %rbp, %rdx
callq 0x12d8
cmpl $-0x1, %eax
jne 0x1bb3
movq 0x29af(%rip), %r15      # 0x40c8
movl 0x8(%r15), %eax
shll $0xa, %eax
addq %rbx, %rax
movq 0x2996(%rip), %rcx      # 0x40c0
movq %rcx, 0x40(%rsp)
movzwl 0x58(%rcx), %edx
movslq 0x8(%rsp), %r12
movq %rdx, 0x8(%rsp)
imulq %rdx, %r12
addq %rax, %r12
pushq $0x1
popq %rcx
movq %rbx, %rdi
movq %r12, %rsi
movq %r14, %rdx
callq 0x12d8
cmpl $-0x1, %eax
jne 0x1adc
movq %rbx, %rdi
movq %r12, 0x28(%rsp)
movq %r12, %rsi
movq %r14, %rdx
xorl %ecx, %ecx
callq 0x12d8
cmpl $-0x1, %eax
jne 0x1adc
movq 0x18(%rsp), %rax
shll $0xa, %eax
addq %rax, 0x30(%rsp)
movq %rbx, %rdi
movq %r13, %rsi
movq %rbp, %rdx
xorl %ecx, %ecx
callq 0x12d8
movl %eax, %r10d
movl 0x4(%r15), %r13d
shll $0xa, %r13d
addq %rbx, %r13
cmpb $0x0, 0x7(%rsp)
movq %r14, 0x20(%rsp)
movl %r10d, 0x18(%rsp)
jne 0x1927
pushq $0x16
popq %rax
movq 0x40(%rsp), %r11
movl (%r11), %ecx
cmpl $0xc, %ecx
pushq $0xb
popq %rdx
movl %edx, %r9d
cmovael %ecx, %r9d
movq 0x30(%rsp), %r8
cmpl %ecx, %edx
jae 0x17f9
movl %edx, %esi
shrl $0x3, %esi
movzbl (%r13,%rsi), %esi
movl %edx, %edi
andl $0x7, %edi
btl %edi, %esi
jae 0x17f6
incl %edx
jmp 0x17d9
movl %edx, %r9d
cmpl %ecx, %r9d
je 0x1be5
movl %r9d, %r14d
movl 0x4(%r11), %ecx
cmpl $0x17, %ecx
movl %eax, %ebp
cmovael %ecx, %ebp
cmpl %ecx, %eax
jae 0x182f
movl %eax, %edx
shrl $0x3, %edx
movzbl (%r8,%rdx), %edx
movl %eax, %esi
andl $0x7, %esi
btl %esi, %edx
jae 0x182d
incl %eax
jmp 0x1811
movl %eax, %ebp
cmpl %ecx, %ebp
je 0x1bd2
movl %r14d, %ecx
andb $0x7, %cl
pushq $0x1
popq %rdx
movl %edx, %eax
shll %cl, %eax
movl %r14d, %ecx
shrl $0x3, %ecx
xorb %al, (%r13,%rcx)
movl %ebp, %ecx
andb $0x7, %cl
movl %edx, %eax
shll %cl, %eax
movl %ebp, %ecx
shrl $0x3, %ecx
xorb %al, (%r8,%rcx)
movq 0x2858(%rip), %rax      # 0x40c0
movq 0x2859(%rip), %rcx      # 0x40c8
movq 0xc(%rax), %xmm0
pcmpeqd %xmm1, %xmm1
paddd %xmm1, %xmm0
movq %xmm0, 0xc(%rax)
movd 0xc(%rcx), %xmm0
paddw %xmm1, %xmm0
movd %xmm0, 0xc(%rcx)
movl 0x8(%rcx), %ebx
shll $0xa, %ebx
addq 0x281c(%rip), %rbx      # 0x40b8
movl %r14d, %ecx
movzwl 0x58(%rax), %r15d
imulq %rcx, %r15
movl $0xa000, (%r15,%rbx)    # imm = 0xA000
movq 0x10(%rsp), %r12
movq %r12, %rdi
callq 0x1060
movl %eax, 0x4(%r15,%rbx)
xorl %edi, %edi
callq 0x10b0
movl %eax, 0xc(%r15,%rbx)
movabsq $0x2000000000000, %rax  # imm = 0x2000000000000
movq %rax, 0x14(%r15,%rbx)
movl $0x2, 0x1c(%r15,%rbx)
andl $0x0, 0x24(%r15,%rbx)
movl %ebp, 0x28(%r15,%rbx)
pxor %xmm0, %xmm0
movdqu %xmm0, 0x64(%r15,%rbx)
movq 0x28(%rsp), %rax
incw 0x1a(%rax)
shll $0xa, %ebp
addq 0x27a9(%rip), %rbp      # 0x40b8
movq %rbp, %rdi
movq %r12, %rsi
callq 0x1050
incl %r14d
movl %r14d, 0x3c(%rsp)
movl 0x18(%rsp), %r10d
movq 0x8(%rsp), %rax
imull %r10d, %eax
movq %rax, 0x8(%rsp)
movq 0x28(%rsp), %rax
leaq 0x28(%rax), %rbp
movl %ecx, %eax
movl (%rbp), %ecx
addq $0x4, %rbp
testl %ecx, %ecx
jne 0x193e
shll $0xa, %eax
movslq %eax, %r12
addq 0x2760(%rip), %r12      # 0x40b8
xorl %ebx, %ebx
movq %r12, %r15
movzwl 0x4(%r15), %eax
addl %ebx, %eax
cmpl $0x3ff, %eax            # imm = 0x3FF
ja 0x1975
movl %eax, %r15d
addq %r12, %r15
movl %eax, %ebx
jmp 0x195d
movq %r13, 0x10(%rsp)
movzbl 0x6(%r15), %r13d
addq $0x8, %r13
movl %r13d, %eax
andl $-0x4, %eax
addl %ebx, %eax
movl $0x3fc, %ecx            # imm = 0x3FC
subl %eax, %ecx
movslq %ecx, %r14
movq 0x20(%rsp), %rdi
callq 0x1060
addq $0x8, %rax
cmpq %r14, %rax
jbe 0x19e4
movq 0x2711(%rip), %rax      # 0x40c0
movl 0x4(%rax), %eax
cmpl $0x17, %eax
pushq $0x16
popq %rcx
movl %ecx, %r12d
cmovael %eax, %r12d
movq 0x30(%rsp), %rdi
movq 0x10(%rsp), %r14
cmpl %eax, %ecx
jae 0x1a08
movl %ecx, %edx
shrl $0x3, %edx
movzbl (%rdi,%rdx), %edx
movl %ecx, %esi
andl $0x7, %esi
btl %esi, %edx
jae 0x1a05
incl %ecx
jmp 0x19c9
andl $-0x4, %r13d
addl $0x4, %r13d
movw %r13w, 0x4(%r15)
addl %r13d, %ebx
addq %rbx, %r12
movl $0x400, %eax            # imm = 0x400
subl %ebx, %eax
movq 0x10(%rsp), %r14
jmp 0x1a4b
movl %ecx, %r12d
cmpl %eax, %r12d
je 0x1bd2
movl %r12d, %ecx
andb $0x7, %cl
pushq $0x1
popq %rdx
shll %cl, %edx
movl %r12d, %eax
shrl $0x3, %eax
xorb %dl, (%rdi,%rax)
movq 0x2694(%rip), %rax      # 0x40c0
decl 0xc(%rax)
movl %r12d, -0x4(%rbp)
movq 0x28(%rsp), %rax
addl $0x2, 0x1c(%rax)
shll $0xa, %r12d
addq 0x2671(%rip), %r12      # 0x40b8
movw $0x400, %ax             # imm = 0x400
movw %ax, 0x4(%r12)
cmpb $0x0, 0x7(%rsp)
movl 0x3c(%rsp), %eax
je 0x1a9d
movl 0x18(%rsp), %eax
incl %eax
movl %eax, (%r12)
movq 0x20(%rsp), %rbx
movq %rbx, %rdi
callq 0x1060
movb %al, 0x6(%r12)
movb $0x1, 0x7(%r12)
addq $0x8, %r12
movzbl %al, %edx
movq %r12, %rdi
movq %rbx, %rsi
callq 0x1040
movq 0x8(%rsp), %rax
incw 0x1a(%r14,%rax)
jmp 0x1acb
movl %eax, (%r12)
movq 0x20(%rsp), %rbx
movq %rbx, %rdi
callq 0x1060
movb %al, 0x6(%r12)
movb $0x7, 0x7(%r12)
addq $0x8, %r12
movzbl %al, %edx
movq %r12, %rdi
movq %rbx, %rsi
callq 0x1040
xorl %eax, %eax
addq $0x48, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq 0x2515(%rip), %rax      # 0x3ff8
movq (%rax), %rdi
leaq 0x64f(%rip), %rsi       # 0x213c
movq %r14, %rdx
xorl %eax, %eax
callq 0x10a0
pushq $0x11
jmp 0x1c03
movq 0x24f3(%rip), %rax      # 0x3ff8
movq (%rax), %rdi
movq (%r14), %rdx
leaq 0x51e(%rip), %rsi       # 0x2030
jmp 0x1b29
movq 0x24dd(%rip), %rax      # 0x3ff8
movq (%rax), %rdi
movq 0x8(%r14), %rdx
leaq 0x53c(%rip), %rsi       # 0x2065
xorl %eax, %eax
callq 0x10a0
jmp 0x1c01
movq 0x24bc(%rip), %rax      # 0x3ff8
movq (%rax), %rcx
leaq 0x53a(%rip), %rdi       # 0x2080
pushq $0x2f
jmp 0x1bf8
movq 0x24a4(%rip), %rax      # 0x3ff8
movq (%rax), %rcx
leaq 0x552(%rip), %rdi       # 0x20b0
pushq $0x2d
jmp 0x1bf8
leaq 0x572(%rip), %rdi       # 0x20de
callq 0x10f0
jmp 0x1c01
movq 0x247b(%rip), %rax      # 0x3ff8
movq (%rax), %rdi
leaq 0x55c(%rip), %rsi       # 0x20e3
movq %r14, %rdx
xorl %eax, %eax
callq 0x10a0
jmp 0x1baf
movq 0x245e(%rip), %rax      # 0x3ff8
movq (%rax), %rcx
leaq 0x55f(%rip), %rdi       # 0x2103
pushq $0x19
popq %rsi
pushq $0x1
popq %rdx
callq 0x1120
pushq $0x2
jmp 0x1c03
movq 0x243e(%rip), %rax      # 0x3ff8
movq (%rax), %rdi
leaq 0x559(%rip), %rsi       # 0x211d
movq %rbp, %rdx
xorl %eax, %eax
callq 0x10a0
pushq $0x15
jmp 0x1c03
movq 0x241f(%rip), %rax      # 0x3ff8
movq (%rax), %rcx
leaq 0x58d(%rip), %rdi       # 0x2170
jmp 0x1bf6
movq 0x240c(%rip), %rax      # 0x3ff8
movq (%rax), %rcx
leaq 0x560(%rip), %rdi       # 0x2156
pushq $0x19
popq %rsi
pushq $0x1
popq %rdx
callq 0x1120
pushq $0x1
popq %rdi
callq 0x1110