<repo_name>baicaiking0422/369A3<file_sep>/ext2_mkdir.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include "ext2.h"
#include "ext2_helper.h"

unsigned char *disk;

int main(int argc, const char * argv[]) {

    /* Check arg */
    if (argc != 3) {
        fprintf(stderr, "Usage: ext2_mkdir <image file name> <abs path>\n");
        exit(1);
    }

    int fd = open(argv[1], O_RDWR);
    
    //record target path
    char *path;
    char *r_path;
    int r_len;
    int path_len;
    path_len = strlen(argv[2]);
    path = malloc(path_len + 1);
    strncpy(path, argv[2], path_len);
    path[path_len] = '\0';
    r_len = strlen(argv[2]);
    r_path = malloc(r_len + 1);
    strncpy(r_path, argv[2], r_len);
    r_path[r_len] = '\0';
    
    
    if (path[0] != '/') {
        fprintf(stderr, "This is not an absolute path!\n");
        exit(1);
    }

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

    struct ext2_group_desc * gd = (struct ext2_group_desc *)(disk + 2048);
    void *inodes = disk + 1024 * gd->bg_inode_table;
    int inode_num = get_inode_num(path, inodes, disk);

    if (inode_num != -1) {
        perror("The directory already exists.\n");
        exit(EEXIST);
    }

    
    char *parent_path;
    char *file_name;
    struct ext2_inode *inode;
    get_file_parent_path(r_path, &parent_path);
    get_file_name(r_path, &file_name);
    // parent dir inode num
    inode_num = get_inode_num(parent_path, inodes, disk);

    if (inode_num == -1) {
        perror("The directory does not exist.\n");
        exit(ENOENT);
    }
    
    // inode for parent path
    inode = (struct ext2_inode *)(disk + 1024 * gd->bg_inode_table + sizeof(struct ext2_inode) * (inode_num - 1));
    
    
    //get inode and block bitmap
    int *inode_bitmap = get_inode_bitmap(disk + 1024 * gd->bg_inode_bitmap);
    int *block_bitmap = get_block_bitmap(disk + 1024 * gd->bg_block_bitmap);
    
    //get a free new inode from inode bitmap
    int new_inode_idx = -1;
    int i;
    for (i = 0; i < 32; i++) {
        if (inode_bitmap[i] == 0) {
            new_inode_idx = i;
            break;
        }
    }
    
    //check new node for copy file
    if  (new_inode_idx == -1){ // no free inode to assign
        fprintf(stderr, "No free inode\n");
        exit(1);
    }
    
    //get one free blocks from block bitmap for new dir
    int *free_blocks = get_free_block(block_bitmap, 1);
    
    //check free_blocks enough
    if (free_blocks == NULL){
        fprintf(stderr, "Not enough blocks in the disk\n");
        exit(1);
    }
    
    set_block_bitmap(disk + 1024 * gd->bg_block_bitmap, *free_blocks, 1);
    gd->bg_free_blocks_count --;
    
    
    
    //assign new node
    struct ext2_inode *n_inode = inodes + sizeof(struct ext2_inode) * new_inode_idx;
    n_inode->i_mode = EXT2_S_IFDIR;
    n_inode->i_size = EXT2_BLOCK_SIZE;
    n_inode->i_links_count = 1;//change lnk?
    n_inode->i_blocks = 2;
    n_inode->i_block[0] = *free_blocks + 1;
    
    set_inode_bitmap(disk + 1024 * gd->bg_inode_bitmap, new_inode_idx, 1);
    gd->bg_free_inodes_count --;

    
    //set to parent entry
    int count;
    struct ext2_dir_entry_2 *n_entry;
    struct ext2_dir_entry_2 *past_entry;
    struct ext2_dir_entry_2 *chk_entry;
    
    int required_rec_len = ((7 + strlen(file_name)) / 4 + 1) * 4;
    int dir_num_blocks = (inode->i_size - 1) / 1024 + 1;
    int empty_rec_len = 0;
    //not indirected
    count = 0;
    if (inode->i_block[dir_num_blocks - 1] != 0) {
        while (count < 1024) {
            chk_entry = (struct ext2_dir_entry_2*)(disk + 1024 * inode->i_block[dir_num_blocks-1] + count);
            count += chk_entry->rec_len;
            if (count == 1024) {
                past_entry = chk_entry;
                int final_entry_rec_len = ((7 + chk_entry->name_len) / 4 + 1) * 4;
                empty_rec_len = chk_entry->rec_len - final_entry_rec_len;
                break;
            }
        }
    }
    
    if ((empty_rec_len > 0) && (empty_rec_len >= required_rec_len)) {
        past_entry->rec_len = ((7 + past_entry->name_len) / 4 + 1) * 4;
        n_entry = (struct ext2_dir_entry_2*)(disk + 1024 * inode->i_block[dir_num_blocks-1] + (1024 - empty_rec_len));
        n_entry->inode = new_inode_idx + 1;
        n_entry->rec_len = empty_rec_len;
        n_entry->name_len = strlen(file_name);
        n_entry->file_type = EXT2_FT_DIR;
        strncpy(n_entry->name, file_name, (int) n_entry->name_len +1);
        n_entry->name[(int) n_entry->name_len +1] = '\0';
    }
    
    // not enough empty_rec_len for this entry, need to open a new block
    if ((empty_rec_len = 0) || ((empty_rec_len > 0) && (empty_rec_len < required_rec_len))) {
        int *n_free_block = get_free_block(block_bitmap, 1);
        if (n_free_block == NULL){
            fprintf(stderr, "No empty block\n");
            exit(1);
        }
        inode->i_block[dir_num_blocks + 1] = *n_free_block;
        n_entry =(struct ext2_dir_entry_2*)(disk + 1024 * inode->i_block[dir_num_blocks+1]);
        n_entry->inode = new_inode_idx + 1;
        n_entry->rec_len = 1024;
        n_entry->name_len = strlen(file_name);
        n_entry->file_type = EXT2_FT_DIR;
        strncpy(n_entry->name, file_name, (int) n_entry->name_len +1);
        n_entry->name[(int) n_entry->name_len +1] = '\0';
        set_block_bitmap(disk + 1024 * gd->bg_block_bitmap, inode->i_block[dir_num_blocks+1], 1);
        gd->bg_free_blocks_count --;
        inode->i_size += 1024;
        inode->i_blocks += 2;
    }
    
    
    
    struct ext2_dir_entry_2 *self_entry;

    // Add .
    self_entry = (struct ext2_dir_entry_2 *)(disk + 1024 * n_inode->i_block[0]);
    self_entry->inode = new_inode_idx + 1;
    self_entry->rec_len = 12;
    self_entry->name_len = 1;
    self_entry->file_type |= EXT2_FT_DIR;
    strcpy(self_entry->name, ".");

    // Add ..
    self_entry = (struct ext2_dir_entry_2 *)(disk + 1024 * n_inode->i_block[0] + 12);
    self_entry->inode = inode_num;
    self_entry->rec_len = 1024 - 12;
    self_entry->name_len = 2;
    self_entry->file_type |= EXT2_FT_DIR;
    strcpy(self_entry->name, "..");
    
    return 0;
}
<file_sep>/readimage.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include "ext2.h"
#include <string.h>
#include <strings.h>

