// _ _ _______ _ _ _ _ // | | | | |__ __| | | | (_) (_) // | |__| | __ _ _ __ _ __ _ _| | | |__ __ _ _ __ | | _____ __ _ ___ ___ _ __ __ _ // | __ |/ _` | '_ \| '_ \| | | | | | '_ \ / _` | '_ \| |/ / __|/ _` | \ \ / / | '_ \ / _` | // | | | | (_| | |_) | |_) | |_| | | | | | | (_| | | | | <\__ \ (_| | |\ V /| | | | | (_| | // |_| |_|\__,_| .__/| .__/ \__, |_| |_| |_|\__,_|_| |_|_|\_\___/\__, |_| \_/ |_|_| |_|\__, | // | | | | __/ | __/ | __/ | // |_| |_| |___/ |___/ |___/ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <getopt.h> #include <sys/mman.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> #include "utils.h" #include "fat32.h" int main(int argc, char **argv) { /* Deal with optional arguments */ int ch; char *disk = NULL; int iFlag = 0, lFlag = 0, rMode = 0; char *fileName = NULL; char *sCode = NULL; while ((ch = getopt(argc, argv, "ilr:R:s:")) != -1) { switch (ch) { case 'i': iFlag = 1; break; case 'l': lFlag = 1; break; case 'r': rMode = 1; fileName = optarg; break; case 'R': rMode = 2; fileName = optarg; break; case 's': sCode = optarg; break; default: usage(); } } /* Check validity for fixed composition of optional arguments */ if (rMode == 2 && sCode == NULL) usage(); if (rMode == 0 && sCode != NULL) usage(); /* Create memory map for the FAT32 disk file */ if (argc - optind != 1) usage(); int fdisk = open(argv[optind], O_RDWR); struct stat fstats; fstat(fdisk, &fstats); disk = mmap(NULL, fstats.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fdisk, 0); /* Read the boot entry */ BootEntry *bootEntry = malloc(sizeof(BootEntry)); memcpy(bootEntry, disk, 90); /* Read the root directory, may spread across multiple clusters */ DirEntry **rootDirEntries = malloc(sizeof(DirEntry *) * 512); int dirEntryCount = 0; unsigned int rootDirCluster = bootEntry -> BPB_RootClus; while (rootDirCluster < FAT32EOF) { unsigned long posoff = followFAT(bootEntry, &rootDirCluster, disk, 1); /* Traverse directory entries in the current cluster of the root directory */ for (int i = 0; i * 32 < bootEntry -> BPB_BytsPerSec * bootEntry -> BPB_SecPerClus; i ++) { rootDirEntries[dirEntryCount] = malloc(sizeof(DirEntry)); memcpy(rootDirEntries[dirEntryCount], disk + posoff + i * 32, 32); if (rootDirEntries[dirEntryCount] -> DIR_Name[0] == 0x00) break; dirEntryCount ++; } } /* Deal with disk information flag */ if (iFlag) printFsInfo(bootEntry); /* Deal with root directory information flag */ if (lFlag) { int entryCount = 0; for (int i = 0; i < dirEntryCount; i ++) { if (printDirInfo(rootDirEntries[i]) == 0) entryCount ++; } printf("Total number of entries = %d\n", entryCount); } /* Deal with recovering contiguously allocated files */ if (rMode == 1) { int target = delContEntryIndex(fileName, sCode, disk, bootEntry, rootDirEntries, dirEntryCount); if (target == -1) printf("%s: file not found\n", fileName); else if (target == -2) printf("%s: multiple candidates found\n", fileName); else { unsigned int i = 0; DirEntry *targetEntry = rootDirEntries[target]; unsigned int targetCluster = (targetEntry -> DIR_FstClusHI << 16) | targetEntry -> DIR_FstClusLO; /* Check if the target file is empty */ if (targetCluster != 0) { /* Update FATs for the contiguous next cluster */ while (targetEntry -> DIR_FileSize > bootEntry -> BPB_BytsPerSec * bootEntry -> BPB_SecPerClus * (i + 1)) { updateFATs(bootEntry, targetCluster + i, disk, targetCluster + i + 1); i ++; } /* Update FATs for the last cluster */ updateFATs(bootEntry, targetCluster + i, disk, FAT32EOF); } /* Recover the name of the deleted file */ int targetInClus = target % (bootEntry -> BPB_BytsPerSec * bootEntry -> BPB_SecPerClus / 32); unsigned int targetDirClus = bootEntry -> BPB_RootClus; for (int j = 0; (j + 1) * (bootEntry -> BPB_BytsPerSec * bootEntry -> BPB_SecPerClus / 32) <= target; j ++) { followFAT(bootEntry, &targetDirClus, disk, 1); } memcpy(disk + followFAT(bootEntry, &targetDirClus, disk, 0) + targetInClus * 32, fileName, 1); if (sCode == NULL) printf("%s: successfully recovered\n", fileName); else printf("%s: successfully recovered with SHA-1\n", fileName); } } /* Deal with recovering non-contiguously allocated files */ else if (rMode == 2) { unsigned int *targets = malloc(sizeof(unsigned int) * 6); int target = delNonContClusters(fileName, sCode, disk, bootEntry, rootDirEntries, dirEntryCount, targets); if (target == -1) printf("%s: file not found\n", fileName); else { /* Check if the target file is empty */ if (targets[0] != 0) { /* Update FATs for the possibly non-contiguous next cluster */ for (unsigned int i = 1; i < targets[0]; i ++) { updateFATs(bootEntry, targets[i], disk, targets[i + 1]); } /* Update FATs for the last cluster */ updateFATs(bootEntry, targets[targets[0]], disk, FAT32EOF); } /* Recover the name of the deleted file */ int targetInClus = target % (bootEntry -> BPB_BytsPerSec * bootEntry -> BPB_SecPerClus / 32); unsigned int targetDirClus = bootEntry -> BPB_RootClus; for (int j = 0; (j + 1) * (bootEntry -> BPB_BytsPerSec * bootEntry -> BPB_SecPerClus / 32) <= target; j ++) { followFAT(bootEntry, &targetDirClus, disk, 1); } memcpy(disk + followFAT(bootEntry, &targetDirClus, disk, 0) + targetInClus * 32, fileName, 1); printf("%s: successfully recovered with SHA-1\n", fileName); } } /* Free allocated heap memory */ free(bootEntry); for (int i = 0; i < dirEntryCount; i ++) free(rootDirEntries[i]); free(rootDirEntries); /* Flush changes and unmap the FAT32 disk file */ msync(disk, fstats.st_size, MS_SYNC); munmap(disk, fstats.st_size); close(fdisk); return 0; }