#include <fcntl.h> #include <linux/limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/sendfile.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #define err(e) \ do { \ perror(e); \ exit(EXIT_FAILURE); \ } while (0) int main(int argc, char *argv[]) { int realfd, destfd; struct stat s; char real[PATH_MAX], cwd[PATH_MAX], in[PATH_MAX]; if (argc != 2) { printf("Usage: %s [file]", argv[0]); exit(EXIT_FAILURE); } if (getcwd(cwd, PATH_MAX) == NULL) err("getcwd"); /* Absolute paths are required here as the new link will have to point to the * approriate file even if they aren't relative to one another. Example: * A/B/file * C/D/link -> ../../A/B/file * * Inside of C/ I run invsymlink and therefore the argv[1] will be "D/link". * This means that unless the path is conveted to absolute the new link * willdon point to "D/link" which cannot be resolved relative to A/B/ * */ snprintf(in, PATH_MAX, "%s/%s", cwd, argv[1]); if (lstat(in, &s) == -1) err("lstat"); if (!S_ISLNK(s.st_mode)) { fprintf(stderr, "%s is not a symlink\n", in); exit(EXIT_FAILURE); } if (realpath(in, real) == NULL) err("realpath/real"); if ((realfd = open(real, O_RDONLY)) == -1) err("open/real"); if (stat(real, &s) == -1) err("stat"); if (unlink(in) == -1) err("unlink"); if ((destfd = open(in, O_WRONLY | O_CREAT, 0666)) == -1) err("open/dest"); if (sendfile(destfd, realfd, 0, s.st_size) == -1) err("sendfile"); if (unlink(real) == -1) err("unlink"); if (symlink(in, real) == -1) err("symlink"); close(realfd); close(destfd); return 0; }