//----------------------------------------------------------------------------- bool copy_directory( std::string source_path, std::string target_path ) { boost::system::error_code ec; // get the directory if( !get_directory( target_path ) ) return false; // clean this directory, i.e. remove all children but not this directory if( !clean_directory( target_path ) ) return false; boost::filesystem::path src_path( source_path ); boost::filesystem::path tgt_path( target_path ); typedef std::vector<boost::filesystem::path> paths_t; paths_t src_paths; copy( boost::filesystem::directory_iterator( src_path ), boost::filesystem::directory_iterator(), back_inserter( src_paths ) ); for( paths_t::const_iterator it = src_paths.begin(); it != src_paths.end(); it++ ) { boost::filesystem::path::iterator rel_path = --it->end(); boost::filesystem::path next_src_path = *it; boost::filesystem::path next_tgt_path = tgt_path; next_tgt_path /= *rel_path; //std::cout << next_src_path << " -> " << next_tgt_path << '\n'; if( boost::filesystem::is_directory( next_src_path, ec ) ) { // if it is a directory, recurse if( !copy_directory( next_src_path.string(), next_tgt_path.string() ) ) { return false; } } else { // otherwise, let boost handle the copy copy( next_src_path, next_tgt_path, ec ); if( ec != boost::system::errc::success ) { std::cerr << ec.message() << std::endl; return false; } } } return true; }
int main(int argc,char **argv) { struct libmsfat_disk_locations_and_info locinfo; struct libmsfat_file_io_ctx_t *fioctx_parent = NULL; struct libmsfat_file_io_ctx_t *fioctx = NULL; struct libmsfat_context_t *msfatctx = NULL; uint32_t first_lba=0,size_lba=0; const char *s_partition = NULL; libmsfat_FAT_entry_t fatent; uint32_t bytes_per_cluster; const char *s_image = NULL; libmsfat_cluster_t cluster; uint32_t zeroed_clusters=0; int i,fd; for (i=1;i < argc;) { const char *a = argv[i++]; if (*a == '-') { do { a++; } while (*a == '-'); if (!strcmp(a,"image")) { s_image = argv[i++]; } else if (!strcmp(a,"partition")) { s_partition = argv[i++]; } else if (!strcmp(a,"hole-punch")) { zero_by_holepunch = 1; } else { fprintf(stderr,"Unknown switch '%s'\n",a); return 1; } } else { fprintf(stderr,"Unexpected arg '%s'\n",a); return 1; } } if (libmsfat_sanity_check() != 0) { fprintf(stderr,"libmsfat sanity check fail\n"); return 1; } if (s_image == NULL) { fprintf(stderr,"zerofree --image <image> ...\n"); fprintf(stderr,"zero out all areas of the disk/partition that are not allocated by the filesystem.\n"); fprintf(stderr,"\n"); fprintf(stderr,"--partition <n> Hard disk image, use partition N from the MBR\n"); fprintf(stderr,"--hole-punch Zero unused spaces by hole punching (making the file sparse).\n"); fprintf(stderr," This can allow zeroing the empty spaces a LOT faster, only if\n"); fprintf(stderr," supported by the filesystem.\n"); return 1; } fd = open(s_image,O_RDWR|O_BINARY); if (fd < 0) { fprintf(stderr,"Unable to open disk image, %s\n",strerror(errno)); return 1; } { /* make sure it's a file */ _polyfill_struct_stat st; if (_polyfill_fstat(fd,&st) || (!S_ISREG(st.st_mode) && !S_ISBLK(st.st_mode))) { fprintf(stderr,"Image is not a file\n"); return 1; } } if (s_partition != NULL) { struct libpartmbr_context_t *ctx = NULL; int index = atoi(s_partition); int dfd = -1; if (index < 0) { fprintf(stderr,"Invalid index\n"); return 1; } ctx = libpartmbr_context_create(); if (ctx == NULL) { fprintf(stderr,"Cannot allocate libpartmbr context\n"); return 1; } /* good! hand it off to the context. use dup() because it takes ownership */ dfd = dup(fd); if (libpartmbr_context_assign_fd(ctx,dfd)) { fprintf(stderr,"libpartmbr did not accept file descriptor %d\n",fd); close(dfd); // dispose of it return 1; } dfd = -1; ctx->geometry.cylinders = 16384; ctx->geometry.sectors = 63; ctx->geometry.heads = 255; /* load the partition table! */ if (libpartmbr_context_read_partition_table(ctx)) { fprintf(stderr,"Failed to read partition table\n"); return 1; } /* index */ if ((size_t)index >= ctx->list_count) { fprintf(stderr,"Index too large\n"); return 1; } /* valid? */ struct libpartmbr_context_entry_t *ent = &ctx->list[(size_t)index]; if (ent->is_empty) { fprintf(stderr,"You chose an empty MBR partition\n"); return 1; } /* show */ printf("Chosen partition:\n"); printf(" Type: 0x%02x %s\n", (unsigned int)ent->entry.partition_type, libpartmbr_partition_type_to_str(ent->entry.partition_type)); if (!(ent->entry.partition_type == LIBPARTMBR_TYPE_FAT12_32MB || ent->entry.partition_type == LIBPARTMBR_TYPE_FAT16_32MB || ent->entry.partition_type == LIBPARTMBR_TYPE_FAT16B_8GB || ent->entry.partition_type == LIBPARTMBR_TYPE_FAT32_CHS || ent->entry.partition_type == LIBPARTMBR_TYPE_FAT32_LBA || ent->entry.partition_type == LIBPARTMBR_TYPE_FAT16B_LBA || ent->entry.partition_type == LIBPARTMBR_TYPE_FAT12_32MB_HIDDEN || ent->entry.partition_type == LIBPARTMBR_TYPE_FAT16_32MB_HIDDEN || ent->entry.partition_type == LIBPARTMBR_TYPE_FAT16B_8GB_HIDDEN || ent->entry.partition_type == LIBPARTMBR_TYPE_FAT32_CHS_HIDDEN || ent->entry.partition_type == LIBPARTMBR_TYPE_FAT32_LBA_HIDDEN || ent->entry.partition_type == LIBPARTMBR_TYPE_FAT16B_LBA_HIDDEN)) { fprintf(stderr,"MBR type does not suggest a FAT filesystem\n"); return 1; } if (ent->entry.number_lba_sectors == (uint32_t)0) { fprintf(stderr,"Partition has no size\n"); return 1; } first_lba = ent->entry.first_lba_sector; size_lba = ent->entry.number_lba_sectors; /* done */ ctx = libpartmbr_context_destroy(ctx); } else { lseek_off_t x; first_lba = 0; x = _polyfill_lseek(fd,0,SEEK_END); if (x < (lseek_off_t)0) x = 0; x /= (lseek_off_t)512UL; if (x > (lseek_off_t)0xFFFFFFFFUL) x = (lseek_off_t)0xFFFFFFFFUL; size_lba = (uint32_t)x; _polyfill_lseek(fd,0,SEEK_SET); } printf("Reading from disk sectors %lu-%lu (%lu sectors)\n", (unsigned long)first_lba, (unsigned long)first_lba+(unsigned long)size_lba-(unsigned long)1UL, (unsigned long)size_lba); { lseek_off_t x = (lseek_off_t)first_lba * (lseek_off_t)512UL; if (_polyfill_lseek(fd,x,SEEK_SET) != x || read(fd,sectorbuf,512) != 512) { fprintf(stderr,"Unable to read boot sector\n"); return 1; } } { const char *err_str = NULL; if (!libmsfat_boot_sector_is_valid(sectorbuf,&err_str)) { printf("Boot sector is not valid: reason=%s\n",err_str); return 1; } } { struct libmsfat_bootsector *p_bs = (struct libmsfat_bootsector*)sectorbuf; int dfd; if (libmsfat_bs_compute_disk_locations(&locinfo,p_bs)) { printf("Unable to locate disk locations.\n"); return 1; } msfatctx = libmsfat_context_create(); if (msfatctx == NULL) { fprintf(stderr,"Failed to create msfat context\n"); return -1; } dfd = dup(fd); if (libmsfat_context_assign_fd(msfatctx,dfd)) { fprintf(stderr,"Failed to assign file descriptor to msfat\n"); close(dfd); return -1; } dfd = -1; /* takes ownership, drop it */ msfatctx->partition_byte_offset = (uint64_t)first_lba * (uint64_t)512UL; if (libmsfat_context_set_fat_info(msfatctx,&locinfo)) { fprintf(stderr,"msfat library rejected disk location info\n"); return -1; } } fioctx_parent = libmsfat_file_io_ctx_create(); if (fioctx_parent == NULL) { fprintf(stderr,"Cannot alloc FIO ctx\n"); return 1; } fioctx = libmsfat_file_io_ctx_create(); if (fioctx == NULL) { fprintf(stderr,"Cannot alloc FIO ctx\n"); return 1; } printf("Scanning directory structure, to clean and repack diretories and zero file cluster tips\n"); if (libmsfat_file_io_ctx_assign_root_directory_with_parent(fioctx,fioctx_parent,msfatctx) == 0) clean_directory(fioctx,fioctx_parent,msfatctx,NULL); else fprintf(stderr,"Unable to assign root directory\n"); bytes_per_cluster = libmsfat_context_get_cluster_size(msfatctx); if (bytes_per_cluster == (uint32_t)0) { fprintf(stderr,"Unable to determine bytes per cluster\n"); return 1; } printf("Scanning FAT table and zeroing unallocated clusters.\n"); for (cluster=(libmsfat_cluster_t)2;cluster < msfatctx->fatinfo.Total_clusters;cluster++) { int percent; if (libmsfat_context_read_FAT(msfatctx,&fatent,cluster,0)) { fprintf(stderr,"\nERROR: unable to read FAT table entry #%lu\n", (unsigned long)cluster); continue; } if (msfatctx->fatinfo.FAT_size == 32) fatent &= libmsfat_FAT32_CLUSTER_MASK; percent = (int)(((uint64_t)cluster * (uint64_t)100UL) / msfatctx->fatinfo.Total_clusters); if (((uint32_t)cluster & 0xFF) == (uint32_t)0) { printf("\x0D" "Reading cluster %lu / %lu (%%%u) (%lu cleared) ", (unsigned long)cluster, (unsigned long)msfatctx->fatinfo.Total_clusters, percent, (unsigned long)zeroed_clusters); fflush(stdout); } if (fatent == (libmsfat_FAT_entry_t)0UL) { uint64_t offset; /* cluster is free! zero it! */ if (libmsfat_context_get_cluster_offset(msfatctx,&offset,cluster)) { fprintf(stderr,"\nERROR: unable to locate cluster #%lu\n", (unsigned long)cluster); continue; } do_zero(msfatctx,offset,bytes_per_cluster); zeroed_clusters++; } } printf("\nDone!\n"); /* this code focuses on FAT table #0. make sure to copy FAT 0 to FAT 1/2/3 etc. */ for (i=1;i < (int)msfatctx->fatinfo.FAT_tables;i++) { if (libmsfat_context_copy_FAT(msfatctx,/*dst*/i,/*src*/0)) fprintf(stderr,"Problem copying FAT table\n"); } /* FAT32: Need to update free cluster count */ if (msfatctx->fatinfo.FAT_size == 32) { if (libmsfat_context_update_fat32_free_cluster_count(msfatctx)) fprintf(stderr,"Problem updating FSInfo free cluster count\n"); } /* done */ fioctx_parent = libmsfat_file_io_ctx_destroy(fioctx_parent); fioctx = libmsfat_file_io_ctx_destroy(fioctx); msfatctx = libmsfat_context_destroy(msfatctx); close(fd); fd = -1; return 0; }
static void clean_directory(struct libmsfat_file_io_ctx_t *fioctx,struct libmsfat_file_io_ctx_t *fioctx_parent,struct libmsfat_context_t *msfatctx,struct libmsfat_dirent_t *dir_dirent) { struct libmsfat_dirent_t dirent; uint32_t last_ro_end=0; uint32_t truncate=0; uint32_t ro=0,wo=0; /* scan the directory. we need to recurse into subdirectories. */ if (libmsfat_file_io_ctx_rewinddir(fioctx,msfatctx,NULL) == 0) { while (libmsfat_file_io_ctx_readdir(fioctx,msfatctx,NULL,&dirent) == 0) { struct libmsfat_file_io_ctx_t *subfioctx; if (dirent.a.n.DIR_Attr & libmsfat_DIR_ATTR_VOLUME_ID) continue; if ((dirent.a.n.DIR_Attr & libmsfat_DIR_ATTR_DIRECTORY) && libmsfat_dirent_is_dot_dir(&dirent)) continue; subfioctx = libmsfat_file_io_ctx_create(); if (subfioctx != NULL && libmsfat_file_io_ctx_assign_from_dirent(subfioctx,msfatctx,&dirent) == 0) { { char tmp[64]; tmp[0] = 0; libmsfat_dirent_filename_to_str(tmp,sizeof(tmp),&dirent); printf("...cleaning "); if (dirent.a.n.DIR_Attr & libmsfat_DIR_ATTR_DIRECTORY) printf("subdirectory "); else printf("file cluster tip in "); printf("'%s'\n",tmp); } if (dirent.a.n.DIR_Attr & libmsfat_DIR_ATTR_DIRECTORY) clean_directory(subfioctx,fioctx,msfatctx,&dirent); else clean_file_cluster_tip(subfioctx,fioctx,msfatctx,&dirent); subfioctx = libmsfat_file_io_ctx_destroy(subfioctx); } } } /* scan the directory again, copying items back to fill in deleted and empty entries */ while (1) { if (libmsfat_file_io_ctx_lseek(fioctx,msfatctx,ro,/*flags*/0)) break; if (libmsfat_file_io_ctx_tell(fioctx,msfatctx) != ro) break; if (libmsfat_file_io_ctx_read(fioctx,msfatctx,&dirent,sizeof(dirent)) != sizeof(dirent)) break; /* skip empty/deleted dirent entries */ if (dirent.a.n.DIR_Name[0] == 0x00 || dirent.a.n.DIR_Name[0] == (char)0xE5) { ro += (uint32_t)sizeof(dirent); continue; } /* if ro != wo, move the dirent back */ if (ro != wo) { if (libmsfat_file_io_ctx_lseek(fioctx,msfatctx,wo,/*flags*/0)) break; if (libmsfat_file_io_ctx_tell(fioctx,msfatctx) != wo) break; if (libmsfat_file_io_ctx_write(fioctx,msfatctx,&dirent,sizeof(dirent)) != sizeof(dirent)) break; } ro += (uint32_t)sizeof(dirent); wo += (uint32_t)sizeof(dirent); last_ro_end = ro; } /* if we removed anything, say so (NTS: the above code should always cause wo < ro) */ if (last_ro_end != wo) printf("Removed %lu empty/deleted directory entries\n", ((unsigned long)(last_ro_end-wo)) / (unsigned long)sizeof(dirent)); /* take note where the write pointer is. we will truncate there after zeroing the directory. * but, do not truncate the directory to zero! it is not specified in Microsoft's standards * document whether an empty directory is given at minimum one cluster, or whether it is not * given any clusters and the starting cluster is zero (like a file). to be safe, we do not * allow truncating to zero. */ truncate = wo; if (truncate == (uint32_t)0) truncate = (uint32_t)sizeof(dirent); /* fill out the rest of the directory with zeros */ memset(&dirent,0,sizeof(dirent)); if (libmsfat_file_io_ctx_lseek(fioctx,msfatctx,wo,/*flags*/0)) return; if (libmsfat_file_io_ctx_tell(fioctx,msfatctx) != wo) return; while (libmsfat_file_io_ctx_write(fioctx,msfatctx,&dirent,sizeof(dirent)) == sizeof(dirent)) { }; /* truncate, if possible (if the directory is based on a cluster chain) */ if (fioctx->is_cluster_chain) { if (libmsfat_file_io_ctx_truncate_file(fioctx,fioctx_parent,msfatctx,dir_dirent,NULL,truncate)) fprintf(stderr,"ERROR: failed to truncate directory (truncate point=%lu first_cluster=%lu)\n", (unsigned long)truncate, (unsigned long)fioctx->first_cluster); } }