diff options
Diffstat (limited to 'toys')
-rw-r--r-- | toys/posix/ln.c | 36 |
1 files changed, 30 insertions, 6 deletions
diff --git a/toys/posix/ln.c b/toys/posix/ln.c index d8f4ce62..04b4f292 100644 --- a/toys/posix/ln.c +++ b/toys/posix/ln.c @@ -46,19 +46,43 @@ void ln_main(void) for (i=0; i<toys.optc; i++) { int rc; - char *try = toys.optargs[i]; + char *oldnew, *try = toys.optargs[i]; if (S_ISDIR(buf.st_mode)) new = xmprintf("%s/%s", dest, basename(try)); else new = dest; - // Silently unlink the existing target (if any) - if (toys.optflags & FLAG_f) unlink(new); + + // Force needs to unlink the existing target (if any). Do that by creating + // a temp version and renaming it over the old one, so we can retain the + // old file in cases we can't replace it (such as hardlink between mounts). + oldnew = new; + if (toys.optflags & FLAG_f) { + new = xmprintf("%s_XXXXXX", new); + rc = mkstemp(new); + if (rc >= 0) { + close(rc); + if (unlink(new)) perror_msg("unlink '%s'", new); + } + } rc = (toys.optflags & FLAG_s) ? symlink(try, new) : link(try, new); + if (toys.optflags & FLAG_f) { + if (!rc) { + int temp; + + rc = rename(new, oldnew); + temp = errno; + if (rc && unlink(new)) perror_msg("unlink '%s'", new); + errno = temp; + } + free(new); + new = oldnew; + } if (rc) - perror_exit("cannot create %s link from '%s' to '%s'", + perror_msg("cannot create %s link from '%s' to '%s'", (toys.optflags & FLAG_s) ? "symbolic" : "hard", try, new); - else if (toys.optflags & FLAG_v) - fprintf(stderr, "'%s' -> '%s'\n", new, try); + else + if (toys.optflags & FLAG_v) fprintf(stderr, "'%s' -> '%s'\n", new, try); + if (new != dest) free(new); } } |