2 * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
4 * This file is part of util-linux.
6 * This file is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This file is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * The original namei(1) was written by:
17 * Roger S. Southwick (May 2, 1990)
18 * Steve Tell (March 28, 1991)
19 * Arkadiusz MiĆkiewicz (1999-02-22)
20 * Li Zefan (2007-09-10).
29 #include <sys/types.h>
31 #include <sys/param.h>
40 #include "closestream.h"
44 #define MAXSYMLINKS 256
47 #define NAMEI_NOLINKS (1 << 1)
48 #define NAMEI_MODES (1 << 2)
49 #define NAMEI_MNTS (1 << 3)
50 #define NAMEI_OWNERS (1 << 4)
51 #define NAMEI_VERTICAL (1 << 5)
55 struct stat st
; /* item lstat() */
56 char *name
; /* item name */
57 char *abslink
; /* absolute symlink path */
58 int relstart
; /* offset of relative path in 'abslink' */
59 struct namei
*next
; /* next item */
61 int mountpoint
; /* is mount point */
62 int noent
; /* is this item not existing */
66 static struct idcache
*gcache
; /* groupnames */
67 static struct idcache
*ucache
; /* usernames */
70 free_namei(struct namei
*nm
)
73 struct namei
*next
= nm
->next
;
82 readlink_to_namei(struct namei
*nm
, const char *path
)
88 sz
= readlink(path
, sym
, sizeof(sym
));
90 err(EXIT_FAILURE
, _("failed to read symlink: %s"), path
);
92 char *p
= strrchr(path
, '/');
96 nm
->relstart
= p
- path
;
97 sz
+= nm
->relstart
+ 1;
100 nm
->abslink
= xmalloc(sz
+ 1);
102 if (*sym
!= '/' && isrel
) {
103 /* create the absolute path from the relative symlink */
104 memcpy(nm
->abslink
, path
, nm
->relstart
);
105 *(nm
->abslink
+ nm
->relstart
) = '/';
107 memcpy(nm
->abslink
+ nm
->relstart
, sym
, sz
- nm
->relstart
);
109 /* - absolute link (foo -> /path/bar)
110 * - or link without any subdir (foo -> bar)
112 memcpy(nm
->abslink
, sym
, sz
);
114 nm
->abslink
[sz
] = '\0';
118 dotdot_stat(const char *dirname
, struct stat
*st
)
123 #define DOTDOTDIR "/.."
128 len
= strlen(dirname
);
129 path
= xmalloc(len
+ sizeof(DOTDOTDIR
));
131 memcpy(path
, dirname
, len
);
132 memcpy(path
+ len
, DOTDOTDIR
, sizeof(DOTDOTDIR
));
135 err(EXIT_FAILURE
, _("stat of %s failed"), path
);
140 static struct namei
*
141 new_namei(struct namei
*parent
, const char *path
, const char *fname
, int lev
)
147 nm
= xcalloc(1, sizeof(*nm
));
152 nm
->name
= xstrdup(fname
);
154 nm
->noent
= (lstat(path
, &nm
->st
) == -1);
158 if (S_ISLNK(nm
->st
.st_mode
))
159 readlink_to_namei(nm
, path
);
160 if (flags
& NAMEI_OWNERS
) {
161 add_uid(ucache
, nm
->st
.st_uid
);
162 add_gid(gcache
, nm
->st
.st_gid
);
165 if ((flags
& NAMEI_MNTS
) && S_ISDIR(nm
->st
.st_mode
)) {
166 struct stat stbuf
, *sb
= NULL
;
168 if (parent
&& S_ISDIR(parent
->st
.st_mode
))
170 else if (!parent
|| S_ISLNK(parent
->st
.st_mode
))
171 sb
= dotdot_stat(path
, &stbuf
);
173 if (sb
&& (sb
->st_dev
!= nm
->st
.st_dev
|| /* different device */
174 sb
->st_ino
== nm
->st
.st_ino
)) /* root directory */
181 static struct namei
*
182 add_namei(struct namei
*parent
, const char *orgpath
, int start
, struct namei
**last
)
184 struct namei
*nm
= NULL
, *first
= NULL
;
185 char *fname
, *end
, *path
;
192 level
= parent
->level
+ 1;
194 path
= xstrdup(orgpath
);
195 fname
= path
+ start
;
199 while (*fname
== '/')
200 fname
++; /* eat extra '/' */
201 first
= nm
= new_namei(nm
, "/", "/", level
);
204 for (end
= fname
; fname
&& end
; ) {
205 /* set end of filename */
207 end
= strchr(fname
, '/');
211 /* create a new entry */
212 nm
= new_namei(nm
, path
, fname
, level
);
217 /* set begin of the next filename */
221 end
++; /* eat extra '/' */
235 follow_symlinks(struct namei
*nm
)
239 for (; nm
; nm
= nm
->next
) {
240 struct namei
*next
, *last
;
244 if (!S_ISLNK(nm
->st
.st_mode
))
246 if (++symcount
> MAXSYMLINKS
) {
247 /* drop the rest of the list */
248 free_namei(nm
->next
);
253 nm
->next
= add_namei(nm
, nm
->abslink
, nm
->relstart
, &last
);
263 print_namei(struct namei
*nm
, char *path
)
268 printf("f: %s\n", path
);
270 for (; nm
; nm
= nm
->next
) {
275 if (flags
& NAMEI_MODES
)
277 if (flags
& NAMEI_OWNERS
)
278 blanks
+= ucache
->width
+ gcache
->width
+ 2;
279 if (!(flags
& NAMEI_VERTICAL
))
281 blanks
+= nm
->level
* 2;
282 printf("%*s ", blanks
, "");
283 printf(_("%s - No such file or directory\n"), nm
->name
);
287 xstrmode(nm
->st
.st_mode
, md
);
292 if (!(flags
& NAMEI_VERTICAL
)) {
293 for (i
= 0; i
< nm
->level
; i
++)
298 if (flags
& NAMEI_MODES
)
303 if (flags
& NAMEI_OWNERS
) {
304 printf(" %-*s", ucache
->width
,
305 get_id(ucache
, nm
->st
.st_uid
)->name
);
306 printf(" %-*s", gcache
->width
,
307 get_id(gcache
, nm
->st
.st_gid
)->name
);
310 if (flags
& NAMEI_VERTICAL
)
311 for (i
= 0; i
< nm
->level
; i
++)
314 if (S_ISLNK(nm
->st
.st_mode
))
315 printf(" %s -> %s\n", nm
->name
,
316 nm
->abslink
+ nm
->relstart
);
318 printf(" %s\n", nm
->name
);
323 static void usage(int rc
)
325 const char *p
= program_invocation_short_name
;
326 FILE *out
= rc
== EXIT_FAILURE
? stderr
: stdout
;
331 fputs(USAGE_HEADER
, out
);
333 _(" %s [options] <pathname>...\n"), p
);
335 fputs(USAGE_SEPARATOR
, out
);
336 fputs(_("Follow a pathname until a terminal point is found.\n"), out
);
338 fputs(USAGE_OPTIONS
, out
);
339 fputs(_(" -h, --help displays this help text\n"
340 " -V, --version output version information and exit\n"
341 " -x, --mountpoints show mount point directories with a 'D'\n"
342 " -m, --modes show the mode bits of each file\n"
343 " -o, --owners show owner and group name of each file\n"
344 " -l, --long use a long listing format (-m -o -v) \n"
345 " -n, --nosymlinks don't follow symlinks\n"
346 " -v, --vertical vertical align of modes and owners\n"), out
);
348 fprintf(out
, USAGE_MAN_TAIL("namei(1)"));
352 static const struct option longopts
[] =
354 { "help", no_argument
, NULL
, 'h' },
355 { "version", no_argument
, NULL
, 'V' },
356 { "mountpoints", no_argument
, NULL
, 'x' },
357 { "modes", no_argument
, NULL
, 'm' },
358 { "owners", no_argument
, NULL
, 'o' },
359 { "long", no_argument
, NULL
, 'l' },
360 { "nolinks", no_argument
, NULL
, 'n' },
361 { "vertical", no_argument
, NULL
, 'v' },
362 { NULL
, 0, NULL
, 0 },
366 main(int argc
, char **argv
)
369 int rc
= EXIT_SUCCESS
;
371 setlocale(LC_ALL
, "");
372 bindtextdomain(PACKAGE
, LOCALEDIR
);
374 atexit(close_stdout
);
376 while ((c
= getopt_long(argc
, argv
, "hVlmnovx", longopts
, NULL
)) != -1) {
382 printf(UTIL_LINUX_VERSION
);
385 flags
|= (NAMEI_OWNERS
| NAMEI_MODES
| NAMEI_VERTICAL
);
388 flags
|= NAMEI_MODES
;
391 flags
|= NAMEI_NOLINKS
;
394 flags
|= NAMEI_OWNERS
;
400 flags
|= NAMEI_VERTICAL
;
403 errtryhelp(EXIT_FAILURE
);
407 if (optind
== argc
) {
408 warnx(_("pathname argument is missing"));
412 ucache
= new_idcache();
414 err(EXIT_FAILURE
, _("failed to allocate UID cache"));
415 gcache
= new_idcache();
417 err(EXIT_FAILURE
, _("failed to allocate GID cache"));
419 for(; optind
< argc
; optind
++) {
420 char *path
= argv
[optind
];
421 struct namei
*nm
= NULL
;
424 if (stat(path
, &st
) != 0)
427 nm
= add_namei(NULL
, path
, 0, NULL
);
430 if (!(flags
& NAMEI_NOLINKS
))
431 sml
= follow_symlinks(nm
);
432 if (print_namei(nm
, path
)) {
439 warnx(_("%s: exceeded limit of symlinks"), path
);
445 free_idcache(ucache
);
446 free_idcache(gcache
);