diff options
Diffstat (limited to 'lib/libalpm')
| -rw-r--r-- | lib/libalpm/filelist.c | 178 | ||||
| -rw-r--r-- | lib/libalpm/filelist.h | 3 | 
2 files changed, 181 insertions, 0 deletions
| diff --git a/lib/libalpm/filelist.c b/lib/libalpm/filelist.c index 1928056d..0a62a2dc 100644 --- a/lib/libalpm/filelist.c +++ b/lib/libalpm/filelist.c @@ -17,10 +17,188 @@   *  along with this program.  If not, see <http://www.gnu.org/licenses/>.   */ +#include <limits.h>  #include <string.h> +#include <sys/stat.h>  /* libalpm */  #include "filelist.h" +#include "util.h" + +/** Helper function for comparing strings when sorting */ +static int _alpm_filelist_strcmp(const void *s1, const void *s2) +{ +	return strcmp(*(char **)s1, *(char **)s2); +} + +/** + * @brief Resolves a symlink and its children. + * + * @attention Pre-condition: files must be sorted! + * + * @param files filelist to resolve + * @param i index in files to start processing + * @param path absolute path for the symlink being resolved + * @param root_len length of the root portion of path + * @param resolving is file \i in \files a symlink that needs to be resolved + * + * @return the index of the last file resolved + */ +size_t _alpm_filelist_resolve_link( +		alpm_filelist_t *files, size_t i, char *path, size_t root_len, int resolving) +{ +	char *causal_dir = NULL; /* symlink being resolved */ +	char *filename_r = NULL; /* resolved filename */ +	size_t causal_dir_len = 0, causal_dir_r_len = 0; + +	if(resolving) { +		/* deal with the symlink being resolved */ +		MALLOC(filename_r, PATH_MAX, goto error); +		causal_dir = files->files[i].name; +		causal_dir_len = strlen(causal_dir); +		if(realpath(path, filename_r) == NULL) { +			STRDUP(files->resolved_path[i], causal_dir, goto error); +			FREE(filename_r); +			return i; +		} +		causal_dir_r_len = strlen(filename_r + root_len) + 1; +		if(causal_dir_r_len >= PATH_MAX) { +			STRDUP(files->resolved_path[i], causal_dir, goto error); +			FREE(filename_r); +			return i; +		} +		/* remove root_r from filename_r */ +		memmove(filename_r, filename_r + root_len, causal_dir_r_len); +		filename_r[causal_dir_r_len - 1] = '/'; +		filename_r[causal_dir_r_len] = '\0'; +		STRDUP(files->resolved_path[i], filename_r, goto error); +		i++; +	} + +	for(; i < files->count; i++) { +		char *filename = files->files[i].name; +		size_t filename_len = strlen(filename); +		size_t filename_r_len = filename_len; +		struct stat sbuf; +		int exists; + +		if(resolving) { +			if(filename_len < causal_dir_len || strncmp(filename, causal_dir, causal_dir_len) != 0) { +				/* not inside causal_dir anymore */ +				break; +			} + +			filename_r_len = filename_len + causal_dir_r_len - causal_dir_len; +			if(filename_r_len >= PATH_MAX) { +				/* resolved path is too long */ +				STRDUP(files->resolved_path[i], filename, goto error); +				continue; +			} + +			strcpy(filename_r + causal_dir_r_len, filename + causal_dir_len); +		} else { +			filename_r = filename; +		} + +		/* deal with files and paths too long to resolve*/ +		if(filename[filename_len - 1] != '/' || root_len + filename_r_len >= PATH_MAX) { +			STRDUP(files->resolved_path[i], filename_r, goto error); +			continue; +		} + +		/* construct absolute path and stat() */ +		strcpy(path + root_len, filename_r); +		exists = !_alpm_lstat(path, &sbuf); + +		/* deal with symlinks */ +		if(exists && S_ISLNK(sbuf.st_mode)) { +			i = _alpm_filelist_resolve_link(files, i, path, root_len, 1); +			continue; +		} + +		/* deal with normal directories */ +		STRDUP(files->resolved_path[i], filename_r, goto error); + +		/* deal with children of non-existent directories to reduce lstat() calls */ +		if (!exists) { +			for(i++; i < files->count; i++) { +				char *f = files->files[i].name;; +				size_t f_len = strlen(f); +				size_t f_r_len; + +				if(f_len < filename_len || strncmp(f, filename, filename_len) != 0) { +					/* not inside the non-existent dir anymore */ +					break; +				} + +				f_r_len = f_len + causal_dir_r_len - causal_dir_len; +				if(resolving && f_r_len <= PATH_MAX) { +					strcpy(filename_r + causal_dir_r_len, f + causal_dir_len); +					STRDUP(files->resolved_path[i], filename_r, goto error); +				} else { +					STRDUP(files->resolved_path[i], f, goto error); +				} +			} +			i--; +		} +	} + +	if(resolving) { +		FREE(filename_r); +	} + +	return i-1; + +error: +	if(resolving) { +		FREE(filename_r); +	} +	/* out of memory, not much point in going on */ +	return files->count; +} + +/** + * @brief Takes a file list and resolves all directory paths according to the + * filesystem + * + * @attention Pre-condition: files must be sorted! + * + * @note A symlink and directory at the same path in two difference packages + * causes a conflict so the filepath can not change as packages get installed + * + * @param handle the context handle + * @param files list of files to resolve + */ +void _alpm_filelist_resolve(alpm_handle_t *handle, alpm_filelist_t *files) +{ +	char path[PATH_MAX]; +	size_t root_len; + +	if(!files || files->resolved_path) { +		return; +	} + +	CALLOC(files->resolved_path, files->count, sizeof(char *), return); + +	/* not much point in going on if we can't even resolve root */ +	if(realpath(handle->root, path) == NULL){ +		return; +	} +	root_len = strlen(path) + 1; +	if(root_len >= PATH_MAX) { +		return; +	} +	path[root_len - 1] = '/'; +	path[root_len] = '\0'; + +	_alpm_filelist_resolve_link(files, 0, path, root_len, 0); + +	qsort(files->resolved_path, files->count, sizeof(char *), +			_alpm_filelist_strcmp); + +	return; +} +  /* Returns the difference of the provided two lists of files.   * Pre-condition: both lists are sorted! diff --git a/lib/libalpm/filelist.h b/lib/libalpm/filelist.h index 2d5cbc03..ef865da7 100644 --- a/lib/libalpm/filelist.h +++ b/lib/libalpm/filelist.h @@ -21,6 +21,9 @@  #include "alpm.h" +size_t _alpm_filelist_resolve_link(alpm_filelist_t *files, size_t i, +		char *path, size_t root_len, int resolving); +void _alpm_filelist_resolve(alpm_handle_t *handle, alpm_filelist_t *files);  alpm_list_t *_alpm_filelist_difference(alpm_filelist_t *filesA,  		alpm_filelist_t *filesB); | 
