/** * @internal * @brief Retrieves the volume label. * @param[in] hRoot handle to the * root directory. * @param[out] pointer to the structure * receiving the volume label. */ static void get_volume_label(HANDLE hRoot,winx_volume_information *v) { FILE_FS_VOLUME_INFORMATION *ffvi; int buffer_size; IO_STATUS_BLOCK IoStatusBlock; NTSTATUS status; /* reset label */ v->label[0] = 0; /* allocate memory */ buffer_size = (sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(wchar_t)) + (MAX_PATH + 1) * sizeof(wchar_t); ffvi = winx_malloc(buffer_size); /* try to get actual label */ RtlZeroMemory(ffvi,buffer_size); status = NtQueryVolumeInformationFile(hRoot,&IoStatusBlock,ffvi, buffer_size,FileFsVolumeInformation); if(!NT_SUCCESS(status)){ strace(status,"cannot get volume label of drive %c:", v->volume_letter); winx_free(ffvi); return; } wcsncpy(v->label,ffvi->VolumeLabel,MAX_PATH); v->label[MAX_PATH] = 0; winx_free(ffvi); }
/** * @brief Removes an item from a double linked list. * @details Frees memory allocated for the item to be removed. * @param[in,out] phead pointer to a variable pointing to the list head. * @param[in] item pointer to the item which must be removed. */ void winx_list_remove(list_entry **phead,list_entry *item) { /* * Avoid winx_dbg_xxx calls here * to avoid recursion. */ /* validate the item */ if(item == NULL) return; /* is list empty? */ if(*phead == NULL) return; /* remove alone first item? */ if(item == *phead && item->next == *phead){ winx_free(item); *phead = NULL; return; } /* remove first item? */ if(item == *phead){ *phead = (*phead)->next; } item->prev->next = item->next; item->next->prev = item->prev; winx_free(item); }
/** * @brief Queries an environment variable. * @param[in] name the environment variable name. * @return The value of the environment variable. * NULL indicates failure. * @note The returned string should be freed * by the winx_free call after its use. */ wchar_t *winx_getenv(wchar_t *name) { wchar_t *value; UNICODE_STRING n, v; NTSTATUS status; DbgCheck1(name,NULL); value = winx_malloc(MAX_ENV_VALUE_LENGTH * sizeof(wchar_t)); RtlInitUnicodeString(&n,name); v.Buffer = value; v.Length = 0; v.MaximumLength = MAX_ENV_VALUE_LENGTH * sizeof(wchar_t); status = RtlQueryEnvironmentVariable_U(NULL,&n,&v); if(!NT_SUCCESS(status)){ strace(status,"cannot query %ws",name); winx_free(value); return NULL; } if(value[0] == 0){ winx_free(value); return NULL; } return value; }
/** * @internal * @brief Saves the list of boot * execute programs to registry. * @return Zero for success, * negative value otherwise. */ static int save_boot_exec_list(struct cmd *list) { struct cmd *c; int length = 1; wchar_t *commands, *p; NTSTATUS status; for(c = list; c; c = c->next){ if(c->cmd[0]) length += (int)wcslen(c->cmd) + 1; if(c->next == list) break; } commands = winx_malloc(length * sizeof(wchar_t)); memset(commands,0,length * sizeof(wchar_t)); for(c = list, p = commands; c; c = c->next){ if(c->cmd[0]){ wcscpy(p,c->cmd); p += wcslen(c->cmd) + 1; } if(c->next == list) break; } status = RtlWriteRegistryValue(RTL_REGISTRY_CONTROL, L"Session Manager",L"BootExecute",REG_MULTI_SZ, commands,length * sizeof(wchar_t)); winx_free(commands); if(!NT_SUCCESS(status)){ strace(status,"cannot save list of boot execute commands"); return (-1); } return 0; }
/** * @brief Removes a file block from * the binary tree of all file blocks. * @return Zero for success, * negative value otherwise. */ int remove_block_from_file_blocks_tree(udefrag_job_parameters *jp, winx_blockmap *block) { struct file_block *fb; struct file_block b; if(block == NULL) return (-1); if(jp->file_blocks == NULL) return (-1); b.file = NULL; b.block = block; fb = prb_delete(jp->file_blocks,&b); if(fb == NULL){ /* the following debugging output indicates either a bug, or file system inconsistency */ etrace("failed for %p: VCN = %I64u, LCN = %I64u, LEN = %I64u", block, block->vcn, block->lcn, block->length); /* if block does not exist in tree, we have nothing to cleanup */ return 0; } winx_free(fb); return 0; }
static wchar_t *get_report_path(udefrag_job_parameters *jp) { wchar_t *instdir, *fpath; wchar_t *isportable;//genBTC wchar_t *path = NULL; isportable = winx_getenv(L"UD_IS_PORTABLE");//genBTC if(isportable != NULL){ /* portable version? */ fpath = winx_get_module_filename(); if(fpath == NULL){ etrace("cannot get program\'s path"); } else { winx_path_remove_filename(fpath); path = winx_swprintf(L"\\??\\%ws\\reports",fpath); if(path == NULL){ etrace("not enough memory (case 1)"); } else { (void)winx_create_directory(path); winx_free(path); } path = winx_swprintf(L"\\??\\%ws\\reports\\fraglist_%c.luar", fpath,winx_tolower(jp->volume_letter)); if(path == NULL) etrace("not enough memory (case 2)"); winx_free(fpath); } } else { instdir = winx_getenv(L"UD_INSTALL_DIR"); /* regular installation */ path = winx_swprintf(L"\\??\\%ws\\reports",instdir); if(path == NULL){ etrace("not enough memory (case 3)"); } else { (void)winx_create_directory(path); winx_free(path); } path = winx_swprintf(L"\\??\\%ws\\reports\\fraglist_%c.luar", instdir,winx_tolower(jp->volume_letter)); if(path == NULL) etrace("not enough memory (case 4)"); winx_free(instdir); } return path; }
/** * @brief Removes all fragmentation reports from the volume. */ void remove_fragmentation_report(udefrag_job_parameters *jp) { wchar_t *paths[] = { L"\\??\\%c:\\fraglist.luar", L"\\??\\%c:\\fraglist.txt", L"\\??\\%c:\\fraglist.htm", L"\\??\\%c:\\fraglist.html", NULL }; wchar_t path[MAX_PATH + 1]; wchar_t *new_path, *ext_path; int i; winx_dbg_print_header(0,0,I"*"); /* remove old reports from the root directory */ for(i = 0; paths[i]; i++){ _snwprintf(path,MAX_PATH,paths[i],jp->volume_letter); path[MAX_PATH] = 0; (void)winx_delete_file(path); } /* remove reports from the reports directory */ new_path = get_report_path(jp); if(new_path){ (void)winx_delete_file(new_path); winx_path_remove_extension(new_path); ext_path = winx_swprintf(L"%ws.txt",new_path); if(ext_path == NULL){ mtrace(); } else { (void)winx_delete_file(ext_path); winx_free(ext_path); } ext_path = winx_swprintf(L"%ws.html",new_path); if(ext_path == NULL){ mtrace(); } else { (void)winx_delete_file(ext_path); winx_free(ext_path); } winx_free(new_path); } }
/** * @internal * @brief Destroys list of * boot execute programs. */ static void destroy_boot_exec_list(struct cmd *list) { struct cmd *c; for(c = list; c; c = c->next){ winx_free(c->cmd); if(c->next == list) break; } winx_list_destroy((list_entry **)(void *)&list); }
/** * @internal * @brief Retrieves the name of the file system. * @param[in] hRoot handle to the root directory. * @param[out] pointer to the structure receiving * the filesystem name. * @return Zero for success, negative value otherwise. * @note We could analyze the first sector of the * partition directly, but this method is not so swift * as it accesses the disk physically. */ static int get_filesystem_name(HANDLE hRoot,winx_volume_information *v) { FILE_FS_ATTRIBUTE_INFORMATION *pfa; int fs_attr_info_size; IO_STATUS_BLOCK IoStatusBlock; NTSTATUS status; wchar_t fs_name[MAX_FS_NAME_LENGTH + 1]; int length; fs_attr_info_size = MAX_PATH * sizeof(WCHAR) + sizeof(FILE_FS_ATTRIBUTE_INFORMATION); pfa = winx_malloc(fs_attr_info_size); RtlZeroMemory(pfa,fs_attr_info_size); status = NtQueryVolumeInformationFile(hRoot,&IoStatusBlock,pfa, fs_attr_info_size,FileFsAttributeInformation); if(!NT_SUCCESS(status)){ strace(status,"cannot get file system name of drive %c:",v->volume_letter); winx_free(pfa); return (-1); } /* * pfa->FileSystemName.Buffer may be not NULL terminated * (theoretically), so name extraction is more tricky * than it should be. */ length = min(MAX_FS_NAME_LENGTH,pfa->FileSystemNameLength / sizeof(wchar_t)); wcsncpy(fs_name,pfa->FileSystemName,length); fs_name[length] = 0; _snprintf(v->fs_name,MAX_FS_NAME_LENGTH,"%ws",fs_name); v->fs_name[MAX_FS_NAME_LENGTH] = 0; /* cleanup */ winx_free(pfa); return 0; }
/** * @brief Destroys a double linked list. * @details Frees memory allocated for all list items. * @param[in,out] phead pointer to a variable * pointing to the list head. */ void winx_list_destroy(list_entry **phead) { list_entry *item, *next, *head; /* is list empty? */ if(*phead == NULL) return; head = *phead; item = head; do { next = item->next; winx_free(item); item = next; } while (next != head); *phead = NULL; }
/** * @internal * @brief Compares two boot execute commands. * @details Treats 'command' and 'autocheck command' as the same. * @param[in] reg_cmd the command read from the registry. * @param[in] cmd the command to be searched for. * @return Positive value indicates that the commands are equal, * zero indicates that they're different, negative value * indicates a failure of the comparison. */ static int cmd_compare(wchar_t *reg_cmd,const wchar_t *cmd) { wchar_t *long_cmd; int result; /* do we have the command registered as it is? */ if(!winx_wcsicmp(cmd,reg_cmd)) return 1; /* compare reg_cmd with 'autocheck {cmd}' */ long_cmd = winx_swprintf(L"autocheck %ws",cmd); if(long_cmd == NULL){ mtrace(); return (-1); } result = winx_wcsicmp(long_cmd,reg_cmd); winx_free(long_cmd); return (result == 0) ? 1 : 0; }
/** * @brief Adds a file block to * the binary tree of all file blocks. * @return Zero for success, * negative value otherwise. * @note Destroys the tree in case of errors. */ int add_block_to_file_blocks_tree(udefrag_job_parameters *jp, winx_file_info *file, winx_blockmap *block) { struct file_block *fb; void **p; if(file == NULL || block == NULL) return (-1); if(jp->file_blocks == NULL) return (-1); fb = winx_malloc(sizeof *fb); fb->file = file; fb->block = block; p = prb_probe(jp->file_blocks,(void *)fb); /* if a duplicate item exists... */ if(*p != fb){ etrace("a duplicate found"); winx_free(fb); } return 0; }
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; }
/** * @brief Auxiliary routine used to free memory allocated for tree items. */ static void free_item (void *prb_item, void *prb_param) { struct file_block *item = (struct file_block *)prb_item; winx_free(item); }