/** * @brief fflush equivalent for entire volume. */ int winx_vflush(char volume_letter) { wchar_t path[] = L"\\??\\A:"; WINX_FILE *f; int result = -1; path[4] = winx_toupper(volume_letter); f = winx_fopen(path,"r+"); if(f){ result = winx_fflush(f); winx_fclose(f); } return result; }
/** * @internal * @brief Retrieves the NTFS data for the filesystem. * @param[out] pointer to the structure * receiving the information. * @return Zero for success, negative value otherwise. */ static int get_ntfs_data(winx_volume_information *v) { WINX_FILE *f; int result; /* open the volume */ f = winx_vopen(v->volume_letter); if(f == NULL) return (-1); /* get ntfs data */ result = winx_ioctl(f,FSCTL_GET_NTFS_VOLUME_DATA, "get_ntfs_data: ntfs data request",NULL,0, &v->ntfs_data,sizeof(NTFS_DATA),NULL); winx_fclose(f); return result; }
/** * @internal * @brief Retrieves the drive geometry. * @param[in] hRoot handle to the * root directory. * @param[out] pointer to the structure * receiving the drive geometry. * @return Zero for success, negative * value otherwise. */ static int get_drive_geometry(HANDLE hRoot,winx_volume_information *v) { FILE_FS_SIZE_INFORMATION ffs; IO_STATUS_BLOCK IoStatusBlock; NTSTATUS status; WINX_FILE *f; DISK_GEOMETRY dg; char buffer[32]; /* get drive geometry */ RtlZeroMemory(&ffs,sizeof(FILE_FS_SIZE_INFORMATION)); status = NtQueryVolumeInformationFile(hRoot,&IoStatusBlock,&ffs, sizeof(FILE_FS_SIZE_INFORMATION),FileFsSizeInformation); if(!NT_SUCCESS(status)){ strace(status,"cannot get geometry of drive %c:",v->volume_letter); return (-1); } /* fill all geometry related fields of the output structure */ v->total_bytes = (ULONGLONG)ffs.TotalAllocationUnits.QuadPart * \ ffs.SectorsPerAllocationUnit * ffs.BytesPerSector; v->free_bytes = (ULONGLONG)ffs.AvailableAllocationUnits.QuadPart * \ ffs.SectorsPerAllocationUnit * ffs.BytesPerSector; v->total_clusters = (ULONGLONG)ffs.TotalAllocationUnits.QuadPart; v->bytes_per_cluster = ffs.SectorsPerAllocationUnit * ffs.BytesPerSector; v->sectors_per_cluster = ffs.SectorsPerAllocationUnit; v->bytes_per_sector = ffs.BytesPerSector; /* optional: get device capacity */ v->device_capacity = 0; f = winx_vopen(v->volume_letter); if(f != NULL){ if(winx_ioctl(f,IOCTL_DISK_GET_DRIVE_GEOMETRY, "get_drive_geometry: device geometry request",NULL,0, &dg,sizeof(dg),NULL) >= 0){ v->device_capacity = dg.Cylinders.QuadPart * \ dg.TracksPerCylinder * dg.SectorsPerTrack * dg.BytesPerSector; winx_bytes_to_hr(v->device_capacity,1,buffer,sizeof(buffer)); itrace("%c: device capacity = %s",v->volume_letter,buffer); } winx_fclose(f); } return 0; }
/** * @internal * @brief Retrieves the volume dirty flag. * @param[out] pointer to the structure * receiving the volume dirty flag. */ static void get_volume_dirty_flag(winx_volume_information *v) { WINX_FILE *f; ULONG dirty_flag; int result; /* open the volume */ f = winx_vopen(v->volume_letter); if(f == NULL) return; /* get dirty flag */ result = winx_ioctl(f,FSCTL_IS_VOLUME_DIRTY, "get_volume_dirty_flag: dirty flag request", NULL,0,&dirty_flag,sizeof(ULONG),NULL); winx_fclose(f); if(result >= 0 && (dirty_flag & VOLUME_IS_DIRTY)){ etrace("%c: volume is dirty! Run CHKDSK to repair it.", v->volume_letter); v->is_dirty = 1; } }
static int save_lua_report(udefrag_job_parameters *jp) { wchar_t *path = NULL; WINX_FILE *f; wchar_t *cn; wchar_t compname[MAX_COMPUTERNAME_LENGTH + 1]; char utf8_compname[(MAX_COMPUTERNAME_LENGTH + 1) * 4]; char buffer[512]; struct prb_traverser t; winx_file_info *file; char *comment; char *status; int length; winx_time tm; /* should be enough for any path in UTF-8 encoding */ #define MAX_UTF8_PATH_LENGTH (256 * 1024) char *utf8_path; utf8_path = winx_tmalloc(MAX_UTF8_PATH_LENGTH); if(utf8_path == NULL){ mtrace(); return (-1); } path = get_report_path(jp); if(path == NULL) return UDEFRAG_NO_MEM; f = winx_fbopen(path,"w",RSB_SIZE); if(f == NULL){ f = winx_fopen(path,"w"); if(f == NULL){ winx_free(path); winx_free(utf8_path); return (-1); } } /* print header */ cn = winx_getenv(L"COMPUTERNAME"); if(cn){ wcsncpy(compname,cn,MAX_COMPUTERNAME_LENGTH + 1); compname[MAX_COMPUTERNAME_LENGTH] = 0; winx_free(cn); } else { wcscpy(compname,L"nil"); } winx_to_utf8(utf8_compname,sizeof(utf8_compname),compname); memset(&tm,0,sizeof(winx_time)); (void)winx_get_local_time(&tm); (void)_snprintf(buffer,sizeof(buffer), "-- UltraDefrag report for disk %c:\r\n\r\n" "format_version = 7\r\n\r\n" "volume_letter = \"%c\"\r\n" "computer_name = \"%hs\"\r\n\r\n" "current_time = {\r\n" "\tyear = %04i,\r\n" "\tmonth = %02i,\r\n" "\tday = %02i,\r\n" "\thour = %02i,\r\n" "\tmin = %02i,\r\n" "\tsec = %02i,\r\n" "\tisdst = false\r\n" "}\r\n\r\n" "files = {\r\n", jp->volume_letter, jp->volume_letter,utf8_compname, (int)tm.year,(int)tm.month,(int)tm.day, (int)tm.hour,(int)tm.minute,(int)tm.second ); buffer[sizeof(buffer) - 1] = 0; (void)winx_fwrite(buffer,1,strlen(buffer),f); /* print body */ prb_t_init(&t,jp->fragmented_files); file = prb_t_first(&t,jp->fragmented_files); while(file){ if(is_directory(file)) comment = "[DIR]"; else if(is_compressed(file)) comment = "[CMP]"; else comment = " - "; /* * On change of status strings don't forget * also to adjust write_file_status routine * in udreportcnv.lua file. */ if(is_locked(file)) status = "locked"; else if(is_moving_failed(file)) status = "move failed"; else if(is_in_improper_state(file)) status = "invalid"; else status = " - "; (void)_snprintf(buffer, sizeof(buffer), "\t{fragments = %u," "size = %I64u," "comment = \"%s\"," "status = \"%s\"," "path = \"", (UINT)file->disp.fragments, file->disp.clusters * jp->v_info.bytes_per_cluster, comment, status ); buffer[sizeof(buffer) - 1] = 0; (void)winx_fwrite(buffer,1,strlen(buffer),f); if(file->path != NULL){ /* skip \??\ sequence in the beginning of the path */ length = (int)wcslen(file->path); if(length > 4){ convert_to_utf8_path(utf8_path,MAX_UTF8_PATH_LENGTH,file->path + 4); } else { convert_to_utf8_path(utf8_path,MAX_UTF8_PATH_LENGTH,file->path); } (void)winx_fwrite(utf8_path,1,strlen(utf8_path),f); } (void)strcpy(buffer,"\"},\r\n"); (void)winx_fwrite(buffer,1,strlen(buffer),f); file = prb_t_next(&t); } /* print footer */ (void)strcpy(buffer,"}\r\n"); (void)winx_fwrite(buffer,1,strlen(buffer),f); itrace("report saved to %ws",path); winx_fclose(f); winx_free(path); winx_free(utf8_path); return 0; }
/** * @brief Retrieves the list of free regions on the volume. * @param[in] volume_letter the volume letter. * @param[in] flags the combination of WINX_GVR_xxx flags. * @param[in] cb the address of the procedure to be called * each time when the free region is found on the volume. * If the callback procedure returns nonzero value, * the scan terminates immediately. * @param[in] user_defined_data pointer to the data * passed to the registered callback. * @return List of the free regions, NULL indicates that * either disk is full (unlikely) or some error occured. * @note * - It is possible to scan disk partially by * requesting the scan termination through the callback * procedure. * - The callback procedure should complete as quickly * as possible to avoid slowdown of the scan. */ winx_volume_region *winx_get_free_volume_regions(char volume_letter, int flags, volume_region_callback cb, void *user_defined_data) { winx_volume_region *rlist = NULL, *rgn = NULL; BITMAP_DESCRIPTOR *bitmap; #define LLINVALID ((ULONGLONG) -1) #define BITMAPBYTES 4096 #define BITMAPSIZE (BITMAPBYTES + 2 * sizeof(ULONGLONG)) /* bit shifting array for efficient processing of the bitmap */ unsigned char bitshift[] = { 1, 2, 4, 8, 16, 32, 64, 128 }; WINX_FILE *f; ULONGLONG i, start, next, free_rgn_start; IO_STATUS_BLOCK iosb; NTSTATUS status; /* ensure that it will work on w2k */ volume_letter = winx_toupper(volume_letter); /* allocate memory */ bitmap = winx_malloc(BITMAPSIZE); /* open volume */ f = winx_vopen(volume_letter); if(f == NULL){ winx_free(bitmap); return NULL; } /* get volume bitmap */ next = 0, free_rgn_start = LLINVALID; do { /* get next portion of the bitmap */ memset(bitmap,0,BITMAPSIZE); status = NtFsControlFile(winx_fileno(f),NULL,NULL,0,&iosb, FSCTL_GET_VOLUME_BITMAP,&next,sizeof(ULONGLONG), bitmap,BITMAPSIZE); if(NT_SUCCESS(status)){ NtWaitForSingleObject(winx_fileno(f),FALSE,NULL); status = iosb.Status; } if(status != STATUS_SUCCESS && status != STATUS_BUFFER_OVERFLOW){ strace(status,"cannot get volume bitmap"); winx_fclose(f); winx_free(bitmap); if(flags & WINX_GVR_ALLOW_PARTIAL_SCAN){ return rlist; } else { winx_list_destroy((list_entry **)(void *)&rlist); return NULL; } } /* scan through the returned bitmap info */ start = bitmap->StartLcn; for(i = 0; i < min(bitmap->ClustersToEndOfVol, 8 * BITMAPBYTES); i++){ if(!(bitmap->Map[ i/8 ] & bitshift[ i % 8 ])){ /* cluster is free */ if(free_rgn_start == LLINVALID) free_rgn_start = start + i; } else { /* cluster isn't free */ if(free_rgn_start != LLINVALID){ /* add free region to the list */ rgn = (winx_volume_region *)winx_list_insert((list_entry **)(void *)&rlist, (list_entry *)rgn,sizeof(winx_volume_region)); rgn->lcn = free_rgn_start; rgn->length = start + i - free_rgn_start; if(cb != NULL){ if(cb(rgn,user_defined_data)) goto done; } free_rgn_start = LLINVALID; } } } /* go to the next portion of data */ next = bitmap->StartLcn + i; } while(status != STATUS_SUCCESS); if(free_rgn_start != LLINVALID){ /* add free region to the list */ rgn = (winx_volume_region *)winx_list_insert((list_entry **)(void *)&rlist, (list_entry *)rgn,sizeof(winx_volume_region)); rgn->lcn = free_rgn_start; rgn->length = start + i - free_rgn_start; if(cb != NULL){ if(cb(rgn,user_defined_data)) goto done; } free_rgn_start = LLINVALID; } done: /* cleanup */ winx_fclose(f); winx_free(bitmap); return rlist; }