/** * @brief Inserts an item to a double linked list. * @details Allocates memory for the item to be inserted. * @param[in,out] phead pointer to a variable pointing to the list head. * @param[in] prev pointer to an item preceeding to the new item. * If this parameter is NULL, the new head will be inserted. * @param[in] size the size of the item to be inserted, in bytes. * @return Pointer to the inserted list item. In case of allocation failure * this routine calls the killer registered by winx_set_killer and returns NULL then. */ list_entry *winx_list_insert(list_entry **phead,list_entry *prev,long size) { list_entry *new_item; /* * Avoid winx_dbg_xxx calls here * to avoid recursion. */ if(size < sizeof(list_entry)) return NULL; new_item = (list_entry *)winx_malloc(size); /* is list empty? */ if(*phead == NULL){ *phead = new_item; new_item->prev = new_item->next = new_item; return new_item; } /* insert as the new head? */ if(prev == NULL){ prev = (*phead)->prev; *phead = new_item; } /* insert after the item specified by prev argument */ new_item->prev = prev; new_item->next = prev->next; new_item->prev->next = new_item; new_item->next->prev = new_item; return new_item; }
/** * @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 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 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 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; }
/** * @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 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; }