To guarantee stack safety you'd want to write out the tail recursion optimisation by hand and don't rely on the compiler to fix the recursive function call for you:
Code:
while (file->is_symlink()) file = resolve_symlink(file);
Note that while() loops, as opposed to your common for(i=...;i<...;i++), loops provokes pretty much the same demand for code scrutiny as recursive calls do.
The second part of the problem is less trivial. To detect circular dependencies exactly you'd need to cache all resolutions thus far:
Code:
hash_set->add(file);
file = resolve_symlink(file);
if (hash_set->contains(file)) generate_error();
Of course caching all symlinks might be a optimisation issue for the common 1-2 symlink case and a potentional memory issue for (an attacker's) really long symlink chains, so taking a heuristic approach to the issue to prevent abuse might be an overall better idea.
Code:
iterations++;
if (iterations > 8) generate_error();
file = resolve_symlink(file);
$.02