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).
28 #include <sys/types.h>
30 #include <sys/param.h>
34 #ifdef HAVE_LIBSELINUX
35 # include <selinux/selinux.h>
43 #include "closestream.h"
47 #define MAXSYMLINKS 256
50 #define NAMEI_NOLINKS (1 << 1)
51 #define NAMEI_MODES (1 << 2)
52 #define NAMEI_MNTS (1 << 3)
53 #define NAMEI_OWNERS (1 << 4)
54 #define NAMEI_VERTICAL (1 << 5)
55 #define NAMEI_CONTEXT (1 << 6)
59 struct stat st
; /* item lstat() */
60 char *name
; /* item name */
61 char *abslink
; /* absolute symlink path */
62 int relstart
; /* offset of relative path in 'abslink' */
63 struct namei
*next
; /* next item */
65 int mountpoint
; /* is mount point */
66 int noent
; /* this item not existing (stores errno from stat()) */
67 #ifdef HAVE_LIBSELINUX
68 int context_len
; /* length of selinux contexts, as returned by lgetfilecon(3) */
69 char *context
; /* selinux contexts, as set by lgetfilecon(3) */
74 static struct idcache
*gcache
; /* groupnames */
75 static struct idcache
*ucache
; /* usernames */
78 free_namei(struct namei
*nm
)
81 struct namei
*next
= nm
->next
;
82 #ifdef HAVE_LIBSELINUX
93 readlink_to_namei(struct namei
*nm
, const char *path
)
99 sz
= readlink(path
, sym
, sizeof(sym
));
101 err(EXIT_FAILURE
, _("failed to read symlink: %s"), path
);
103 char *p
= strrchr(path
, '/');
107 nm
->relstart
= p
- path
;
108 sz
+= nm
->relstart
+ 1;
111 nm
->abslink
= xmalloc(sz
+ 1);
114 /* create the absolute path from the relative symlink */
115 memcpy(nm
->abslink
, path
, nm
->relstart
);
116 *(nm
->abslink
+ nm
->relstart
) = '/';
118 memcpy(nm
->abslink
+ nm
->relstart
, sym
, sz
- nm
->relstart
);
120 /* - absolute link (foo -> /path/bar)
121 * - or link without any subdir (foo -> bar)
123 memcpy(nm
->abslink
, sym
, sz
);
125 nm
->abslink
[sz
] = '\0';
129 dotdot_stat(const char *dirname
, struct stat
*st
)
134 #define DOTDOTDIR "/.."
139 len
= strlen(dirname
);
140 path
= xmalloc(len
+ sizeof(DOTDOTDIR
));
142 memcpy(path
, dirname
, len
);
143 memcpy(path
+ len
, DOTDOTDIR
, sizeof(DOTDOTDIR
));
146 err(EXIT_FAILURE
, _("stat of %s failed"), path
);
151 static struct namei
*
152 new_namei(struct namei
*parent
, const char *path
, const char *fname
, int lev
)
158 nm
= xcalloc(1, sizeof(*nm
));
163 nm
->name
= xstrdup(fname
);
165 #ifdef HAVE_LIBSELINUX
166 /* Don't use is_selinux_enabled() here. We need info about a context
167 * also on systems where SELinux is (temporary) disabled */
168 nm
->context_len
= lgetfilecon(path
, &nm
->context
);
170 if (lstat(path
, &nm
->st
) != 0) {
175 if (S_ISLNK(nm
->st
.st_mode
))
176 readlink_to_namei(nm
, path
);
177 if (flags
& NAMEI_OWNERS
) {
178 add_uid(ucache
, nm
->st
.st_uid
);
179 add_gid(gcache
, nm
->st
.st_gid
);
182 if ((flags
& NAMEI_MNTS
) && S_ISDIR(nm
->st
.st_mode
)) {
183 struct stat stbuf
, *sb
= NULL
;
185 if (parent
&& S_ISDIR(parent
->st
.st_mode
))
187 else if (!parent
|| S_ISLNK(parent
->st
.st_mode
))
188 sb
= dotdot_stat(path
, &stbuf
);
190 if (sb
&& (sb
->st_dev
!= nm
->st
.st_dev
|| /* different device */
191 sb
->st_ino
== nm
->st
.st_ino
)) /* root directory */
198 static struct namei
*
199 add_namei(struct namei
*parent
, const char *orgpath
, int start
, struct namei
**last
)
201 struct namei
*nm
= NULL
, *first
= NULL
;
202 char *fname
, *end
, *path
;
209 level
= parent
->level
+ 1;
211 path
= xstrdup(orgpath
);
212 fname
= path
+ start
;
216 while (*fname
== '/')
217 fname
++; /* eat extra '/' */
218 first
= nm
= new_namei(nm
, "/", "/", level
);
221 for (end
= fname
; fname
&& end
; ) {
222 /* set end of filename */
224 end
= strchr(fname
, '/');
228 /* create a new entry */
229 nm
= new_namei(nm
, path
, fname
, level
);
234 /* set begin of the next filename */
238 end
++; /* eat extra '/' */
252 follow_symlinks(struct namei
*nm
)
256 for (; nm
; nm
= nm
->next
) {
257 struct namei
*next
, *last
;
261 if (!S_ISLNK(nm
->st
.st_mode
))
263 if (++symcount
> MAXSYMLINKS
) {
264 /* drop the rest of the list */
265 free_namei(nm
->next
);
270 nm
->next
= add_namei(nm
, nm
->abslink
, nm
->relstart
, &last
);
280 print_namei(struct namei
*nm
, char *path
)
285 printf("f: %s\n", path
);
287 for (; nm
; nm
= nm
->next
) {
292 if (flags
& NAMEI_MODES
)
294 if (flags
& NAMEI_OWNERS
)
295 blanks
+= ucache
->width
+ gcache
->width
+ 2;
296 if (!(flags
& NAMEI_VERTICAL
))
298 if (!(flags
& NAMEI_CONTEXT
))
300 blanks
+= nm
->level
* 2;
301 printf("%*s ", blanks
, "");
302 printf("%s - %s\n", nm
->name
, strerror(nm
->noent
));
306 xstrmode(nm
->st
.st_mode
, md
);
311 if (!(flags
& NAMEI_VERTICAL
)) {
312 for (i
= 0; i
< nm
->level
; i
++)
317 if (flags
& NAMEI_MODES
)
322 if (flags
& NAMEI_OWNERS
) {
323 printf(" %-*s", ucache
->width
,
324 get_id(ucache
, nm
->st
.st_uid
)->name
);
325 printf(" %-*s", gcache
->width
,
326 get_id(gcache
, nm
->st
.st_gid
)->name
);
328 #ifdef HAVE_LIBSELINUX
329 if (flags
& NAMEI_CONTEXT
) {
331 printf(" %-*s", nm
->context_len
, nm
->context
);
336 if (flags
& NAMEI_VERTICAL
)
337 for (i
= 0; i
< nm
->level
; i
++)
340 if (S_ISLNK(nm
->st
.st_mode
))
341 printf(" %s -> %s\n", nm
->name
,
342 nm
->abslink
+ nm
->relstart
);
344 printf(" %s\n", nm
->name
);
349 static void __attribute__((__noreturn__
)) usage(void)
351 const char *p
= program_invocation_short_name
;
357 fputs(USAGE_HEADER
, out
);
359 _(" %s [options] <pathname>...\n"), p
);
361 fputs(USAGE_SEPARATOR
, out
);
362 fputs(_("Follow a pathname until a terminal point is found.\n"), out
);
364 fputs(USAGE_OPTIONS
, out
);
366 " -x, --mountpoints show mount point directories with a 'D'\n"
367 " -m, --modes show the mode bits of each file\n"
368 " -o, --owners show owner and group name of each file\n"
369 " -l, --long use a long listing format (-m -o -v) \n"
370 " -n, --nosymlinks don't follow symlinks\n"
371 " -v, --vertical vertical align of modes and owners\n"), out
);
372 #ifdef HAVE_LIBSELINUX
373 fputs(_( " -Z, --context print any security context of each file \n"), out
);
376 fprintf(out
, USAGE_HELP_OPTIONS(21));
378 fprintf(out
, USAGE_MAN_TAIL("namei(1)"));
382 static const struct option longopts
[] =
384 { "help", no_argument
, NULL
, 'h' },
385 { "version", no_argument
, NULL
, 'V' },
386 { "mountpoints", no_argument
, NULL
, 'x' },
387 { "modes", no_argument
, NULL
, 'm' },
388 { "owners", no_argument
, NULL
, 'o' },
389 { "long", no_argument
, NULL
, 'l' },
390 { "nolinks", no_argument
, NULL
, 'n' },
391 { "vertical", no_argument
, NULL
, 'v' },
392 #ifdef HAVE_LIBSELINUX
393 { "context", no_argument
, NULL
, 'Z' },
395 { NULL
, 0, NULL
, 0 },
399 main(int argc
, char **argv
)
402 int rc
= EXIT_SUCCESS
;
403 static const char *shortopts
=
404 #ifdef HAVE_LIBSELINUX
409 setlocale(LC_ALL
, "");
410 bindtextdomain(PACKAGE
, LOCALEDIR
);
412 close_stdout_atexit();
414 while ((c
= getopt_long(argc
, argv
, shortopts
, longopts
, NULL
)) != -1) {
417 flags
|= (NAMEI_OWNERS
| NAMEI_MODES
| NAMEI_VERTICAL
);
420 flags
|= NAMEI_MODES
;
423 flags
|= NAMEI_NOLINKS
;
426 flags
|= NAMEI_OWNERS
;
432 flags
|= NAMEI_VERTICAL
;
434 #ifdef HAVE_LIBSELINUX
436 flags
|= NAMEI_CONTEXT
;
442 print_version(EXIT_SUCCESS
);
444 errtryhelp(EXIT_FAILURE
);
448 if (optind
== argc
) {
449 warnx(_("pathname argument is missing"));
450 errtryhelp(EXIT_FAILURE
);
453 ucache
= new_idcache();
455 err(EXIT_FAILURE
, _("failed to allocate UID cache"));
456 gcache
= new_idcache();
458 err(EXIT_FAILURE
, _("failed to allocate GID cache"));
460 for(; optind
< argc
; optind
++) {
461 char *path
= argv
[optind
];
462 struct namei
*nm
= NULL
;
465 if (stat(path
, &st
) != 0)
468 nm
= add_namei(NULL
, path
, 0, NULL
);
471 if (!(flags
& NAMEI_NOLINKS
))
472 sml
= follow_symlinks(nm
);
473 if (print_namei(nm
, path
)) {
480 warnx(_("%s: exceeded limit of symlinks"), path
);
486 free_idcache(ucache
);
487 free_idcache(gcache
);