#include "map.h"
#include "map_struct.h"
#include <maps_struct.h>
#include <map_info_struct.h>
#include <kmalloc.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include <stddef.h>
#include <limits.h>
static inline int check_for_addition(unsigned long i1, unsigned long i2)
{
return i1 <= ULONG_MAX - i2;
}
static int merge_map(struct maps_struct *maps, struct map_struct *map, struct map_struct *map1)
{
if (!map_merge(map, map1))
return 0;
if (map1->next) {
map->next = map1->next;
map1->next->previous = map;
}
else {
map->next = NULL;
maps->last_map = map;
}
return 1;
}
void init_maps(struct maps_struct *maps)
{
maps->first_map = NULL;
maps->last_map = NULL;
}
int insert_map(struct maps_struct *maps, struct map_struct *map)
{
struct map_struct *map1, *map2;
int st;
map1 = NULL;
map2 = maps->first_map;
while (map2) {
st = compare_maps(map, map2);
if (st == 0)
return 0;
if (st < 0)
break;
map1 = map2;
map2 = map2->next;
}
if (!map2) {
map->next = NULL;
maps->last_map = map;
}
else if (!merge_map(maps, map, map2)) {
map->next = map2;
map2->previous = map;
}
if (!map1) {
map->previous = NULL;
maps->first_map = map;
}
else if (!merge_map(maps, map1, map)) {
map->previous = map1;
map1->next = map;
}
return 1;
}
int clear_maps(struct maps_struct *maps, unsigned long start, unsigned long length)
{
struct map_struct *map1, *map2, *new_map, *next_map;
int st;
int rv;
map1 = NULL;
map2 = maps->first_map;
while (1) {
if (!map2)
return 0;
st = compare_map_laddr(map2, start);
if (st > 0)
break;
if (st == 0) {
if ((rv = map_split(map2, start, &new_map)) < 0)
return rv;
if (new_map) {
new_map->previous = map2;
new_map->next = map2->next;
map2->next = new_map;
if (new_map->next)
new_map->next->previous = new_map;
else
maps->last_map = new_map;
map1 = map2;
map2 = new_map;
}
break;
}
map1 = map2;
map2 = map2->next;
}
do {
if (check_for_addition(start, length)) {
st = compare_map_laddr(map2, start + length);
if (st > 0)
break;
if (st == 0) {
if ((rv = map_split(map2, start + length, &new_map)) < 0)
return rv;
if (!new_map)
break;
if (map2->next) {
new_map->next = map2->next;
new_map->next->previous = new_map;
}
else {
new_map->next = NULL;
maps->last_map = new_map;
}
if (map1) {
new_map->previous = map1;
map1->next = new_map;
}
else {
new_map->previous = NULL;
maps->first_map = new_map;
}
free_map(map2);
break;
}
}
if (map1) {
if (map2->next) {
map1->next = map2->next;
map2->next->previous = map1;
}
else {
map1->next = NULL;
maps->last_map = map1;
}
}
else {
if (map2->next) {
maps->first_map = map2->next;
map2->next->previous = NULL;
}
else {
maps->first_map = NULL;
maps->last_map = NULL;
}
}
next_map = map2->next;
free_map(map2);
map2 = next_map;
}
while (map2);
return 0;
}
int clone_maps(const struct maps_struct *maps, struct maps_struct *dest_maps, unsigned long start, unsigned long length)
{
struct map_struct *map, *new_map;
int st;
int rv;
for (map = maps->first_map; map; map = map->next) {
if ((rv = map_clone(map, start, length, &st, &new_map)) < 0)
return rv;
if (st > 0)
break;
if (st == 0)
assert(insert_map(dest_maps, new_map));
}
return 0;
}
int protect_maps(struct maps_struct *maps, unsigned long start, unsigned long length, int prot)
{
struct map_struct *map, *new_map;
int st;
int rv;
map = maps->first_map;
while (1) {
if (!map)
return 0;
st = compare_map_laddr(map, start);
if (st > 0)
break;
if (st == 0) {
if (compare_map_prot(map, prot)) {
map = map->next;
break;
}
if ((rv = map_split(map, start, &new_map)) < 0)
return rv;
if (new_map) {
new_map->previous = map;
new_map->next = map->next;
map->next = new_map;
if (new_map->next)
new_map->next->previous = new_map;
else
maps->last_map = new_map;
map = new_map;
}
break;
}
map = map->next;
}
do {
if (check_for_addition(start, length)) {
st = compare_map_laddr(map, start + length);
if (st > 0)
break;
if (st == 0) {
if (compare_map_prot(map, prot))
break;
if ((rv = map_split(map, start + length, &new_map)) < 0)
return rv;
if (!new_map)
break;
if (map->next) {
new_map->next = map->next;
new_map->next->previous = new_map;
}
else {
new_map->next = NULL;
maps->last_map = new_map;
}
new_map->previous = map;
map->next = new_map;
map_protect(map, prot);
break;
}
}
map_protect(map, prot);
map = map->next;
}
while (map);
return 0;
}
struct map_struct *find_map(const struct maps_struct *maps, unsigned long laddr)
{
struct map_struct *map;
for (map = maps->first_map; map; map = map->next)
if (compare_map_laddr(map, laddr) == 0)
return map;
return NULL;
}
struct map_struct *find_next_map(const struct maps_struct *maps, unsigned long laddr)
{
struct map_struct *map;
for (map = maps->first_map; map; map = map->next)
if (compare_map_laddr(map, laddr) >= 0)
return map;
return NULL;
}
unsigned long get_free_area(const struct maps_struct *maps, unsigned long base, unsigned long size, unsigned long length, int side)
{
const struct map_struct *map1, *map2;
unsigned long start, len;
int st;
if (length > size)
return 0;
if (side < 0) {
map1 = NULL;
map2 = maps->first_map;
while (1) {
st = hole_intersect(map1, map2, base, size, &start, &len);
if (st > 0)
return 0;
if (st == 0 && len >= length)
return start;
if (!map2)
return 0;
map1 = map2;
map2 = map2->next;
}
}
else {
map1 = maps->last_map;
map2 = NULL;
while (1) {
st = hole_intersect(map1, map2, base, size, &start, &len);
if (st < 0)
return 0;
if (st == 0 && len >= length)
return start + (len - length);
if (!map1)
return 0;
map2 = map1;
map1 = map1->previous;
}
}
}
int get_maps(const struct maps_struct *maps, struct map_info_struct **map_info_array, unsigned int *count)
{
int retval;
int map_count;
struct map_struct *map;
int i, j;
if (!maps->first_map) {
*map_info_array = NULL;
*count = 0;
return 0;
}
map_count = 0;
for (map = maps->first_map; map; map = map->next)
map_count++;
if (!(*map_info_array = kmalloc(map_count * sizeof (struct map_info_struct))))
return -ENOMEM;
*count = map_count;
i = 0;
for (map = maps->first_map; map; map = map->next) {
if ((retval = map_get_info(map, &(*map_info_array)[i])) < 0)
goto error;
i++;
}
return 0;
error:
for (j = 0; j < i; j++)
if ((*map_info_array)[j].pathname)
kfree((*map_info_array)[j].pathname, strlen((*map_info_array)[j].pathname) + 1);
kfree(*map_info_array, *count * sizeof (struct map_info_struct));
return retval;
}