This was broken in multiple ways. For instance, the overlay-
check of identical files (name and target) was omitted for
symbolic links for some reason.
While at it, I fixed the stat-handling, improved the error-
messages so the right paths were shown and removed the
illegimite bail-out when the target-fstatat failed (we want
only a warning here as well).
Get rid of the !!()-constructs and use ret where available (or introduce it).
In some cases, there would be an "abort" on the first fshut-error, but we want
to close all files and report all warnings and then quit, not just the warning
for the first file.
The argument handling was quite garbled up. So I fixed it.
In the process, it drops a lot of locs.
Previously, it would lead to an off-by-one in an edge case, so
stop messing around with argv and use an idiomatic fp- and fname-
array.
Now this works fine and is much easier to read.
This is also the first step towards going back to strcmp() instead
of handrolling the "-"-checks.
Otherwise, we run into problems in a typical autoconf-based build
system:
- config.status is created at some point between two seconds.
- config.status is run, generating Makefile by first writing to a file
in /tmp, and then mv-ing it to Makefile.
- If this mv happens before the beginning of the next second, Makefile
will be created with the same tv_sec as config.status, but with
tv_nsec = 0.
- When make runs, it sees that Makefile is older than config.status,
and re-runs config.status to generate Makefile.
In general, POSIX does not define /dev/std{in, out, err} because it
does not want to depend on the dev-filesystem.
For utilities, it thus introduced the '-'-keyword to denote standard
input (and output in some cases) and the programs have to deal with
it accordingly.
Sadly, the design of many tools doesn't allow strict shell-redirections
and many scripts don't even use this feature when possible.
Thus, we made the decision to implement it consistently across all
tools where it makes sense (namely those which read files).
Along the way, I spotted some behavioural bugs in libutil/crypt.c and
others where it was forgotten to fshut the files after use.
This allows for creating dangling symlinks with force applied:
# Before:
$ ln -sf non-existant target
ln: stat non-existent: No such file or directory
$ ls -l target
ls: lstat target: No such file or directory
# After:
$ ln -sf non-existant target
$ ls -l target
lrwxrwxrwx 1 eu users 12 May 08 07:50 target -> non-existent
This also allows creating relative non-dangling symlinks with force applied:
touch existant; mkdir dir
# Before
$ ln -sf ../existant dir
ln: stat ../existant: No such file or directory
$ ls -l dir
# After
$ ln -sf ../existant dir
$ ls -l dir
lrwxrwxrwx 1 eu users 11 May 08 07:53 existant -> ../existant
The check for whether each src and to pairs are on the same device with the
same inode are only needed for hardlinks so that a forcefull link does
not remove the underlying file:
touch f; mkdir dir
# Before:
$ ln -f f f dir
ln: f and f are the same file
$ ls -i f dir/f
3670611 dir/f
3670611 f
# After:
$ ln -f f f dir
ln: f and f are the same file
$ ls -i f dir/f
4332236 dir/f
4332236 f
Use the *at functions instead of building paths manually. We do
still have path-building in recurse() and other areas, but the
long-term goal is to rid most interfaces of that for practical
and security reasons.
In this case, it's more or less trivial.
Also, refactor the manpage to be more consistent with the others.
BUGFIX: Return exit status 3 on error.