]>
git.ipfire.org Git - thirdparty/e2fsprogs.git/blob - util/symlinks.c
1 #define _FILE_OFFSET_BITS 64
2 #ifndef _LARGEFILE_SOURCE
3 #define _LARGEFILE_SOURCE
5 #ifndef _LARGEFILE64_SOURCE
6 #define _LARGEFILE64_SOURCE
20 #include <sys/param.h>
21 #include <sys/types.h>
29 #define S_ISLNK(mode) (((mode) & (_S_IFMT)) == (_S_IFLNK))
36 #define progver "%s: scan/change symbolic links - v1.3 - by Mark Lord\n\n"
37 static char *progname
;
38 static int verbose
= 0, fix_links
= 0, recurse
= 0, delete = 0, shorten
= 0,
39 testing
= 0, single_fs
= 1;
42 * tidypath removes excess slashes and "." references from a path string
45 static int substr (char *s
, char *old
, char *new)
48 int oldlen
= strlen(old
), newlen
= 0;
50 if (NULL
== strstr(s
, old
))
56 if (newlen
> oldlen
) {
57 if ((tmp
= malloc(strlen(s
))) == NULL
) {
58 fprintf(stderr
, "no memory\n");
63 while (NULL
!= (s
= strstr(s
, old
))) {
68 old_s
= strcpy(tmp
, s
);
74 while ((*s
++ = *p
++));
82 static int tidy_path (char *path
)
87 s
= path
+ strlen(path
) - 1;
88 if (s
[0] != '/') { /* tmp trailing slash simplifies things */
92 while (substr(path
, "/./", "/"))
94 while (substr(path
, "//", "/"))
97 while ((p
= strstr(path
,"/../")) != NULL
) {
99 for (p
--; p
!= path
; p
--) if (*p
== '/') break;
102 while ((*p
++ = *s
++));
107 p
= path
+ strlen(path
) - 1;
108 if (p
!= path
&& *p
== '/')
109 *p
-- = '\0'; /* remove tmp trailing slash */
110 while (p
!= path
&& *p
== '/') { /* remove any others */
114 while (!strncmp(path
,"./",2)) {
115 for (p
= path
, s
= path
+2; (*p
++ = *s
++););
121 static int shorten_path (char *path
, char *abspath
)
123 static char dir
[PATH_MAX
];
127 /* get rid of unnecessary "../dir" sequences */
128 while (abspath
&& strlen(abspath
) > 1 && (p
= strstr(path
,"../"))) {
129 /* find innermost occurrence of "../dir", and save "dir" */
131 char *a
, *s
, *d
= dir
;
132 while ((s
= strstr(p
+3, "../"))) {
138 while (*s
&& *s
!= '/')
142 if (!strcmp(dir
,"//"))
144 /* note: p still points at ../dir */
145 if (*s
!= '/' || !*++s
)
147 a
= abspath
+ strlen(abspath
) - 1;
148 while (slashes
-- > 0) {
151 while (*--a
!= '/') {
156 if (strncmp(dir
, a
, strlen(dir
)))
158 while ((*p
++ = *s
++)); /* delete the ../dir */
166 static void fix_symlink (char *path
, dev_t my_dev
)
168 static char lpath
[PATH_MAX
], new[PATH_MAX
], abspath
[PATH_MAX
];
169 char *p
, *np
, *lp
, *tail
, *msg
;
170 struct stat stbuf
, lstbuf
;
171 int c
, fix_abs
= 0, fix_messy
= 0, fix_long
= 0;
173 if ((c
= readlink(path
, lpath
, sizeof(lpath
) - 1)) == -1) {
177 lpath
[c
] = '\0'; /* readlink does not null terminate it */
179 /* construct the absolute address of the link */
181 if (lpath
[0] != '/') {
182 strcat(abspath
,path
);
184 if ((c
> 0) && (abspath
[c
-1] == '/'))
185 abspath
[c
-1] = '\0'; /* cut trailing / */
186 if ((p
= strrchr(abspath
,'/')) != NULL
)
187 *p
= '\0'; /* cut last component */
190 strcat(abspath
,lpath
);
191 (void) tidy_path(abspath
);
193 /* check for various things */
194 if (stat(abspath
, &stbuf
) == -1) {
195 printf("dangling: %s -> %s\n", path
, lpath
);
200 printf("deleted: %s -> %s\n", path
, lpath
);
206 lstat(abspath
, &lstbuf
); /* if the above didn't fail, then this shouldn't */
208 if (single_fs
&& lstbuf
.st_dev
!= my_dev
) {
210 } else if (lpath
[0] == '/') {
213 } else if (verbose
) {
217 fix_messy
= tidy_path(strcpy(new,lpath
));
219 fix_long
= shorten_path(new, path
);
227 printf("%s %s -> %s\n", msg
, path
, lpath
);
228 if (!(fix_links
|| testing
) || !(fix_messy
|| fix_abs
|| fix_long
))
232 /* convert an absolute link to relative: */
233 /* point tail at first part of lpath that differs from path */
234 /* point p at first part of path that differs from lpath */
235 (void) tidy_path(lpath
);
238 while (*p
&& (*p
== *lp
)) {
245 /* now create new, with "../"s followed by tail */
252 while (*p
== '/') ++p
;
256 (void) tidy_path(new);
257 if (shorten
) (void) shorten_path(new, path
);
259 shorten_path(new,path
);
265 if (symlink(new, path
)) {
270 printf("changed: %s -> %s\n", path
, new);
273 static void dirwalk (char *path
, int pathlen
, dev_t dev
)
277 static struct stat st
;
278 static struct dirent
*dp
;
280 if ((dfd
= opendir(path
)) == NULL
) {
285 name
= path
+ pathlen
;
286 if (*(name
-1) != '/')
289 while ((dp
= readdir(dfd
)) != NULL
) {
290 strcpy(name
, dp
->d_name
);
291 if (strcmp(name
, ".") && strcmp(name
,"..")) {
292 if (lstat(path
, &st
) == -1) {
294 } else if (st
.st_dev
== dev
) {
295 if (S_ISLNK(st
.st_mode
)) {
296 fix_symlink (path
, dev
);
297 } else if (recurse
&& S_ISDIR(st
.st_mode
)) {
298 dirwalk(path
, strlen(path
), dev
);
304 path
[pathlen
] = '\0';
307 static void usage_error (void)
309 fprintf(stderr
, progver
, progname
);
310 fprintf(stderr
, "Usage:\t%s [-cdorstv] LINK|DIR ...\n\n", progname
);
311 fprintf(stderr
, "Flags:"
312 "\t-c == change absolute/messy links to relative\n"
313 "\t-d == delete dangling links\n"
314 "\t-o == warn about links across file systems\n"
315 "\t-r == recurse into subdirs\n"
316 "\t-s == shorten lengthy links (displayed in output only when -c not specified)\n"
317 "\t-t == show what would be done by -c\n"
318 "\t-v == verbose (show all symlinks)\n\n");
322 int main(int argc
, char **argv
)
324 #if defined (_GNU_SOURCE) && defined (__GLIBC__)
325 static char path
[PATH_MAX
+2];
326 char* cwd
= get_current_dir_name();
328 static char path
[PATH_MAX
+2], cwd
[PATH_MAX
+2];
333 if ((progname
= (char *) strrchr(*argv
, '/')) == NULL
)
338 #if defined (_GNU_SOURCE) && defined (__GLIBC__)
340 fprintf(stderr
,"get_current_dir_name() failed\n");
342 if (NULL
== getcwd(cwd
,PATH_MAX
)) {
343 fprintf(stderr
,"getcwd() failed\n");
347 #if defined (_GNU_SOURCE) && defined (__GLIBC__)
348 cwd
= realloc(cwd
, strlen(cwd
)+2);
350 fprintf(stderr
, "realloc() failed\n");
354 if (!*cwd
|| cwd
[strlen(cwd
)-1] != '/')
363 if (c
== 'c') fix_links
= 1;
364 else if (c
== 'd') delete = 1;
365 else if (c
== 'o') single_fs
= 0;
366 else if (c
== 'r') recurse
= 1;
367 else if (c
== 's') shorten
= 1;
368 else if (c
== 't') testing
= 1;
369 else if (c
== 'v') verbose
= 1;
378 tidy_path(strcat(path
, p
));
379 if (lstat(path
, &st
) == -1)
381 else if (S_ISLNK(st
.st_mode
))
382 fix_symlink(path
, st
.st_dev
);
384 dirwalk(path
, strlen(path
), st
.st_dev
);