From 57604cfcb25c77358055c7e398a5bd43cae2cf13 Mon Sep 17 00:00:00 2001 From: "J. Brandt Buckley" Date: Wed, 4 Jul 2012 18:03:32 -0700 Subject: [PATCH] Initial commit. --- Makefile | 15 +++ symlinks.8 | 123 ++++++++++++++++++++ symlinks.c | 362 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ symlinks.lsm | 36 ++++++ 4 files changed, 536 insertions(+) create mode 100644 Makefile create mode 100644 symlinks.8 create mode 100644 symlinks.c create mode 100644 symlinks.lsm diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f305449 --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +# Makefile for symlinks + +CC = gcc + +all: symlinks + +symlinks: symlinks.c + $(CC) -Wall -Wstrict-prototypes -O2 ${CFLAGS} -o symlinks symlinks.c + +install: all symlinks.8 + install -m 755 -o root -g root symlinks /usr/local/bin + install -m 644 -o root -g root symlinks.8 /usr/local/man/man8 + +clean: + rm -f symlinks *.o core diff --git a/symlinks.8 b/symlinks.8 new file mode 100644 index 0000000..de293ab --- /dev/null +++ b/symlinks.8 @@ -0,0 +1,123 @@ +.TH SYMLINKS 8 "October 2008" "Version 1.4" + +.SH NAME +symlinks \- symbolic link maintenance utility +.SH SYNOPSIS +.B symlinks +[ +.B -cdorstv +] +dirlist +.SH DESCRIPTION +.BI symlinks +is a useful utility for maintainers of FTP sites, CDROMs, +and Linux software distributions. +It scans directories for symbolic links and lists them on stdout, +often revealing flaws in the filesystem tree. +.PP +Each link is output with a classification of +.B relative, +.B absolute, +.B dangling, +.B messy, +.B lengthy, +or +.B other_fs. +.PP +.B relative +links are those expressed as paths relative to the directory in which +the links reside, usually independent of the mount point of the filesystem. +.PP +.B absolute +links are those given as an absolute path from the root directory +as indicated by a leading slash (/). +.PP +.B dangling +links are those for which the target of the link does not currently exist. +This commonly occurs for +.B absolute +links when a filesystem is mounted at other than its +customary mount point (such as when the normal root filesystem is +mounted at /mnt after booting from alternative media). +.PP +.B messy +links are links which contain unnecessary slashes or dots in the path. +These are cleaned up as well when +.B -c +is specified. +.PP +.B lengthy +links are links which use "../" more than necessary in the path +(eg. /bin/vi -> ../bin/vim) +These are only detected when +.B -s +is specified, and are only cleaned up when +.B -c +is also specified. +.PP +.B other_fs +are those links whose target currently resides on a different filesystem +from where symlinks was run (most useful with +.B -r +). +.PP +.SH OPTIONS +.TP +.I -c +convert absolute links (within the same filesystem) to relative links. +This permits links to maintain their validity regardless of the mount +point used for the filesystem -- a desirable setup in most cases. +This option also causes any +.B messy +links to be cleaned up, and, if +.B -s +was also specified, then +.B lengthy +links are also shortened. +Links affected by +.B -c +are prefixed with +.B changed +in the output. +.TP +.I -d +causes +.B dangling +links to be removed. +.TP +.I -o +fix links on other filesystems encountered while recursing. +Normally, other filesystems encountered are not modified by symlinks. +.TP +.I -r +recursively operate on subdirectories within the same filesystem. +.TP +.I -s +causes +.B lengthy +links to be detected. +.TP +.I -t +is used to test for what +.B symlinks +would do if +.B -c +were specified, but without really changing anything. +.TP +.I -v +show all symbolic links. By default, +.B relative +links are not shown unless +.B -v +is specified. +.PP +.SH BUGS +.B symlinks +does not recurse or change links across filesystems. +.PP +.SH AUTHOR +.B symlinks +has been written by Mark Lord , the original developer and maintainer +of the IDE Performance Package for linux, the Linux IDE Driver subsystem, hdparm, and a current day libata hacker. +.SH SEE ALSO +.BR symlink (2) diff --git a/symlinks.c b/symlinks.c new file mode 100644 index 0000000..2b2452a --- /dev/null +++ b/symlinks.c @@ -0,0 +1,362 @@ +#include +#ifndef _POSIX_SOURCE +#define _POSIX_SOURCE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef S_ISLNK +#define S_ISLNK(mode) (((mode) & (_S_IFMT)) == (_S_IFLNK)) +#endif + +#ifndef PATH_MAX +#define PATH_MAX 1024 +#endif + +#define progver "%s: scan/change symbolic links - v1.3 - by Mark Lord\n\n" +static char *progname; +static int verbose = 0, fix_links = 0, recurse = 0, delete = 0, shorten = 0, + testing = 0, single_fs = 1; + +/* + * tidypath removes excess slashes and "." references from a path string + */ + +static int substr (char *s, char *old, char *new) +{ + char *tmp = NULL; + int oldlen = strlen(old), newlen = 0; + + if (NULL == strstr(s, old)) + return 0; + + if (new) + newlen = strlen(new); + + if (newlen > oldlen) { + if ((tmp = malloc(strlen(s))) == NULL) { + fprintf(stderr, "no memory\n"); + exit (1); + } + } + + while (NULL != (s = strstr(s, old))) { + char *p, *old_s = s; + + if (new) { + if (newlen > oldlen) + old_s = strcpy(tmp, s); + p = new; + while (*p) + *s++ = *p++; + } + p = old_s + oldlen; + while ((*s++ = *p++)); + } + if (tmp) + free(tmp); + return 1; +} + + +static int tidy_path (char *path) +{ + int tidied = 0; + char *s, *p; + + s = path + strlen(path) - 1; + if (s[0] != '/') { /* tmp trailing slash simplifies things */ + s[1] = '/'; + s[2] = '\0'; + } + while (substr(path, "/./", "/")) + tidied = 1; + while (substr(path, "//", "/")) + tidied = 1; + + while ((p = strstr(path,"/../")) != NULL) { + s = p+3; + for (p--; p != path; p--) if (*p == '/') break; + if (*p != '/') + break; + while ((*p++ = *s++)); + tidied = 1; + } + if (*path == '\0') + strcpy(path,"/"); + p = path + strlen(path) - 1; + if (p != path && *p == '/') + *p-- = '\0'; /* remove tmp trailing slash */ + while (p != path && *p == '/') { /* remove any others */ + *p-- = '\0'; + tidied = 1; + } + while (!strncmp(path,"./",2)) { + for (p = path, s = path+2; (*p++ = *s++);); + tidied = 1; + } + return tidied; +} + +static int shorten_path (char *path, char *abspath) +{ + static char dir[PATH_MAX]; + int shortened = 0; + char *p; + + /* get rid of unnecessary "../dir" sequences */ + while (abspath && strlen(abspath) > 1 && (p = strstr(path,"../"))) { + /* find innermost occurance of "../dir", and save "dir" */ + int slashes = 2; + char *a, *s, *d = dir; + while ((s = strstr(p+3, "../"))) { + ++slashes; + p = s; + } + s = p+3; + *d++ = '/'; + while (*s && *s != '/') + *d++ = *s++; + *d++ = '/'; + *d = '\0'; + if (!strcmp(dir,"//")) + break; + /* note: p still points at ../dir */ + if (*s != '/' || !*++s) + break; + a = abspath + strlen(abspath) - 1; + while (slashes-- > 0) { + if (a <= abspath) + goto ughh; + while (*--a != '/') { + if (a <= abspath) + goto ughh; + } + } + if (strncmp(dir, a, strlen(dir))) + break; + while ((*p++ = *s++)); /* delete the ../dir */ + shortened = 1; + } +ughh: + return shortened; +} + + +static void fix_symlink (char *path, dev_t my_dev) +{ + static char lpath[PATH_MAX], new[PATH_MAX], abspath[PATH_MAX]; + char *p, *np, *lp, *tail, *msg; + struct stat stbuf, lstbuf; + int c, fix_abs = 0, fix_messy = 0, fix_long = 0; + + if ((c = readlink(path, lpath, sizeof(lpath))) == -1) { + perror(path); + return; + } + lpath[c] = '\0'; /* readlink does not null terminate it */ + + /* construct the absolute address of the link */ + abspath[0] = '\0'; + if (lpath[0] != '/') { + strcat(abspath,path); + c = strlen(abspath); + if ((c > 0) && (abspath[c-1] == '/')) + abspath[c-1] = '\0'; /* cut trailing / */ + if ((p = strrchr(abspath,'/')) != NULL) + *p = '\0'; /* cut last component */ + strcat(abspath,"/"); + } + strcat(abspath,lpath); + (void) tidy_path(abspath); + + /* check for various things */ + if (stat(abspath, &stbuf) == -1) { + printf("dangling: %s -> %s\n", path, lpath); + if (delete) { + if (unlink (path)) { + perror(path); + } else + printf("deleted: %s -> %s\n", path, lpath); + } + return; + } + + if (single_fs) + lstat(abspath, &lstbuf); /* if the above didn't fail, then this shouldn't */ + + if (single_fs && lstbuf.st_dev != my_dev) { + msg = "other_fs:"; + } else if (lpath[0] == '/') { + msg = "absolute:"; + fix_abs = 1; + } else if (verbose) { + msg = "relative:"; + } else + msg = NULL; + fix_messy = tidy_path(strcpy(new,lpath)); + if (shorten) + fix_long = shorten_path(new, path); + if (!fix_abs) { + if (fix_messy) + msg = "messy: "; + else if (fix_long) + msg = "lengthy: "; + } + if (msg != NULL) + printf("%s %s -> %s\n", msg, path, lpath); + if (!(fix_links || testing) || !(fix_messy || fix_abs || fix_long)) + return; + + if (fix_abs) { + /* convert an absolute link to relative: */ + /* point tail at first part of lpath that differs from path */ + /* point p at first part of path that differs from lpath */ + (void) tidy_path(lpath); + tail = lp = lpath; + p = path; + while (*p && (*p == *lp)) { + if (*lp++ == '/') { + tail = lp; + while (*++p == '/'); + } + } + + /* now create new, with "../"s followed by tail */ + np = new; + while (*p) { + if (*p++ == '/') { + *np++ = '.'; + *np++ = '.'; + *np++ = '/'; + while (*p == '/') ++p; + } + } + strcpy (np, tail); + (void) tidy_path(new); + if (shorten) (void) shorten_path(new, path); + } + shorten_path(new,path); + if (!testing) { + if (unlink (path)) { + perror(path); + return; + } + if (symlink(new, path)) { + perror(path); + return; + } + } + printf("changed: %s -> %s\n", path, new); +} + +static void dirwalk (char *path, int pathlen, dev_t dev) +{ + char *name; + DIR *dfd; + static struct stat st; + static struct dirent *dp; + + if ((dfd = opendir(path)) == NULL) { + perror(path); + return; + } + + name = path + pathlen; + if (*(name-1) != '/') + *name++ = '/'; + + while ((dp = readdir(dfd)) != NULL ) { + strcpy(name, dp->d_name); + if (strcmp(name, ".") && strcmp(name,"..")) { + if (lstat(path, &st) == -1) { + perror(path); + } else if (st.st_dev == dev) { + if (S_ISLNK(st.st_mode)) { + fix_symlink (path, dev); + } else if (recurse && S_ISDIR(st.st_mode)) { + dirwalk(path, strlen(path), dev); + } + } + } + } + closedir(dfd); + path[pathlen] = '\0'; +} + +static void usage_error (void) +{ + fprintf(stderr, progver, progname); + fprintf(stderr, "Usage:\t%s [-cdorstv] dirlist\n\n", progname); + fprintf(stderr, "Flags:" + "\t-c == change absolute/messy links to relative\n" + "\t-d == delete dangling links\n" + "\t-o == warn about links across file systems\n" + "\t-r == recurse into subdirs\n" + "\t-s == shorten lengthy links (displayed in output only when -c not specified)\n" + "\t-t == show what would be done by -c\n" + "\t-v == verbose (show all symlinks)\n\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + static char path[PATH_MAX+2], cwd[PATH_MAX+2]; + int dircount = 0; + char c, *p; + + if ((progname = (char *) strrchr(*argv, '/')) == NULL) + progname = *argv; + else + progname++; + + if (NULL == getcwd(cwd,PATH_MAX)) { + fprintf(stderr,"getcwd() failed\n"); + exit (1); + } + if (!*cwd || cwd[strlen(cwd)-1] != '/') + strcat(cwd,"/"); + + while (--argc) { + p = *++argv; + if (*p == '-') { + if (*++p == '\0') + usage_error(); + while ((c = *p++)) { + if (c == 'c') fix_links = 1; + else if (c == 'd') delete = 1; + else if (c == 'o') single_fs = 0; + else if (c == 'r') recurse = 1; + else if (c == 's') shorten = 1; + else if (c == 't') testing = 1; + else if (c == 'v') verbose = 1; + else usage_error(); + } + } else { + struct stat st; + if (*p == '/') + *path = '\0'; + else + strcpy(path,cwd); + tidy_path(strcat(path, p)); + if (lstat(path, &st) == -1) + perror(path); + else + dirwalk(path, strlen(path), st.st_dev); + ++dircount; + } + } + if (dircount == 0) + usage_error(); + exit (0); +} diff --git a/symlinks.lsm b/symlinks.lsm new file mode 100644 index 0000000..796c856 --- /dev/null +++ b/symlinks.lsm @@ -0,0 +1,36 @@ +Begin2 +Title =symlinks - scan/change symbolic links +Version =1.4 +Desc1 =Scans directories for symbolic links, and identifies dangling, +Desc2 =relative, absolute, messy, and other_fs links. Can optionally +Desc3 =change absolute links to relative within a given filesystem. +Desc4 =Recommended for use by anyone developing and/or maintaining +Desc5 =a Linux FTP site or distribution or CD-ROM. +Author =Mark Lord +AuthorEmail =mlord@pobox.com +Maintainer =Mark Lord +MaintEmail =mlord@pobox.com +Site1 =tsx-11.mit.edu +Path1 =/pub/linux/sources/usr.bin +File1 =symlinks-1.4.tar.gz +FileSize1 =5KB +Required1 = +Required2 = +Required3 = +Required4 = +CopyPolicy1 =(c) Mark Lord, freely distributable +CopyPolicy2 = +Keywords =symbolic links cd cdrom CD-ROM distribution +Comment1 =v1.4 incorporate patches from Fedora +Comment1 =v1.3 more messy-link fixes, new -o flag for other_fs +Comment2 =v1.2 added -s flag to shorten links with redundant +Comment3 =path elements. Also includes code to remove excess +Comment4 =slashes from paths. +RelFiles1 = +RelFiles2 = +RelFiles3 = +Entered =09OCT2009 +EnteredBy =Mark Lord +CheckedEmail =mlord@pobox.com +End +