]>
git.ipfire.org Git - thirdparty/e2fsprogs.git/blob - util/symlinks.c
1 #define _FILE_OFFSET_BITS 64
2 #define _LARGEFILE_SOURCE
3 #define _LARGEFILE64_SOURCE
16 #include <sys/param.h>
17 #include <sys/types.h>
25 #define S_ISLNK(mode) (((mode) & (_S_IFMT)) == (_S_IFLNK))
32 #define progver "%s: scan/change symbolic links - v1.3 - by Mark Lord\n\n"
33 static char *progname
;
34 static int verbose
= 0, fix_links
= 0, recurse
= 0, delete = 0, shorten
= 0,
35 testing
= 0, single_fs
= 1;
38 * tidypath removes excess slashes and "." references from a path string
41 static int substr (char *s
, char *old
, char *new)
44 int oldlen
= strlen(old
), newlen
= 0;
46 if (NULL
== strstr(s
, old
))
52 if (newlen
> oldlen
) {
53 if ((tmp
= malloc(strlen(s
))) == NULL
) {
54 fprintf(stderr
, "no memory\n");
59 while (NULL
!= (s
= strstr(s
, old
))) {
64 old_s
= strcpy(tmp
, s
);
70 while ((*s
++ = *p
++));
78 static int tidy_path (char *path
)
83 s
= path
+ strlen(path
) - 1;
84 if (s
[0] != '/') { /* tmp trailing slash simplifies things */
88 while (substr(path
, "/./", "/"))
90 while (substr(path
, "//", "/"))
93 while ((p
= strstr(path
,"/../")) != NULL
) {
95 for (p
--; p
!= path
; p
--) if (*p
== '/') break;
98 while ((*p
++ = *s
++));
103 p
= path
+ strlen(path
) - 1;
104 if (p
!= path
&& *p
== '/')
105 *p
-- = '\0'; /* remove tmp trailing slash */
106 while (p
!= path
&& *p
== '/') { /* remove any others */
110 while (!strncmp(path
,"./",2)) {
111 for (p
= path
, s
= path
+2; (*p
++ = *s
++););
117 static int shorten_path (char *path
, char *abspath
)
119 static char dir
[PATH_MAX
];
123 /* get rid of unnecessary "../dir" sequences */
124 while (abspath
&& strlen(abspath
) > 1 && (p
= strstr(path
,"../"))) {
125 /* find innermost occurance of "../dir", and save "dir" */
127 char *a
, *s
, *d
= dir
;
128 while ((s
= strstr(p
+3, "../"))) {
134 while (*s
&& *s
!= '/')
138 if (!strcmp(dir
,"//"))
140 /* note: p still points at ../dir */
141 if (*s
!= '/' || !*++s
)
143 a
= abspath
+ strlen(abspath
) - 1;
144 while (slashes
-- > 0) {
147 while (*--a
!= '/') {
152 if (strncmp(dir
, a
, strlen(dir
)))
154 while ((*p
++ = *s
++)); /* delete the ../dir */
162 static void fix_symlink (char *path
, dev_t my_dev
)
164 static char lpath
[PATH_MAX
], new[PATH_MAX
], abspath
[PATH_MAX
];
165 char *p
, *np
, *lp
, *tail
, *msg
;
166 struct stat stbuf
, lstbuf
;
167 int c
, fix_abs
= 0, fix_messy
= 0, fix_long
= 0;
169 if ((c
= readlink(path
, lpath
, sizeof(lpath
) - 1)) == -1) {
173 lpath
[c
] = '\0'; /* readlink does not null terminate it */
175 /* construct the absolute address of the link */
177 if (lpath
[0] != '/') {
178 strcat(abspath
,path
);
180 if ((c
> 0) && (abspath
[c
-1] == '/'))
181 abspath
[c
-1] = '\0'; /* cut trailing / */
182 if ((p
= strrchr(abspath
,'/')) != NULL
)
183 *p
= '\0'; /* cut last component */
186 strcat(abspath
,lpath
);
187 (void) tidy_path(abspath
);
189 /* check for various things */
190 if (stat(abspath
, &stbuf
) == -1) {
191 printf("dangling: %s -> %s\n", path
, lpath
);
196 printf("deleted: %s -> %s\n", path
, lpath
);
202 lstat(abspath
, &lstbuf
); /* if the above didn't fail, then this shouldn't */
204 if (single_fs
&& lstbuf
.st_dev
!= my_dev
) {
206 } else if (lpath
[0] == '/') {
209 } else if (verbose
) {
213 fix_messy
= tidy_path(strcpy(new,lpath
));
215 fix_long
= shorten_path(new, path
);
223 printf("%s %s -> %s\n", msg
, path
, lpath
);
224 if (!(fix_links
|| testing
) || !(fix_messy
|| fix_abs
|| fix_long
))
228 /* convert an absolute link to relative: */
229 /* point tail at first part of lpath that differs from path */
230 /* point p at first part of path that differs from lpath */
231 (void) tidy_path(lpath
);
234 while (*p
&& (*p
== *lp
)) {
241 /* now create new, with "../"s followed by tail */
248 while (*p
== '/') ++p
;
252 (void) tidy_path(new);
253 if (shorten
) (void) shorten_path(new, path
);
255 shorten_path(new,path
);
261 if (symlink(new, path
)) {
266 printf("changed: %s -> %s\n", path
, new);
269 static void dirwalk (char *path
, int pathlen
, dev_t dev
)
273 static struct stat st
;
274 static struct dirent
*dp
;
276 if ((dfd
= opendir(path
)) == NULL
) {
281 name
= path
+ pathlen
;
282 if (*(name
-1) != '/')
285 while ((dp
= readdir(dfd
)) != NULL
) {
286 strcpy(name
, dp
->d_name
);
287 if (strcmp(name
, ".") && strcmp(name
,"..")) {
288 if (lstat(path
, &st
) == -1) {
290 } else if (st
.st_dev
== dev
) {
291 if (S_ISLNK(st
.st_mode
)) {
292 fix_symlink (path
, dev
);
293 } else if (recurse
&& S_ISDIR(st
.st_mode
)) {
294 dirwalk(path
, strlen(path
), dev
);
300 path
[pathlen
] = '\0';
303 static void usage_error (void)
305 fprintf(stderr
, progver
, progname
);
306 fprintf(stderr
, "Usage:\t%s [-cdorstv] LINK|DIR ...\n\n", progname
);
307 fprintf(stderr
, "Flags:"
308 "\t-c == change absolute/messy links to relative\n"
309 "\t-d == delete dangling links\n"
310 "\t-o == warn about links across file systems\n"
311 "\t-r == recurse into subdirs\n"
312 "\t-s == shorten lengthy links (displayed in output only when -c not specified)\n"
313 "\t-t == show what would be done by -c\n"
314 "\t-v == verbose (show all symlinks)\n\n");
318 int main(int argc
, char **argv
)
320 #if defined (_GNU_SOURCE) && defined (__GLIBC__)
321 static char path
[PATH_MAX
+2];
322 char* cwd
= get_current_dir_name();
324 static char path
[PATH_MAX
+2], cwd
[PATH_MAX
+2];
329 if ((progname
= (char *) strrchr(*argv
, '/')) == NULL
)
334 #if defined (_GNU_SOURCE) && defined (__GLIBC__)
336 fprintf(stderr
,"get_current_dir_name() failed\n");
338 if (NULL
== getcwd(cwd
,PATH_MAX
)) {
339 fprintf(stderr
,"getcwd() failed\n");
343 #if defined (_GNU_SOURCE) && defined (__GLIBC__)
344 cwd
= realloc(cwd
, strlen(cwd
)+2);
346 fprintf(stderr
, "realloc() failed\n");
350 if (!*cwd
|| cwd
[strlen(cwd
)-1] != '/')
359 if (c
== 'c') fix_links
= 1;
360 else if (c
== 'd') delete = 1;
361 else if (c
== 'o') single_fs
= 0;
362 else if (c
== 'r') recurse
= 1;
363 else if (c
== 's') shorten
= 1;
364 else if (c
== 't') testing
= 1;
365 else if (c
== 'v') verbose
= 1;
374 tidy_path(strcat(path
, p
));
375 if (lstat(path
, &st
) == -1)
377 else if (S_ISLNK(st
.st_mode
))
378 fix_symlink(path
, st
.st_dev
);
380 dirwalk(path
, strlen(path
), st
.st_dev
);