diff options
Diffstat (limited to 'symlinks.c')
| -rw-r--r-- | symlinks.c | 82 | 
1 files changed, 49 insertions, 33 deletions
| diff --git a/symlinks.c b/symlinks.c index be9ace6c04..5a5e781a15 100644 --- a/symlinks.c +++ b/symlinks.c @@ -1,48 +1,64 @@  #include "cache.h" -int has_symlink_leading_path(const char *name, char *last_symlink) -{ +struct pathname { +	int len;  	char path[PATH_MAX]; -	const char *sp, *ep; -	char *dp; - -	sp = name; -	dp = path; - -	if (last_symlink && *last_symlink) { -		size_t last_len = strlen(last_symlink); -		size_t len = strlen(name); -		if (last_len < len && -		    !strncmp(name, last_symlink, last_len) && -		    name[last_len] == '/') -			return 1; -		*last_symlink = '\0'; +}; + +/* Return matching pathname prefix length, or zero if not matching */ +static inline int match_pathname(int len, const char *name, struct pathname *match) +{ +	int match_len = match->len; +	return (len > match_len && +		name[match_len] == '/' && +		!memcmp(name, match->path, match_len)) ? match_len : 0; +} + +static inline void set_pathname(int len, const char *name, struct pathname *match) +{ +	if (len < PATH_MAX) { +		match->len = len; +		memcpy(match->path, name, len); +		match->path[len] = 0;  	} +} + +int has_symlink_leading_path(int len, const char *name) +{ +	static struct pathname link, nonlink; +	char path[PATH_MAX]; +	struct stat st; +	char *sp; +	int known_dir; -	while (1) { -		size_t len; -		struct stat st; +	/* +	 * See if the last known symlink cache matches. +	 */ +	if (match_pathname(len, name, &link)) +		return 1; -		ep = strchr(sp, '/'); -		if (!ep) -			break; -		len = ep - sp; -		if (PATH_MAX <= dp + len - path + 2) -			return 0; /* new name is longer than that??? */ -		memcpy(dp, sp, len); -		dp[len] = 0; +	/* +	 * Get rid of the last known directory part +	 */ +	known_dir = match_pathname(len, name, &nonlink); + +	while ((sp = strchr(name + known_dir + 1, '/')) != NULL) { +		int thislen = sp - name ; +		memcpy(path, name, thislen); +		path[thislen] = 0;  		if (lstat(path, &st))  			return 0; +		if (S_ISDIR(st.st_mode)) { +			set_pathname(thislen, path, &nonlink); +			known_dir = thislen; +			continue; +		}  		if (S_ISLNK(st.st_mode)) { -			if (last_symlink) -				strcpy(last_symlink, path); +			set_pathname(thislen, path, &link);  			return 1;  		} - -		dp[len++] = '/'; -		dp = dp + len; -		sp = ep + 1; +		break;  	}  	return 0;  } | 