unsigned char *disk;

void aaaa(int *a) {
    *a += 1;
}

int main(int argc, char **argv) {

    if(argc != 2) {
        fprintf(stderr, "Usage: readimg <image file name>\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);
    }

    struct ext2_super_block *sb = (struct ext2_super_block *)(disk + 1024);
    struct ext2_group_desc *gd = (struct ext2_group_desc *)(disk + 2048);
    struct ext2_dir_entry_2 *entry;

    printf("Inodes: %d\n", sb->s_inodes_count);
    printf("Blocks: %d\n", sb->s_blocks_count);
    printf("Block group:\n");
    printf("\tblock bitmap: %d\n", gd->bg_block_bitmap);
    printf("\tinode bitmap: %d\n", gd->bg_inode_bitmap);
    printf("\tinode table: %d\n", gd->bg_inode_table);
    printf("\tfree blocks: %d\n", gd->bg_free_blocks_count);
    printf("\tfree inodes: %d\n", gd->bg_free_inodes_count);
    printf("\tused_dirs: %d\n", gd->bg_used_dirs_count);


    
    int i, j, tmp;
    printf("Block bitmap:");
    for(i = 0; i < 16; i++) {
        tmp = disk[gd->bg_block_bitmap * 1024 + i];
        for(j = 0; j < 8; j++) {
            if (tmp & 1)
                printf("1");
            else
                printf("0");
            tmp >>= 1;
        }
        printf(" ");
    }
    printf("\n");

    printf("Inode bitmap:");
    for(i = 0; i < 4; i++) {
        tmp = disk[gd->bg_inode_bitmap * 1024 + i];
        for(j = 0; j < 8; j++) {
            if (tmp & 1)
                printf("1");
            else
                printf("0");
            tmp >>= 1;
        }
        printf(" ");
    }
    printf("\n\nInodes:\n");

    int count;
    char type;
    for(i = 1; i < sb->s_inodes_count; i++) {
        if(i < EXT2_GOOD_OLD_FIRST_INO && i != EXT2_ROOT_INO - 1) {
            continue;
        } 
        struct ext2_inode *inode = (struct ext2_inode *)(disk + 1024 * gd->bg_inode_table + sizeof(struct ext2_inode) * i);

        if ((inode->i_mode & EXT2_S_IFLNK) == EXT2_S_IFLNK) {
            type = 'l';
        } else if (inode->i_mode & EXT2_S_IFDIR) {
            type = 'd';
        } else if ((inode->i_mode & EXT2_S_IFREG) == EXT2_S_IFREG ) {
            type = 'f';
        }
        if(inode->i_size != 0) {
            printf("[%d] type: %c size: %d links: %d blocks: %d\n", i + 1, type, inode->i_size, inode->i_links_count, inode->i_blocks);
            printf("[%d] Blocks: ", i + 1);
            for(j = 0; j < 12; j++) {
                if(inode->i_block[j] != 0)
                    printf(" %d", inode->i_block[j]);
            }
            if(inode->i_block[12] != 0) {
                entry = (struct ext2_dir_entry_2*)(disk + 1024 * inode->i_block[12]);
                count = 0;
                while(count < 1024 && disk[1024 * inode->i_block[12] + count] != 0) {
                    printf(" %d", disk[1024 * inode->i_block[12] + count]);
                    count += 4;
                }
            }
        
            printf("\n");
        }
    }

    
    char *name;
    
    for(i = 0; i < sb->s_inodes_count; i++) {
        if(i < EXT2_GOOD_OLD_FIRST_INO && i != EXT2_ROOT_INO - 1) {
            continue;
        }
        struct ext2_inode *inode = (struct ext2_inode *)(disk + 1024 * gd->bg_inode_table + 128 * i);
        if(inode->i_mode & EXT2_S_IFREG) {
            continue;
        }

        if(inode->i_size != 0) {
            for(j = 0; j < 12; j++) {
                if(inode->i_block[j] != 0) {
                    entry = (struct ext2_dir_entry_2*)(disk + 1024 * inode->i_block[j]);
                    
                    printf("\tDIR BLOCK NUM: %d (for inode %d)\n", inode->i_block[j], i + 1);
            
                    count = 0;
                    while(count < 1024) {
                        entry = (struct ext2_dir_entry_2*)(disk + 1024 * inode->i_block[j] + count);
                        
                        if (entry->file_type == EXT2_FT_REG_FILE) {
                            type = 'f';
                        } else if (entry->file_type == EXT2_FT_DIR) {
                            type = 'd';
                        } else if (entry->file_type == EXT2_FT_SYMLINK) {
                            type = 'l';
                        } else if (entry->file_type == EXT2_FT_UNKNOWN) {
                            type = 'u';
                        }
                        
                        count += entry->rec_len;
                        name = malloc(sizeof(char) * (entry->name_len + 1));
                        strncpy(name, entry->name, entry->name_len);
                        name[entry->name_len] = '\0';
                        printf("Inode: %d rec_len: %d name_len: %d type= %c name=%s\n", entry->inode, entry->rec_len, entry->name_len, type, name);
                        free(name);
                    }
                }
            }
            
        }
    }
    return 0;
}
<file_sep>/ext2_cp.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include "ext2.h"
#include "ext2_helper.h"

unsigned char *disk;

int main(int argc, const char * argv[]){
    if (argc != 4) {
        fprintf(stderr, "Usage: ext2_cp <image file name> <source file> <target path>\n");
        exit(1);
    }
    //image fd
    int fd = open(argv[1], O_RDWR);
    //local file
    int local_fd = open(argv[2], O_RDONLY);
    if (local_fd == -1) {
        fprintf(stderr, "This file does not exist.\n");
        return ENOENT;
    }
    
    //local file size and required blocks
    int file_size = lseek(local_fd, 0, SEEK_END);
    int file_block_num = (file_size - 1) / EXT2_BLOCK_SIZE + 1;
    int indirection = 0; // 0: no indirection, 1: indirection
    if (file_block_num > 12){
        indirection = 1;
        file_block_num++;
    }
    
    //record target path
    int path_len;
    int r_path;
    char *record_path;
    char *path;
    path_len = strlen(argv[3]);
    r_path = strlen(argv[3]);
    record_path = malloc(r_path+1);
    path = malloc(path_len+1);
    strncpy(record_path, argv[3],r_path);
    record_path[r_path] = '\0';
    strncpy(path, argv[3],path_len);
    path[path_len] = '\0';
    
    //record local filename
    int lc_len;
    char *lc_name;
    lc_len = strlen(argv[2]);
    lc_name = malloc(sizeof(char) * (lc_len + 1));
    strncpy(lc_name, argv[2], lc_len);
    lc_name[lc_len] = '\0';
    
    // check absolute path
    if (path[0] != '/') {
        fprintf(stderr, "This is not an absolute path!");
        exit(1);
    }
    
    disk = mmap(NULL, 128 * 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if(disk == MAP_FAILED) {
        perror("mmap");
        exit(1);
    }
    
    
    struct ext2_group_desc * gd = (struct ext2_group_desc *)(disk + 2048);
    void *inodes = disk + 1024 * gd->bg_inode_table;
    
    //get inode and block bitmap
    int *inode_bitmap = get_inode_bitmap(disk + 1024 * gd->bg_inode_bitmap);
    
    int *block_bitmap = get_block_bitmap(disk + 1024 * gd->bg_block_bitmap);
    
    struct ext2_inode *inode;
    struct ext2_inode *check_inode;
    
    // get inode number from absolute path
    //check if already dir
    // /a/c/ /a/c are different
    int inode_num, inode_num_p, type_add;
    char *file_name;
    char *file_parent_path;
    char *new_path;
    type_add = 1;
    //int new_path_len;
    
    inode_num = get_inode_num(path, inodes, disk);
    //inode for full path
    check_inode = (struct ext2_inode *)(inodes + sizeof(struct ext2_inode) * (inode_num - 1));
    // inode is file and already exist
    if (inode_num != -1) {
        // /abc/a
        if (check_inode->i_mode & EXT2_S_IFREG || check_inode->i_mode & EXT2_S_IFLNK ){
            perror("This file name existed.\n");
            exit(EEXIST);
        }
        else if (check_entry_file(lc_name, check_inode, disk) == -1) {
            perror("This file name existed2.\n");
            exit(EEXIST);
        }
        type_add = 0;//find correct directory /abc/a or /abc/a/ type0
    }
    
    // not exist
    if (inode_num == -1){
        get_file_parent_path(record_path, &file_parent_path);
        get_file_name(record_path, &file_name); //record file name
        new_path = malloc(strlen(file_parent_path));
        strcpy(new_path, file_parent_path); // record new path
        
        inode_num_p = get_inode_num(new_path, inodes, disk);
        if ((inode_num_p != -1) && (record_path[strlen(record_path) - 1] == '/')) {
            fprintf(stderr, "Illegal target path for copy1\n");
            return ENOENT;
        }
        // inode for parent path
        inode = (struct ext2_inode *)(disk + 1024 * gd->bg_inode_table + sizeof(struct ext2_inode) * (inode_num_p - 1));
        //check parent path
        //printf("%d\n",inode_num_p);
        if (inode_num_p == -1)  {
            fprintf(stderr, "Illegal target path for copy2\n");
            return ENOENT;
        }
    }
    
    
    //get a free new inode from inode bitmap
    int new_inode_idx = -1;
    int i;
    for (i = 0; i < 32; i++) {
        if (inode_bitmap[i] == 0) {
            new_inode_idx = i;
            break;
        }
    }
    
    //check new node for copy file
    if  (new_inode_idx == -1){ // no free inode to assign
        fprintf(stderr, "No free inode\n");
        exit(1);
    }
    
    //get free blocks from block bitmap
    int *free_blocks = get_free_block(block_bitmap, file_block_num);
    
    //check free_blocks enough
    if (free_blocks == NULL){
        fprintf(stderr, "Not enough blocks in the disk\n");
        exit(1);
    }
    
    //copy local file
    unsigned char* local_file = mmap(NULL, file_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, local_fd, 0);
    if(local_file == MAP_FAILED) {
        perror("mmap");
        exit(1);
    }
    
    
    
    // copy local file  to disk block
    int j,k;
    int total_size = file_size;
    int record = 0;
    
    // indirection and set reserved blocks
    // if need to do indirection
    if (indirection == 1){
        for (j = 0; j < 12; j++){
            set_block_bitmap(disk + 1024 * gd->bg_block_bitmap, free_blocks[j], 1);
            gd->bg_free_blocks_count --;
            if (total_size < EXT2_BLOCK_SIZE){
                memcpy(disk + 1024 * (free_blocks[j] +1), local_file + record, total_size);
                total_size = 0;
                break;
            }else{
                memcpy(disk + 1024 * (free_blocks[j] +1), local_file + record, 1024);
                total_size -= EXT2_BLOCK_SIZE;
                record += EXT2_BLOCK_SIZE;
            }
        }
        
        // set the last position to be the master idx to store blocks
        set_block_bitmap(disk + 1024 * gd->bg_block_bitmap, free_blocks[12], 1);
        gd->bg_free_blocks_count --;
        
        for (j = 13; j < file_block_num; j++){
            set_block_bitmap(disk + 1024 * gd->bg_block_bitmap, free_blocks[j], 1);
            gd->bg_free_blocks_count --;
            if (total_size < EXT2_BLOCK_SIZE){
                memcpy(disk + 1024 * (free_blocks[j] +1), local_file + record, total_size);
                total_size = 0;
                break;
            }else{
                memcpy(disk + 1024 * (free_blocks[j] +1), local_file + record, 1024);
                total_size -= EXT2_BLOCK_SIZE;
                record += EXT2_BLOCK_SIZE;
            }
        }
        
        disk[1024 * (free_blocks[12] + 1)] = free_blocks[12] + 1;
        int idx = 4;
        while (idx < 1024) {
            for (k = 13; k < file_block_num; k++){
                disk[1024 * (free_blocks[12] + 1) + idx] = free_blocks[k] + 1;
                idx += 4;
            }
            disk[1024 * (free_blocks[12] + 1) + idx] = 0;
            idx += 4;
        }
    }
    
    // not indirection
    // copy all file to block
    if (indirection == 0){
        for (j = 0; j < file_block_num; j++){
            set_block_bitmap(disk + 1024 * gd->bg_block_bitmap, free_blocks[j], 1);
            gd->bg_free_blocks_count --;
            if (total_size < EXT2_BLOCK_SIZE){
                memcpy(disk + 1024 * (free_blocks[j] +1), local_file + record, total_size);
                total_size = 0;
                break;
            }else{
                memcpy(disk + 1024 * (free_blocks[j] +1), local_file + record, 1024);
                total_size -= EXT2_BLOCK_SIZE;
                record += EXT2_BLOCK_SIZE;
            }
        }
    }
    
    //assign new node
    struct ext2_inode *n_inode = inodes + sizeof(struct ext2_inode) * new_inode_idx;
    //new_inode num = new_inode + 1
    n_inode->i_mode = EXT2_S_IFREG;
    n_inode->i_size = file_size;
    n_inode->i_links_count = 1;
    n_inode->i_blocks = file_block_num * 2;
    int z;
    for (z = 0; z < file_block_num; z++){
        if (z > 11){
            break;
        }
        n_inode->i_block[z] = free_blocks[z] + 1;
    }
    
    if (indirection == 1){
        n_inode->i_block[12] = free_blocks[12] + 1;
    }
    
    set_inode_bitmap(disk + 1024 * gd->bg_inode_bitmap, new_inode_idx, 1);
    gd->bg_free_inodes_count --;
    
    
    
    //condition1: type0 use current path node
    //condition2: type1 use parent path node with new name
    //new entry for new file in parent dir
    int count;
    struct ext2_dir_entry_2 *n_entry;
    struct ext2_dir_entry_2 *chk_entry;
    struct ext2_dir_entry_2 *past_entry;
    struct ext2_inode *dir_inode;
    char *final_name;
    
    if (type_add == 0) {
        dir_inode = check_inode;
        final_name = lc_name;
    }
    if (type_add == 1) {
        dir_inode = inode;
        final_name = file_name;
    }
    
    int required_rec_len = ((7 + strlen(final_name)) / 4 + 1) * 4;
    int dir_num_blocks = (dir_inode->i_size - 1) / 1024 + 1;
    int empty_rec_len = 0;
    //not indirected
    count = 0;
    if (dir_inode->i_block[dir_num_blocks - 1] != 0) {
        while (count < 1024) {
            chk_entry = (struct ext2_dir_entry_2*)(disk + 1024 * dir_inode->i_block[dir_num_blocks-1] + count);
            count += chk_entry->rec_len;
            if (count == 1024) {
                past_entry = chk_entry;
                int final_entry_rec_len = ((7 + chk_entry->name_len) / 4 + 1) * 4;
                empty_rec_len = chk_entry->rec_len - final_entry_rec_len;
                break;
            }
        }
    }
    
    //not indirected
    // enough empty rec_len for new entry name
    if ((empty_rec_len > 0) && (empty_rec_len >= required_rec_len)) {
        past_entry->rec_len = ((7 + past_entry->name_len) / 4 + 1) * 4;
        n_entry = (struct ext2_dir_entry_2*)(disk + 1024 * dir_inode->i_block[dir_num_blocks-1] + (1024 - empty_rec_len));
        n_entry->inode = new_inode_idx + 1;
        n_entry->rec_len = empty_rec_len;
        n_entry->name_len = strlen(final_name);
        n_entry->file_type = EXT2_FT_REG_FILE;
        strncpy(n_entry->name, final_name, (int) n_entry->name_len +1);
        n_entry->name[(int) n_entry->name_len +1] = '\0';
    }
    // not enough empty_rec_len for this entry, need to open a new block
    if ((empty_rec_len = 0) || ((empty_rec_len > 0) && (empty_rec_len < required_rec_len))) {
        int *n_free_block = get_free_block(block_bitmap, 1);
        if (n_free_block == NULL){
            fprintf(stderr, "No empty block\n");
            exit(1);
        }
        //get a new block and then set new entry
        dir_inode->i_block[dir_num_blocks + 1] = *n_free_block;
        n_entry =(struct ext2_dir_entry_2*)(disk + 1024 * dir_inode->i_block[dir_num_blocks+1]);
        n_entry->inode = new_inode_idx + 1;
        n_entry->rec_len = 1024;
        n_entry->name_len = strlen(final_name);
        n_entry->file_type = EXT2_FT_REG_FILE;
        strncpy(n_entry->name, final_name, (int) n_entry->name_len +1);
        n_entry->name[(int) n_entry->name_len +1] = '\0';
        set_block_bitmap(disk + 1024 * gd->bg_block_bitmap, dir_inode->i_block[dir_num_blocks+1], 1);
        gd->bg_free_blocks_count --;
        dir_inode->i_size += 1024;
        dir_inode->i_blocks += 2;
    }
    
    return 0;
}

<file_sep>/ext2_mkdir copy.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include "ext2.h"
#include "ext2_helper.h"

unsigned char *disk;

int main(int argc, const char * argv[]) {

    /* Check arg */
    if (argc != 3) {
        fprintf(stderr, "Usage: ext2_mkdir <image file name> <abs path>\n");
        exit(1);
    }

    int fd = open(argv[1], O_RDWR);
    char *path;

    path = malloc(strlen(argv[2]));
    strcpy(path, argv[2]);

    if (path[0] != '/') {
        fprintf(stderr, "This is not an absolute path!\n");
        exit(1);
    }

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

    struct ext2_group_desc * gd = (struct ext2_group_desc *)(disk + 2048);
    void *inodes = disk + 1024 * gd->bg_inode_table;
    int inode_num = get_inode_num(path, inodes, disk);

    if (inode_num != -1) {
        perror("The directory already exists.\n");
        exit(EEXIST);
    }

    char *parent_path;
    char *file_name;
    get_file_parent_path(path, &parent_path);
    inode_num = get_inode_num(parent_path, inodes, disk);

    if (inode_num == -1) {
        perror("The directory does not exist.\n");
        exit(ENOENT);
    }

    int i, j, k, l, tmp, set;
    set = 0;
    for (k = 0; k < 16; k ++) {
        tmp = disk[gd->bg_block_bitmap * 1024 + k];
        for (l = 0; l < 8; l ++) {
            if (!(tmp & 1)) {
                // printf("k: %d\tj: %d\n", k, l);
                set_block_bitmap(&disk[gd->bg_block_bitmap * 1024 + k], l, 1);
                gd->bg_free_blocks_count ++;
                set = 1;
                break;
            }
            tmp >>= 1;
        }
        if (set) break;
    }

    set = 0;
    for (i = 0; i < 16; i ++) {
        tmp = disk[gd->bg_inode_bitmap * 1024 + i];
        for (j = 0; j < 8; j ++) {
            if (!(tmp & 1)) {
                set_inode_bitmap(&disk[gd->bg_inode_bitmap * 1024 + i], j, 1);
                gd->bg_free_inodes_count ++;
                set = 1;
                break;
            }
            tmp >>= 1;
        }
        if (set) break;
    }

    struct ext2_inode *inode;
    inode = (struct ext2_inode *)(disk + 1024 * gd->bg_inode_table + 128 * (i * 8 + j));
    inode->i_size = 1;
    inode->i_mode |= EXT2_S_IFDIR;

    int inode_block_num;
    struct ext2_dir_entry_2 *entry;
    inode_block_num = 0;

    // Add .
    inode->i_block[0] = k * 8 + l;
    entry = (struct ext2_dir_entry_2 *)(disk + 1024 * inode->i_block[0]);
    entry->inode = i * 8 + j + 1;
    entry->rec_len = 12;
    entry->name_len = 1;
    entry->file_type |= EXT2_FT_DIR;
    strcpy(entry->name, ".");

    // Add ..
    entry = (struct ext2_dir_entry_2 *)(disk + 1024 * inode->i_block[0] + 12);
    entry->inode = inode_num;
    entry->rec_len = 1024 - 12;
    entry->name_len = 2;
    entry->file_type |= EXT2_FT_DIR;
    strcpy(entry->name, "..");

    // Add Entry in its parent node
    struct ext2_inode *parent_inode;
    parent_inode = (struct ext2_inode *)(disk + 1024 * gd->bg_inode_table + sizeof(struct ext2_inode) * (inode_num - 1));

    int count, timeout;
    count = 0;
    timeout = 0;
    while (count < 1024) {
        entry = (struct ext2_dir_entry_2 *)(disk + 1024 * parent_inode->i_block[0] + count);

        if (count + entry->rec_len >= 1024) {
            // If it is the last one
            entry->rec_len = ((7 + entry->name_len) / 4 + 1) * 4;
            count += entry->rec_len;
            break;
        }
        count += entry->rec_len;
        timeout ++;
        if (timeout > 10) {
            printf("Timeout, break.\n");
            break;
        }
    }

    // Entry for the new directory
    entry = (struct ext2_dir_entry_2 *)(disk + 1024 * parent_inode->i_block[0] + count);
    entry->file_type |= EXT2_FT_DIR;
    entry->rec_len = 1024 - count;
    get_file_name(path, &file_name);
    entry->name_len = strlen(file_name);
    strncpy(entry->name, file_name, entry->name_len);
    entry->inode = i * 8 + j + 1;
}
<file_sep>/ext2_helper.h

#ifndef ext2_helper_h
#define ext2_helper_h

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include "ext2.h"

/*
 * Get inode number with a path.
 *
 */
int get_inode_num(char *path, void *inodes, unsigned char *disk);

/*
 * Seperate file path and get the file name.
 *
 */
void get_file_name(char *file_path, char **file_name);

/*
 * Seperate file path and get the parent path of this file.
 *
 */
void get_file_parent_path(char *file_path, char **file_parent_path);


/*
 * Get an inode bitmap from disk
 */

int *get_inode_bitmap(void *inode_info);

/*
 * Get a block bitmap from disk
 */
int *get_block_bitmap(void *block_info);

/*
 * Get an array of free block index with block bitmap
 */
int *get_free_block(int *block_bitmap, int needed_num_blocks);


/*
 * Set bit to 0 or 1 on inode bitmap
 */
void set_inode_bitmap(void *inode_info, int inode_num, int bit);

/*
 * Set bit to 0 or 1 on block bitmap
 */
void set_block_bitmap(void *block_info, int block_num, int bit);

/*
 * Check where inode has already get the local file entry
 */
int check_entry_file(char *lc_file, struct ext2_inode *check_inode, unsigned char *disk);


#endif /* ext2_helper_h */
<file_sep>/ext2_helper.c
//
//  ext2_helper.c
//
//

#include "ext2_helper.h"
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include "ext2.h"

/*
 * Get an inode index with path from disk
 */
int get_inode_num(char *path, void *inodes, unsigned char *disk){
    int cur_inode_num = EXT2_ROOT_INO;
    int new_inode_num;
    int check_exist = -1;
    int count;
    char *name;
    char *token;
    //int inode_block_num;
    token = strtok(path, "/");

    struct ext2_inode *inode;
    struct ext2_dir_entry_2 *entry;

    if (strcmp(path, "/") == 0){
        return cur_inode_num;
    }

    new_inode_num = cur_inode_num;

    while (token != NULL){
        //printf("%s\n", token);
        check_exist = -1;
        inode = (struct ext2_inode *)(inodes + (new_inode_num - 1) * sizeof(struct ext2_inode));
        //inode->i_size?
        if (inode->i_size != 0) {
            count = 0;
            int i;
            for (i = 0; i < 12; i ++) {
                if (inode->i_block[i] != 0) {
                    while (count < 1024) {
                        entry = (struct ext2_dir_entry_2*)(disk+1024 * inode->i_block[i] + count);
                        count += entry->rec_len;
                        name = malloc(sizeof(char) * (entry->name_len+1));
                        strncpy(name, entry->name, entry->name_len);
                        name[entry->name_len] = '\0';
                        if (strcmp(token, name) == 0) {
                            new_inode_num = entry->inode;
                            check_exist = 0;
                            break;
                        }
                    }
                }
            }
            // indirection
            if (inode->i_block[12] != 0){
	            entry = (struct ext2_dir_entry_2*)(disk + 1024 * inode->i_block[12]);
		        count = 4;
		        while (count < 1024 && (disk[1024 * inode->i_block[12] + count] != 0)){
		            entry = (struct ext2_dir_entry_2*)(disk + 1024 * inode->i_block[12] + count);
		            count += 4;
		            name = malloc(sizeof(char) * (entry->name_len+1));
		            strncpy(name, entry->name, entry->name_len);
		            name[entry->name_len] = '\0';
                    if (strcmp(token, name) == 0){
                        new_inode_num = entry->inode;
                        check_exist = 0;
                        break;
		            }
                }
            }
        }
        free(name);
        // check whether this token exists in entries of this inode
        if (check_exist == -1) {
            return -1;
        }
        token = strtok(NULL, "/");
    }
    if (check_exist == 0) {
        return new_inode_num;
    }
    else {
        return -1;
    }
}

/*
 * Seperate original path to a parent directory path
 */

 void get_file_name(char *file_path, char **file_name) {

    int i;
    i = strlen(file_path) - 1;
    if (file_path[i] != '/'){
        while (i >= 0 && file_path[i] != '/') {
        i--;
    }
        *file_name = malloc(sizeof(char) * (strlen(file_path) - i));
        strncpy(*file_name, file_path + i + 1, strlen(file_path) - i);
    } else {
        while (i >= 0 && file_path[i-1] != '/') {
        i--;
    }
        *file_name = malloc(sizeof(char) * (strlen(file_path) - i - 1));
        strncpy(*file_name, file_path + i, strlen(file_path) - i - 1);
    }

    //return file_name;

}

/*
 * Seperate original path to a file path
 */

void get_file_parent_path(char *file_path, char **file_parent_path){

    int i;
    i = strlen(file_path) - 1;
    if (file_path[i] != '/'){
        while (i >= 0 && file_path[i] != '/') {
            i--;
        }

        *file_parent_path = malloc(sizeof(char) * (i + 1 + 1));

        strncpy(*file_parent_path, file_path, i +1);
        (*file_parent_path)[i+1] = '\0';

    }else{
        i--;
        while (i >= 0 && file_path[i] != '/') {
            i--;
        }
        *file_parent_path = malloc(sizeof(char) * (i + 1 + 1));
        strncpy(*file_parent_path, file_path, i + 1);
        (*file_parent_path)[i+1]= '\0';
    }

 }   //return file_parent_path;

/*
 * Get an inode bitmap from disk
 */

int *get_inode_bitmap(void *inode_info){
    int *inode_bitmap = malloc(sizeof(int) * 32);
    int i;
    for (i = 0; i < 32; i++) {
        char *byte = inode_info + (i / 8);
        inode_bitmap[i] = (*byte >> (i % 8)) & 1;
    }
    return inode_bitmap;
}

/*
 * Get a block bitmap from disk
 */

int *get_block_bitmap(void *block_info){
    int *block_bitmap = malloc(sizeof(int) * 128);
    int i;
    for (i = 0; i < 128; i++) {
        char *byte = block_info + (i / 8);
        block_bitmap[i] = (*byte >> (i % 8)) & 1;
    }
    return block_bitmap;
}

/*
 * Get an array of free block index with block bitmap
 */

int *get_free_block(int *block_bitmap, int needed_num_blocks){
    int i, j;
    j = 0;
    int *new_blocks = malloc(sizeof(int) * needed_num_blocks);
    for (i = 0; i < needed_num_blocks; i ++) {
        while (block_bitmap[j] != 0) {
            j++;
            if (j == 128) {
                return NULL;
            }
        }
        new_blocks[i] = j;
        j++;
    }
    return new_blocks;
}

/*
 * Set bit to 0 or 1 on inode bitmap
 */

void set_inode_bitmap(void *inode_info, int inode_num, int bit){
    int byte = inode_num / 8;
    int i_bit = inode_num % 8;
    char *change = inode_info + byte;
    *change = (*change & ~(1 << i_bit)) | (bit << i_bit);
}

/*
 * Set bit to 0 or 1 on block bitmap
 */
void set_block_bitmap(void *block_info, int block_num, int bit){
    int byte = block_num / 8;
    int b_bit = block_num % 8;
    char *change = block_info + byte;
    *change = (*change & ~(1 << b_bit)) | (bit << b_bit);
}

/*
 * Check where inode has already get the local file entry
 */
int check_entry_file(char *lc_file, struct ext2_inode *check_inode, unsigned char *disk){
    int i, count;
    char *name;
    struct ext2_dir_entry_2 *entry;
    
    if (check_inode->i_size != 0) {
        count = 0;
        for (i = 0; i < 12; i++) {
            if (check_inode->i_block[i] != 0) {
                while (count < 1024) {
                    entry = (struct ext2_dir_entry_2*)(disk + 1024 * check_inode->i_block[i] + count);
                    count += entry->rec_len;
                    if (entry->file_type == EXT2_FT_REG_FILE){
                        name = malloc(sizeof(char) * (entry->name_len+1));
                        strncpy(name, entry->name, entry->name_len);
                        name[entry->name_len] = '\0';
                        if (strcmp(lc_file, name) == 0) {
                            return -1;
                        }
                    }
                }
            }
        }

        return 0;
    }
    return 1;
}
<file_sep>/ext2_rm.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include "ext2.h"
#include "ext2_helper.h"

unsigned char *disk;

int main(int argc, const char * argv[]) {

    /* Check arg */
    if (argc != 3) {
        fprintf(stderr, "Usage: ext2_mkdir <image file name> <abs path>\n");
        exit(1);
    }

    /* Open */
    int fd = open(argv[1], O_RDWR);
    char *path;

    path = malloc(strlen(argv[2]));
    strcpy(path, argv[2]);

    if (path[0] != '/') {
        fprintf(stderr, "This is not an absolute path!\n");
        exit(1);
    }

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

    struct ext2_group_desc * gd = (struct ext2_group_desc *)(disk + 2048);
    void *inodes = disk + 1024 * gd->bg_inode_table;
    char *parent_path;
    char *file_name;
    get_file_parent_path(path, &parent_path);
    get_file_name(path, &file_name);
    int inode_num = get_inode_num(parent_path, inodes, disk);

    if (inode_num == -1) {
        perror("The file does not exist.\n");
        exit(ENOENT);
    }

    struct ext2_inode *inode = (struct ext2_inode *)(disk + 1024 * gd->bg_inode_table + sizeof(struct ext2_inode) * (inode_num - 1));
    struct ext2_dir_entry_2 *entry;
    struct ext2_dir_entry_2 *next_entry;
    int count, timeout, offset, found;
    char *name;
    count = 0;
    timeout = 0;
    offset = 0;
    found = 0;

    while (count < 1024) {

        entry = (struct ext2_dir_entry_2 *)(disk + 1024 * inode->i_block[0] + count);
        count += entry->rec_len;
        next_entry = (struct ext2_dir_entry_2 *)(disk + 1024 * inode->i_block[0] + count);

        name = malloc(sizeof(char) * next_entry->name_len + 1);
        strncpy(name, next_entry->name, next_entry->name_len);
        name[next_entry->name_len] = '\0';

        // If found
        if (strcmp(file_name, name) == 0) {

            found = 1;

            // If not a regular file
            if (next_entry->file_type & EXT2_S_IFREG) {
                perror("This is not a regular file.\n");
                exit(ENOENT);
            }

            // If is a regular file
            entry->rec_len += next_entry->rec_len;
            next_entry->rec_len = 0;
            next_entry->name_len = 0;
            strcpy(next_entry->name, "");
            next_entry->inode = 0;

        }

        free(name);

        timeout ++;
        if (timeout > 10) {
            break;
        }
    }

    /* If the file does not exist. */
    if (!found) {
        perror("The file does not exist.\n");
        exit(ENOENT);
    }

}
<file_sep>/Makefile
FLAGS = -Wall -g -std=c99
all : ext2_ls ext2_mkdir ext2_rm ext2_cp ext2_ln

ext2_ls: ext2_ls.o ext2_helper.o
	gcc ${FLAGS} -o ext2_ls ext2_ls.o ext2_helper.o

ext2_mkdir: ext2_mkdir.o ext2_helper.o
	gcc ${FLAGS} -o ext2_mkdir ext2_mkdir.o ext2_helper.o

ext2_rm: ext2_rm.o ext2_helper.o
	gcc ${FLAGS} -o ext2_rm ext2_rm.o ext2_helper.o

ext2_cp: ext2_cp.o ext2_helper.o
	gcc ${FLAGS} -o ext2_cp ext2_cp.o ext2_helper.o

ext2_ln: ext2_ln.o ext2_helper.o
	gcc ${FLAGS} -o ext2_ln ext2_ln.o ext2_helper.o

ext2_helper.o: ext2_helper.c ext2_helper.h ext2.h
	gcc ${FLAGS} -c ext2_helper.c

ext2_ls.o: ext2_ls.c ext2.h ext2_helper.h
	gcc ${FLAGS} -c ext2_ls.c

ext2_mkdir.o: ext2_mkdir.c ext2.h ext2_helper.h
	gcc ${FLAGS} -c ext2_mkdir.c

ext2_rm.o: ext2_rm.c ext2.h ext2_helper.h
	gcc ${FLAGS} -c ext2_rm.c

ext2_cp.o: ext2_cp.c ext2.h ext2_helper.h
	gcc ${FLAGS} -c ext2_cp.c

ext2_ln.o: ext2_ln.c ext2.h ext2_helper.h
	gcc ${FLAGS} -c ext2_ln.c

clean:
	rm *.o ext2_ls ext2_cp ext2_mkdir ext2_rm ext2_ln
<file_sep>/ext2_ls.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include "ext2.h"
#include "ext2_helper.h"

unsigned char *disk;

int main(int argc, const char * argv[]) {
    
    /* check correct path*/
    if((argc != 3) && (argc != 4)) {
        fprintf(stderr, "Usage: readimg <image file name> <abs path>\n");
        exit(1);
    }
    
    /* list all */
    if (argc == 4 && (strcmp(argv[2], "-a") != 0)) {
        fprintf(stderr, "Wrong command\n");
        exit(1);
    }
    
    /* Read and record path*/
    int fd = open(argv[1], O_RDWR);
    int path_len;
    char *path;
    char *record_path;
    int r_path;

    if (argc == 4 && (strcmp(argv[2], "-a") == 0)) {
        path_len = strlen(argv[3]);
        r_path = strlen(argv[3]);
        record_path = malloc(r_path+1);
        path = malloc(path_len+1);
        strncpy(record_path, argv[3],r_path);
        record_path[r_path] = '\0';
        strncpy(path, argv[3],path_len);
        path[path_len] = '\0';
    }

    if (argc == 3) {
        path_len = strlen(argv[2]);
        r_path = strlen(argv[2]);
        record_path = malloc(r_path+1);
        path = malloc(path_len+1);
        strncpy(record_path, argv[2],r_path);
        record_path[r_path] = '\0';
        strncpy(path, argv[2],path_len);
        path[path_len] = '\0';
    }

    if (path[0] != '/') {
        fprintf(stderr, "This is not an absolute path!\n");
        exit(1);
    }


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

    struct ext2_group_desc * gd = (struct ext2_group_desc *)(disk + 2048);
    void *inodes = disk + 1024 * gd->bg_inode_table;
    
    /* Test full pat*/
    int inode_num = get_inode_num(path, inodes, disk);
    if (inode_num == -1) {
        fprintf(stderr, "The directory or file does not exist.\n");
        return ENOENT;
    }

    struct ext2_inode *inode = (struct ext2_inode *)(disk + 1024 * gd->bg_inode_table + sizeof(struct ext2_inode) * (inode_num - 1));

    if (inode->i_mode & EXT2_S_IFREG || inode->i_mode & EXT2_S_IFLNK){
        char *file_name;
        get_file_name(record_path, &file_name);
        printf("%s\n", file_name);
        return 0;
    }
    
    if (inode -> i_size != 0) {
        int inode_block_num;
        int count;
        char *name;
        int check;
        struct ext2_dir_entry_2 *entry;
        /* print all entry name*/
        for (inode_block_num = 0; inode_block_num < 12; inode_block_num ++) {
            count = 0;
            check = 0;
            if (inode->i_block[inode_block_num] != 0) {

                while (count < 1024) {
                    entry = (struct ext2_dir_entry_2*)(disk + 1024 * inode -> i_block[inode_block_num] + count);
                    count += entry->rec_len;
                    check ++;
                    name = malloc(sizeof(char) * (entry->name_len+1));
                    strncpy(name, entry->name, entry->name_len);
                    name[entry->name_len] = '\0';
                    /* if -a, need to printf . & ..*/
                    if (argc == 4) {
                        printf("%s", name);
                        if (entry->rec_len != 1024) {
                            printf("\n");
                        }
                    }

                    else {
                        if (check >= 3) {
                            printf("%s", name);
                            if (entry -> rec_len != 1024) {
                                printf("\n");
                            }
                        }
                    }
                }
            }
        }
        
        /* indirection part */
        if (inode->i_block[12] != 0){
            entry = (struct ext2_dir_entry_2*)(disk + 1024 * inode->i_block[12]);
            count = 4;
            while (count < 1024 && (disk[1024 * inode->i_block[12] + count != 0])) {
                entry = (struct ext2_dir_entry_2*)(disk + 1024 * inode->i_block[12] + count);
                count += 4;
                name = malloc(sizeof(char) * (entry->name_len + 1));
                strncpy(name, entry->name, entry->name_len);
                name[entry->name_len] = '\0';
                printf("%s\n",name);
            }
        }
    }
    return 0;
}
<file_sep>/ext2_ln.c
//
//  ext2_ln.c
//
//
//  Created by <NAME> on 2016-07-28.
//
//

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include "ext2.h"
#include "ext2_helper.h"


// helper functions, better write in ext2_helper but there is a warning when compile it, might relate to C99
// if have time find a way to put it back to helper

unsigned char *disk;

int find_dirn(char* list, char* buf) {
  int i, j;

  if (list[0] != '/') {
    return -1;
  }

  for (i = 1, j = 0; list[i] != '/'; i++, j++) {
    if (list[i] == '\0') {
      break;
    }
    buf[j] = list[i];
  }
  buf[j] = '\0';

  return i;
}

int find_dir_inode(char* query, void* inodes) {

  struct ext2_inode* inode;
  struct ext2_dir_entry_2* dir;

  int n_inode = EXT2_ROOT_INO;
  int n_block;

  char buf[512];

  while (1) {
    inode = (struct ext2_inode*)
      (inodes + (n_inode - 1) * sizeof(struct ext2_inode));
    int total_size = inode->i_size;

    if (strcmp(query, "/") == 0) {
      return n_inode - 1;
    }

    if (strcmp(query, "") == 0) {
      query[1] = '\0';
      query[0] = '/';
    }


    int n = find_dirn(query, buf);

    query += n;
    int new_n_inode = -1;
    int n_blockidx = 0;
    int size = 0;

    n_block = inode->i_block[n_blockidx];
    dir = (struct ext2_dir_entry_2*)(disk + EXT2_BLOCK_SIZE * n_block);

    while (size < total_size) {
      size += dir->rec_len;
      if (dir->file_type == EXT2_FT_DIR) {
        if (strlen(buf) == dir->name_len &&
            strncmp(buf, dir->name, dir->name_len) == 0) {
          new_n_inode = dir->inode;
          break;
        }
      }

      dir = (void*)dir + dir->rec_len;

      if (size % EXT2_BLOCK_SIZE == 0) {
        n_blockidx++;
        n_block = inode->i_block[n_blockidx];
        dir = (struct ext2_dir_entry_2*)(disk + EXT2_BLOCK_SIZE * n_block);
      }
    }

    if (new_n_inode == -1) {
      return -1;
    } else {
      n_inode = new_n_inode;
    }

  }
}

/*
detach a file
*/
void file_detach(char* path, char* name) {

  int length = strlen(path);
  int counter = length - 1;
  while (path[counter] != '/') {
    counter--;
  }
  int len_n = length - 1 - counter;
  strncpy(name, path + length - len_n, len_n + 1);
  if (counter != 0) {
    path[counter] = '\0';
  } else {
    path[counter + 1] = '\0';
  }
}

int main(int argc, char **argv) {

  if ((argc != 4) && (argc != 5)) {
    fprintf(stderr, "Usage: ext2_ln <image file name> <source path> <target path>\n");
    exit(1);
  }

  if ((argc == 5) && strcmp(argv[2], "-a") != 0) {
    fprintf(stderr, "Wrong command\n");
  }

  int fd = open(argv[1], O_RDWR);

  char* src;
  char* des;
  if (argc == 4) {
    src = argv[2];
    des = argv[3];
  }
  if (argc == 5) {
    src = argv[3];
    des = argv[4];
  }

  src = argv[2];
  des = argv[3]; // how to put these in an if statement???

// error checking => if either location refers to a directory (EISDIR)
  if (src[strlen(src) - 1] == '/' || des[strlen(src) - 1] == '/'){
    errno = EISDIR;
    if (src[strlen(src) - 1] == '/') {
      perror(src);
    } else {
      perror(des);
    }
    exit(errno);
  }

// error checking => absolute path TODO: Is the check necessary?
  if (src[0] != '/' || des[0] != '/') {
    fprintf(stderr, "not absolute path\n");
    exit(1);
  }

  int i;
  char src_n[1024];
  char des_n[1024];
  int src_nlength;
  int des_nlength;
  file_detach(src, src_n);
  file_detach(des, des_n);
  src_nlength = strlen(src_n);
  des_nlength = strlen(des_n);

// map disk to memory
  disk = mmap(NULL, 128 * 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  struct ext2_group_desc* gd = (void*)disk + 1024 + EXT2_BLOCK_SIZE;
  void* inodes = disk + EXT2_BLOCK_SIZE * gd->bg_inode_table;
  void* block_bitmaploc = disk + EXT2_BLOCK_SIZE * gd->bg_block_bitmap;
  int* block_bitmap = get_block_bitmap(block_bitmaploc);
// error checking => in case map Failed
  if (disk == MAP_FAILED) {
    perror("mmap map failed\n");
    exit(1);
  }

  // find src dir, des dir
  int src_inodeidx = find_dir_inode(src, inodes); //TODO: move find_dir_inode
  int des_inodeidx = find_dir_inode(des, inodes); //TODO: move find_dir_inode

// error checking =>
  if (src_inodeidx < 0 || des_inodeidx < 0) {
    errno = ENOENT;
    if (src_inodeidx < 0) {
      perror(src);
    }
    if (des_inodeidx < 0) {
      perror(des);
    }
    exit(errno);
  }

  int desf_inodeidx = -1;
  struct ext2_inode* des_inode = des_inodeidx * sizeof(struct ext2_inode) + inodes;
  int des_num_blocks = des_inode->i_size / EXT2_BLOCK_SIZE;
  int block_pos;
  struct ext2_dir_entry_2* dir;

  for (i = 0; i < des_num_blocks; i++) {
    block_pos = des_inode->i_block[i];
    dir = (void*)dir + EXT2_BLOCK_SIZE * block_pos;
    int size = 0;
    while (size < EXT2_BLOCK_SIZE) {
      size += dir->rec_len;
      if (des_nlength == dir->name_len && strncmp(des_n, dir->name, des_nlength) == 0) {
        desf_inodeidx = dir->inode - 1;
        break;
      }
      dir = (void*)dir + dir->rec_len;
    }
    if (desf_inodeidx != -1) {
      break;
    }
  }
// error checking => file is already in directory
  if (desf_inodeidx != -1) {
    errno = EEXIST;
    perror(des_n);
    exit(errno);
  }


  // find the source file in source directory
  int srcf_inodeidx = -1;

  struct ext2_inode* src_inode = inodes + src_inodeidx * sizeof(struct ext2_inode);
  int src_num_blocks = src_inode->i_size / EXT2_BLOCK_SIZE;

  for (i = 0; i < src_num_blocks; i++) {
    block_pos = src_inode->i_block[i];
    dir = (void*)disk + EXT2_BLOCK_SIZE * block_pos;
    int size = 0;
    while (size < EXT2_BLOCK_SIZE) {
      size += dir->rec_len;
      if (des_nlength == dir->name_len && strncmp(des_n, dir->name, des_nlength) == 0) {
        if (dir->file_type == EXT2_FT_REG_FILE) {
          srcf_inodeidx = dir->inode - 1;
          break;
        }
      }
      dir = (void*)dir + dir->rec_len;
    }
    if (srcf_inodeidx != -1) {
      break;
    }
  }
// error checking => file not in directory
  if (srcf_inodeidx == -1) {
    errno = ENOENT;
    perror(src_n);
    exit(errno);
  }


  int exist = 1;
  int req_size = ((des_nlength - 1)/4+1)*4+8;
  for (i = 0; i < des_num_blocks; i++) {
    block_pos = des_inode->i_block[i];
    dir = (void*)disk + block_pos * EXT2_BLOCK_SIZE;
    int size = 0;
    while (size < EXT2_BLOCK_SIZE) {
      size += dir->rec_len;
      if (size == EXT2_BLOCK_SIZE){
        if ((dir->rec_len) >= req_size + 8 + ((dir->name_len - 1)/4+1)*4) {
          exist = 0;
          break;
        }
      }
      dir = (void*)dir + dir->rec_len;
    }
      if (exist == 0) {
        break;
      }
    }
    if (exist == 1) { // not found in this case
      block_bitmap = get_block_bitmap(block_bitmaploc);
      int* free_block = get_free_block(block_bitmap, 1);
      if (free_block == NULL) {
        errno = ENOSPC;
        perror("not enough free block\n");
        exit(errno);
      }
      int idx_temp = *free_block;
      dir = (void*)disk + EXT2_BLOCK_SIZE * (idx_temp + 1);
      dir->inode = srcf_inodeidx + 1;
      dir->file_type = EXT2_FT_REG_FILE;
      dir->rec_len = EXT2_BLOCK_SIZE;
      dir->name_len = des_nlength;
      strncpy((void*)dir + 8, des_n, des_nlength);
      set_block_bitmap(block_bitmaploc, idx_temp, 1);
      des_inode->i_size += EXT2_BLOCK_SIZE;
      des_inode->i_blocks += 2;
      des_inode->i_block[des_num_blocks] = idx_temp + 1;
    } else {
      int prev = dir->rec_len;
      int cls = ((dir->name_len -1)/4+1)*4+8;
      dir->rec_len = cls;
      dir = (void*)dir + cls;
      dir->inode = srcf_inodeidx + 1;
      dir->file_type = EXT2_FT_REG_FILE;
      dir->rec_len = prev - cls;
      dir->name_len = des_nlength;
      strncpy((void*)dir + 8, des_n, des_nlength);
    }

    // dont forget to update the count of links
    struct ext2_inode* inode = inodes + sizeof(struct ext2_inode) * srcf_inodeidx;
    inode->i_links_count++;

    return 0;
}
