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 writtent by:
17 * Roger S. Southwick (May 2, 1990)
18 * Steve Tell (March 28, 1991)
19 * Arkadiusz Mikiewicz (1999-02-22)
20 * Li Zefan (2007-09-10).
30 #include <sys/types.h>
32 #include <sys/param.h>
41 #include "closestream.h"
44 #define MAXSYMLINKS 256
47 #ifndef LOGIN_NAME_MAX
48 #define LOGIN_NAME_MAX 256
51 #define NAMEI_NOLINKS (1 << 1)
52 #define NAMEI_MODES (1 << 2)
53 #define NAMEI_MNTS (1 << 3)
54 #define NAMEI_OWNERS (1 << 4)
55 #define NAMEI_VERTICAL (1 << 5)
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
; /* is this item not existing */
76 static int uwidth
; /* maximal width of username */
77 static int gwidth
; /* maximal width of groupname */
78 static struct idcache
*gcache
; /* groupnames */
79 static struct idcache
*ucache
; /* usernames */
81 static struct idcache
*
82 get_id(struct idcache
*ic
, unsigned long int id
)
93 free_idcache(struct idcache
*ic
)
96 struct idcache
*next
= ic
->next
;
104 add_id(struct idcache
**ic
, char *name
, unsigned long int id
, int *width
)
106 struct idcache
*nc
, *x
;
109 nc
= xcalloc(1, sizeof(*nc
));
114 wchar_t wc
[LOGIN_NAME_MAX
+ 1];
116 if (mbstowcs(wc
, name
, LOGIN_NAME_MAX
) > 0) {
117 wc
[LOGIN_NAME_MAX
] = '\0';
118 w
= wcswidth(wc
, LOGIN_NAME_MAX
);
124 /* note, we ignore names with non-printable widechars */
126 nc
->name
= xstrdup(name
);
127 else if (xasprintf(&nc
->name
, "%lu", id
) == -1)
130 for (x
= *ic
; x
&& x
->next
; x
= x
->next
);
132 /* add 'nc' at end of the 'ic' list */
138 w
= nc
->name
? strlen(nc
->name
) : 0;
140 *width
= *width
< w
? w
: *width
;
145 add_uid(unsigned long int id
)
147 struct idcache
*ic
= get_id(ucache
, id
);
150 struct passwd
*pw
= getpwuid((uid_t
) id
);
151 add_id(&ucache
, pw
? pw
->pw_name
: NULL
, id
, &uwidth
);
156 add_gid(unsigned long int id
)
158 struct idcache
*ic
= get_id(gcache
, id
);
161 struct group
*gr
= getgrgid((gid_t
) id
);
162 add_id(&gcache
, gr
? gr
->gr_name
: NULL
, id
, &gwidth
);
167 free_namei(struct namei
*nm
)
170 struct namei
*next
= nm
->next
;
179 readlink_to_namei(struct namei
*nm
, const char *path
)
185 sz
= readlink(path
, sym
, sizeof(sym
));
187 err(EXIT_FAILURE
, _("failed to read symlink: %s"), path
);
189 char *p
= strrchr(path
, '/');
193 nm
->relstart
= p
? p
- path
: 0;
194 sz
+= nm
->relstart
+ 1;
197 nm
->abslink
= xmalloc(sz
+ 1);
199 if (*sym
!= '/' && isrel
) {
200 /* create the absolute path from the relative symlink */
201 memcpy(nm
->abslink
, path
, nm
->relstart
);
202 *(nm
->abslink
+ nm
->relstart
) = '/';
204 memcpy(nm
->abslink
+ nm
->relstart
, sym
, sz
- nm
->relstart
);
206 /* - absolute link (foo -> /path/bar)
207 * - or link without any subdir (foo -> bar)
209 memcpy(nm
->abslink
, sym
, sz
);
211 nm
->abslink
[sz
] = '\0';
215 dotdot_stat(const char *dirname
, struct stat
*st
)
220 #define DOTDOTDIR "/.."
225 len
= strlen(dirname
);
226 path
= xmalloc(len
+ sizeof(DOTDOTDIR
));
228 memcpy(path
, dirname
, len
);
229 memcpy(path
+ len
, DOTDOTDIR
, sizeof(DOTDOTDIR
));
232 err(EXIT_FAILURE
, _("could not stat '%s'"), path
);
237 static struct namei
*
238 new_namei(struct namei
*parent
, const char *path
, const char *fname
, int lev
)
244 nm
= xcalloc(1, sizeof(*nm
));
249 nm
->name
= xstrdup(fname
);
251 nm
->noent
= (lstat(path
, &nm
->st
) == -1);
255 if (S_ISLNK(nm
->st
.st_mode
))
256 readlink_to_namei(nm
, path
);
257 if (flags
& NAMEI_OWNERS
) {
258 add_uid(nm
->st
.st_uid
);
259 add_gid(nm
->st
.st_gid
);
262 if ((flags
& NAMEI_MNTS
) && S_ISDIR(nm
->st
.st_mode
)) {
263 struct stat stbuf
, *sb
= NULL
;
265 if (parent
&& S_ISDIR(parent
->st
.st_mode
))
267 else if (!parent
|| S_ISLNK(parent
->st
.st_mode
))
268 sb
= dotdot_stat(path
, &stbuf
);
270 if (sb
&& (sb
->st_dev
!= nm
->st
.st_dev
|| /* different device */
271 sb
->st_ino
== nm
->st
.st_ino
)) /* root directory */
278 static struct namei
*
279 add_namei(struct namei
*parent
, const char *orgpath
, int start
, struct namei
**last
)
281 struct namei
*nm
= NULL
, *first
= NULL
;
282 char *fname
, *end
, *path
;
289 level
= parent
->level
+ 1;
291 path
= xstrdup(orgpath
);
292 fname
= path
+ start
;
296 while (*fname
== '/')
297 fname
++; /* eat extra '/' */
298 first
= nm
= new_namei(nm
, "/", "/", level
);
301 for (end
= fname
; fname
&& end
; ) {
302 /* set end of filename */
304 end
= strchr(fname
, '/');
308 /* create a new entry */
309 nm
= new_namei(nm
, path
, fname
, level
);
314 /* set begin of the next filename */
318 end
++; /* eat extra '/' */
332 follow_symlinks(struct namei
*nm
)
336 for (; nm
; nm
= nm
->next
) {
337 struct namei
*next
, *last
;
341 if (!S_ISLNK(nm
->st
.st_mode
))
343 if (++symcount
> MAXSYMLINKS
) {
344 /* drop the rest of the list */
345 free_namei(nm
->next
);
350 nm
->next
= add_namei(nm
, nm
->abslink
, nm
->relstart
, &last
);
360 print_namei(struct namei
*nm
, char *path
)
365 printf("f: %s\n", path
);
367 for (; nm
; nm
= nm
->next
) {
371 printf(_("%s - No such file or directory\n"), nm
->name
);
375 strmode(nm
->st
.st_mode
, md
);
380 if (!(flags
& NAMEI_VERTICAL
)) {
381 for (i
= 0; i
< nm
->level
; i
++)
386 if (flags
& NAMEI_MODES
)
391 if (flags
& NAMEI_OWNERS
) {
392 printf(" %-*s", uwidth
,
393 get_id(ucache
, nm
->st
.st_uid
)->name
);
394 printf(" %-*s", gwidth
,
395 get_id(gcache
, nm
->st
.st_gid
)->name
);
398 if (flags
& NAMEI_VERTICAL
)
399 for (i
= 0; i
< nm
->level
; i
++)
402 if (S_ISLNK(nm
->st
.st_mode
))
403 printf(" %s -> %s\n", nm
->name
,
404 nm
->abslink
+ nm
->relstart
);
406 printf(" %s\n", nm
->name
);
411 static void usage(int rc
)
413 const char *p
= program_invocation_short_name
;
414 FILE *out
= rc
== EXIT_FAILURE
? stderr
: stdout
;
419 fputs(_("\nUsage:\n"), out
);
421 _(" %s [options] pathname [pathname ...]\n"), p
);
423 fputs(_("\nOptions:\n"), out
);
424 fputs(_(" -h, --help displays this help text\n"
425 " -V, --version output version information and exit\n"
426 " -x, --mountpoints show mount point directories with a 'D'\n"
427 " -m, --modes show the mode bits of each file\n"
428 " -o, --owners show owner and group name of each file\n"
429 " -l, --long use a long listing format (-m -o -v) \n"
430 " -n, --nosymlinks don't follow symlinks\n"
431 " -v, --vertical vertical align of modes and owners\n"), out
);
433 fputs(_("\nFor more information see namei(1).\n"), out
);
437 static const struct option longopts
[] =
439 { "help", 0, 0, 'h' },
440 { "version", 0, 0, 'V' },
441 { "mountpoints",0, 0, 'x' },
442 { "modes", 0, 0, 'm' },
443 { "owners", 0, 0, 'o' },
444 { "long", 0, 0, 'l' },
445 { "nolinks", 0, 0, 'n' },
446 { "vertical", 0, 0, 'v' },
451 main(int argc
, char **argv
)
454 int rc
= EXIT_SUCCESS
;
456 setlocale(LC_ALL
, "");
457 bindtextdomain(PACKAGE
, LOCALEDIR
);
459 atexit(close_stdout
);
461 while ((c
= getopt_long(argc
, argv
, "hVlmnovx", longopts
, NULL
)) != -1) {
467 printf(_("%s from %s\n"), program_invocation_short_name
,
471 flags
|= (NAMEI_OWNERS
| NAMEI_MODES
| NAMEI_VERTICAL
);
474 flags
|= NAMEI_MODES
;
477 flags
|= NAMEI_NOLINKS
;
480 flags
|= NAMEI_OWNERS
;
486 flags
|= NAMEI_VERTICAL
;
493 if (optind
== argc
) {
494 warnx(_("pathname argument is missing"));
498 for(; optind
< argc
; optind
++) {
499 char *path
= argv
[optind
];
500 struct namei
*nm
= NULL
;
503 if (stat(path
, &st
) != 0)
506 nm
= add_namei(NULL
, path
, 0, NULL
);
509 if (!(flags
& NAMEI_NOLINKS
))
510 sml
= follow_symlinks(nm
);
511 if (print_namei(nm
, path
)) {
518 warnx(_("%s: exceeded limit of symlinks"), path
);
524 free_idcache(ucache
);
525 free_idcache(gcache
);