diff options
Diffstat (limited to 'tests/fs-tests/integrity')
| -rw-r--r-- | tests/fs-tests/integrity/integck.c | 197 | 
1 files changed, 192 insertions, 5 deletions
| diff --git a/tests/fs-tests/integrity/integck.c b/tests/fs-tests/integrity/integck.c index 2e44419..4009c2a 100644 --- a/tests/fs-tests/integrity/integck.c +++ b/tests/fs-tests/integrity/integck.c @@ -63,6 +63,12 @@ struct file_info /* Each file has one of these */  	uint64_t check_run_no; /* Run number used when checking */  }; +struct symlink_info /* Each symlink has one of these */ +{ +	char *target_pathname; +	struct dir_entry_info *entry; /* dir entry of this symlink */ +}; +  struct dir_info /* Each directory has one of these */  {  	char *name; @@ -81,12 +87,13 @@ struct dir_entry_info /* Each entry in a directory has one of these */  	struct dir_entry_info *prev_link; /* List of hard links for same file */  	char *name;  	struct dir_info *parent; /* Parent directory */ -	char type; /* f => file, d => dir */ +	char type; /* f => file, d => dir, s => symlink */  	int checked; /* Temporary flag used when checking */  	union entry_  	{  		struct file_info *file;  		struct dir_info *dir; +		struct symlink_info *symlink;  		void *target;  	} entry;  }; @@ -295,6 +302,11 @@ static void add_dir_entry(struct dir_info *parent, char type, const char *name,  		dir->entry = entry;  		dir->name = copy_string(name);  		dir->parent = parent; +	} else if (entry->type == 's') { +		struct symlink_info *symlink = target; + +		entry->entry.symlink = symlink; +		symlink->entry = entry;  	}  } @@ -354,6 +366,7 @@ static struct dir_info *dir_new(struct dir_info *parent, const char *name)  static void file_delete(struct file_info *file);  static void file_unlink(struct dir_entry_info *entry); +static void symlink_remove(struct symlink_info *symlink);  static void dir_remove(struct dir_info *dir)  { @@ -368,6 +381,8 @@ static void dir_remove(struct dir_info *dir)  			dir_remove(entry->entry.dir);  		else if (entry->type == 'f')  			file_unlink(entry); +		else if (entry->type == 's') +			symlink_remove(entry->entry.symlink);  		else  			CHECK(0); /* Invalid struct dir_entry_info */  	} @@ -1196,6 +1211,55 @@ static void file_check(struct file_info *file, int fd)  	CHECK(link_count == file->link_count);  } +static char *symlink_path(const char *path, const char *target_pathname) +{ +	char *p; +	size_t len, totlen, tarlen; + +	if (target_pathname[0] == '/') +		return copy_string(target_pathname); +	p = strrchr(path, '/'); +	len = p - path; +	len += 1; +	tarlen = strlen(target_pathname); +	totlen = len + tarlen + 1; +	p = malloc(totlen); +	CHECK(p != NULL); +	strncpy(p, path, len); +	p[len] = '\0'; +	strcat(p, target_pathname); +	return p; +} + +void symlink_check(const struct symlink_info *symlink) +{ +	char *path, buf[8192], *target; +	struct stat st1, st2; +	ssize_t len; +	int ret1, ret2; + +	path = dir_path(symlink->entry->parent, symlink->entry->name); +	CHECK(lstat(path, &st1) != -1); +	CHECK(S_ISLNK(st1.st_mode)); +	CHECK(st1.st_nlink == 1); +	len = readlink(path, buf, 8192); +	CHECK(len > 0 && len < 8192); +	buf[len] = '\0'; +	CHECK(strlen(symlink->target_pathname) == len); +	CHECK(strncmp(symlink->target_pathname, buf, len) == 0); +	/* Check symlink points where it should */ +	ret1 = stat(path, &st1); +	target = symlink_path(path, symlink->target_pathname); +	ret2 = stat(target, &st2); +	CHECK(ret1 == ret2); +	if (ret1 != -1) { +		CHECK(st1.st_dev == st2.st_dev); +		CHECK(st1.st_ino == st2.st_ino); +	} +	free(target); +	free(path); +} +  static int search_comp(const void *pa, const void *pb)  {  	const struct dirent *a = (const struct dirent *) pa; @@ -1284,6 +1348,8 @@ static void dir_check(struct dir_info *dir)  			link_count += 1; /* <subdir>/.. */  		} else if (entry->type == 'f')  			file_check(entry->entry.file, -1); +		else if (entry->type == 's') +			symlink_check(entry->entry.symlink);  		else  			CHECK(0);  		entry = entry->next; @@ -1504,6 +1570,117 @@ static void rename_entry(struct dir_entry_info *entry)  	free(name);  } +static size_t str_count(const char *s, char c) +{ +	size_t count = 0; +	char cc; + +	while ((cc = *s++) != '\0') +		if (cc == c) +			count += 1; +	return count; +} + +static char *relative_path(const char *path1, const char *path2) +{ +	const char *p1, *p2; +	char *rel; +	size_t up, len, len2, i; + +	p1 = path1; +	p2 = path2; +	while (*p1 == *p2 && *p1) { +		p1 += 1; +		p2 += 1; +	} +	len2 = strlen(p2); +	up = str_count(p1, '/'); +	if (up == 0 && len2 != 0) +		return copy_string(p2); +	if (up == 0 && len2 == 0) { +		p2 = strrchr(path2, '/'); +		return copy_string(p2); +	} +	if (up == 1 && len2 == 0) +		return copy_string("."); +	if (len2 == 0) +		up -= 1; +	len = up * 3 + len2 + 1; +	rel = malloc(len); +	CHECK(rel != NULL); +	rel[0] = '\0'; +	if (up) { +		strcat(rel, ".."); +		for (i = 1; i < up; i++) +			strcat(rel, "/.."); +		if (len2) +			strcat(rel, "/"); +	} +	if (len2) +		strcat(rel, p2); +	return rel; +} + +static char *pick_symlink_target(const char *symlink_path) +{ +	struct dir_info *dir; +	struct dir_entry_info *entry; +	size_t r; +	char *path, *rel_path; + +	dir = pick_dir(); + +	if (tests_random_no(100) < 10) +		return dir_path(dir, make_name(dir)); + +	r = tests_random_no(dir->number_of_entries); +	entry = dir->first; +	while (entry && r) { +		entry = entry->next; +		--r; +	} +	if (!entry) +		entry = dir->first; +	if (!entry) +		return dir_path(dir, make_name(dir)); +	path = dir_path(dir, entry->name); +	if (tests_random_no(20) < 10) +		return path; +	rel_path = relative_path(symlink_path, path); +	free(path); +	return rel_path; +} + +static void symlink_new(struct dir_info *dir, const char *name) +{ +	struct symlink_info *s; +	char *path; +	size_t sz; + +	sz = sizeof(struct symlink_info); +	s = malloc(sz); +	CHECK(s != NULL); +	memset(s, 0, sz); +	add_dir_entry(dir, 's', name, s); + +	path = dir_path(dir, name); +	s->target_pathname = pick_symlink_target(path); +	CHECK(symlink(s->target_pathname, path) != -1); +	free(path); +} + +static void symlink_remove(struct symlink_info *symlink) +{ +	char *path; + +	path = dir_path(symlink->entry->parent, symlink->entry->name); + +	remove_dir_entry(symlink->entry); + +	CHECK(unlink(path) != -1); +	free(path); +} +  static void operate_on_dir(struct dir_info *dir);  static void operate_on_file(struct file_info *file); @@ -1515,6 +1692,13 @@ static void operate_on_entry(struct dir_entry_info *entry)  		rename_entry(entry);  		return;  	} +	if (entry->type == 's') { +		symlink_check(entry->entry.symlink); +		/* If shrinking, 1 time in 50, remove a symlink */ +		if (shrink && tests_random_no(50) == 0) +			symlink_remove(entry->entry.symlink); +		return; +	}  	if (entry->type == 'd') {  		/* If shrinking, 1 time in 50, remove a directory */  		if (shrink && tests_random_no(50) == 0) { @@ -1546,16 +1730,19 @@ static void operate_on_dir(struct dir_info *dir)  	struct dir_entry_info *entry;  	struct file_info *file; -	r = tests_random_no(12); +	r = tests_random_no(14);  	if (r == 0 && grow) -		/* When growing, 1 time in 12 create a file */ +		/* When growing, 1 time in 14 create a file */  		file_new(dir, make_name(dir));  	else if (r == 1 && grow) -		/* When growing, 1 time in 12 create a directory */ +		/* When growing, 1 time in 14 create a directory */  		dir_new(dir, make_name(dir));  	else if (r == 2 && grow && (file = pick_file()) != NULL) -		/* When growing, 1 time in 12 create a hard link */ +		/* When growing, 1 time in 14 create a hard link */  		link_new(dir, make_name(dir), file); +	else if (r == 3 && grow && tests_random_no(5) == 0) +		/* When growing, 1 time in 70 create a symbolic link */ +		symlink_new(dir, make_name(dir));  	else {  		/* Otherwise randomly select an entry to operate on */  		r = tests_random_no(dir->number_of_entries); | 
