Browse Source

Initial commit.

devel
J. Brandt Buckley 13 years ago
commit
57604cfcb2
  1. 15
      Makefile
  2. 123
      symlinks.8
  3. 362
      symlinks.c
  4. 36
      symlinks.lsm

15
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

123
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 <mlord@pobox.com>, 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)

362
symlinks.c

@ -0,0 +1,362 @@
#include <unistd.h>
#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
#endif
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include <fcntl.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <time.h>
#include <stddef.h>
#include <errno.h>
#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);
}

36
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
Loading…
Cancel
Save