2 * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
4 * This file is part of util-linux-ng.
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).
29 #include <sys/types.h>
31 #include <sys/param.h>
39 #define MAXSYMLINKS 256
46 #ifndef LOGIN_NAME_MAX
47 #define LOGIN_NAME_MAX 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)
58 struct stat st
; /* item lstat() */
59 char *name
; /* item name */
60 char *abslink
; /* absolute symlink path */
61 int relstart
; /* offset of relative path in 'abslink' */
62 struct namei
*next
; /* next item */
73 static int uwidth
; /* maximal width of username */
74 static int gwidth
; /* maximal width of groupname */
75 static struct idcache
*gcache
; /* groupnames */
76 static struct idcache
*ucache
; /* usernames */
78 static struct idcache
*
79 get_id(struct idcache
*ic
, unsigned long int id
)
90 free_idcache(struct idcache
*ic
)
93 struct idcache
*next
= ic
->next
;
101 add_id(struct idcache
**ic
, char *name
, unsigned long int id
, int *width
)
103 struct idcache
*nc
, *x
;
106 nc
= calloc(1, sizeof(*nc
));
113 wchar_t wc
[LOGIN_NAME_MAX
+ 1];
115 if (mbstowcs(wc
, name
, LOGIN_NAME_MAX
) > 0) {
116 wc
[LOGIN_NAME_MAX
] = '\0';
117 w
= wcswidth(wc
, LOGIN_NAME_MAX
);
123 /* note, we ignore names with non-printable widechars */
125 nc
->name
= strdup(name
);
126 else if (asprintf(&nc
->name
, "%lu", id
) == -1)
131 for (x
= *ic
; x
&& x
->next
; x
= x
->next
);
133 /* add 'nc' at end of the 'ic' list */
139 w
= strlen(nc
->name
);
140 *width
= *width
< w
? w
: *width
;
144 err(EXIT_FAILURE
, _("out of memory?"));
148 add_uid(unsigned long int id
)
150 struct idcache
*ic
= get_id(ucache
, id
);
153 struct passwd
*pw
= getpwuid((uid_t
) id
);
154 add_id(&ucache
, pw
? pw
->pw_name
: NULL
, id
, &uwidth
);
159 add_gid(unsigned long int id
)
161 struct idcache
*ic
= get_id(gcache
, id
);
164 struct group
*gr
= getgrgid((gid_t
) id
);
165 add_id(&gcache
, gr
? gr
->gr_name
: NULL
, id
, &gwidth
);
170 free_namei(struct namei
*nm
)
173 struct namei
*next
= nm
->next
;
182 readlink_to_namei(struct namei
*nm
, const char *path
)
187 sz
= readlink(path
, sym
, sizeof(sym
));
189 err(EXIT_FAILURE
, _("failed to read symlink: %s"), path
);
191 char *p
= strrchr(path
, '/');
193 nm
->relstart
= p
? p
- path
: 0;
195 sz
+= nm
->relstart
+ 1;
197 nm
->abslink
= malloc(sz
+ 1);
199 err(EXIT_FAILURE
, _("out of memory?"));
201 if (*sym
!= '/' && nm
->relstart
) {
202 /* create the absolute path from the relative symlink */
203 memcpy(nm
->abslink
, path
, nm
->relstart
);
204 *(nm
->abslink
+ nm
->relstart
) = '/';
206 memcpy(nm
->abslink
+ nm
->relstart
, sym
, sz
- nm
->relstart
);
208 memcpy(nm
->abslink
, sym
, sz
);
209 nm
->abslink
[sz
] = '\0';
212 static struct namei
*
213 new_namei(struct namei
*parent
, const char *path
, const char *fname
, int lev
)
219 nm
= calloc(1, sizeof(*nm
));
221 err(EXIT_FAILURE
, _("out of memory?"));
226 nm
->name
= strdup(fname
);
228 err(EXIT_FAILURE
, _("out of memory?"));
229 if (lstat(path
, &nm
->st
) == -1)
230 err(EXIT_FAILURE
, _("could not stat '%s'"), path
);
232 if (S_ISLNK(nm
->st
.st_mode
))
233 readlink_to_namei(nm
, path
);
234 if (flags
& NAMEI_OWNERS
) {
235 add_uid(nm
->st
.st_uid
);
236 add_gid(nm
->st
.st_gid
);
242 static struct namei
*
243 add_namei(struct namei
*parent
, const char *orgpath
, int start
, struct namei
**last
)
245 struct namei
*nm
= NULL
, *first
= NULL
;
246 char *fname
, *end
, *path
;
253 level
= parent
->level
+ 1;
255 path
= strdup(orgpath
);
257 err(EXIT_FAILURE
, _("out of memory?"));
258 fname
= path
+ start
;
262 while (*fname
== '/')
263 fname
++; /* eat extra '/' */
264 first
= nm
= new_namei(nm
, "/", "/", level
);
267 for (end
= fname
; fname
&& end
; ) {
268 /* set end of filename */
270 end
= strchr(fname
, '/');
274 /* create a new entry */
275 nm
= new_namei(nm
, path
, fname
, level
);
280 /* set begin of the next filename */
284 end
++; /* eat extra '/' */
296 follow_symlinks(struct namei
*nm
)
300 for (; nm
; nm
= nm
->next
) {
301 struct namei
*next
, *last
;
303 if (!S_ISLNK(nm
->st
.st_mode
))
305 if (++symcount
> MAXSYMLINKS
) {
306 /* drop the rest of the list */
307 free_namei(nm
->next
);
312 nm
->next
= add_namei(nm
, nm
->abslink
, nm
->relstart
, &last
);
322 strmode(mode_t mode
, char *str
)
326 else if (S_ISLNK(mode
))
328 else if (S_ISCHR(mode
))
330 else if (S_ISBLK(mode
))
332 else if (S_ISSOCK(mode
))
334 else if (S_ISFIFO(mode
))
336 else if (S_ISREG(mode
))
339 str
[1] = mode
& S_IRUSR
? 'r' : '-';
340 str
[2] = mode
& S_IWUSR
? 'w' : '-';
341 str
[3] = (mode
& S_ISUID
342 ? (mode
& S_IXUSR
? 's' : 'S')
343 : (mode
& S_IXUSR
? 'x' : '-'));
344 str
[4] = mode
& S_IRGRP
? 'r' : '-';
345 str
[5] = mode
& S_IWGRP
? 'w' : '-';
346 str
[6] = (mode
& S_ISGID
347 ? (mode
& S_IXGRP
? 's' : 'S')
348 : (mode
& S_IXGRP
? 'x' : '-'));
349 str
[7] = mode
& S_IROTH
? 'r' : '-';
350 str
[8] = mode
& S_IWOTH
? 'w' : '-';
351 str
[9] = (mode
& S_ISVTX
352 ? (mode
& S_IXOTH
? 't' : 'T')
353 : (mode
& S_IXOTH
? 'x' : '-'));
358 print_namei(struct namei
*nm
, char *path
)
360 struct namei
*prev
= NULL
;
364 printf("f: %s\n", path
);
366 for (; nm
; prev
= nm
, nm
= nm
->next
) {
369 strmode(nm
->st
.st_mode
, md
);
371 if ((flags
& NAMEI_MNTS
) && prev
&&
372 S_ISDIR(nm
->st
.st_mode
) && S_ISDIR(prev
->st
.st_mode
) &&
373 prev
->st
.st_dev
!= nm
->st
.st_dev
)
376 if (!(flags
& NAMEI_VERTICAL
)) {
377 for (i
= 0; i
< nm
->level
; i
++)
382 if (flags
& NAMEI_MODES
)
387 if (flags
& NAMEI_OWNERS
) {
388 printf(" %-*s", uwidth
,
389 get_id(ucache
, nm
->st
.st_uid
)->name
);
390 printf(" %-*s", gwidth
,
391 get_id(gcache
, nm
->st
.st_gid
)->name
);
394 if (flags
& NAMEI_VERTICAL
)
395 for (i
= 0; i
< nm
->level
; i
++)
398 if (S_ISLNK(nm
->st
.st_mode
))
399 printf(" %s -> %s\n", nm
->name
,
400 nm
->abslink
+ nm
->relstart
);
402 printf(" %s\n", nm
->name
);
409 const char *p
= program_invocation_short_name
;
414 printf(_("\nUsage: %s [options] pathname [pathname ...]\n"), p
);
415 printf(_("\nOptions:\n"));
418 " -h, --help displays this help text\n"
419 " -x, --mountpoints show mount point directories with a 'D'\n"
420 " -m, --modes show the mode bits of each file\n"
421 " -o, --owners show owner and group name of each file\n"
422 " -l, --long use a long listing format (-m -o -v) \n"
423 " -n, --nosymlinks don't follow symlinks\n"
424 " -v, --vertical vertical align of modes and owners\n"));
426 printf(_("\nFor more information see namei(1).\n"));
430 struct option longopts
[] =
432 { "help", 0, 0, 'h' },
433 { "mountpoints",0, 0, 'x' },
434 { "modes", 0, 0, 'm' },
435 { "owners", 0, 0, 'o' },
436 { "long", 0, 0, 'l' },
437 { "nolinks", 0, 0, 'n' },
438 { "vertical", 0, 0, 'v' },
443 main(int argc
, char **argv
)
448 setlocale(LC_ALL
, "");
449 bindtextdomain(PACKAGE
, LOCALEDIR
);
455 while ((c
= getopt_long(argc
, argv
, "+h?lmnovx", longopts
, NULL
)) != -1) {
462 flags
|= (NAMEI_OWNERS
| NAMEI_MODES
| NAMEI_VERTICAL
);
465 flags
|= NAMEI_MODES
;
468 flags
|= NAMEI_NOLINKS
;
471 flags
|= NAMEI_OWNERS
;
477 flags
|= NAMEI_VERTICAL
;
481 for(; optind
< argc
; optind
++) {
482 char *path
= argv
[optind
];
483 struct namei
*nm
= NULL
;
486 if (stat(path
, &st
) != 0)
487 err(EXIT_FAILURE
, _("failed to stat: %s"), path
);
489 nm
= add_namei(NULL
, path
, 0, NULL
);
492 if (!(flags
& NAMEI_NOLINKS
))
493 sml
= follow_symlinks(nm
);
494 print_namei(nm
, path
);
498 _("%s: exceeded limit of symlinks"),
503 free_idcache(ucache
);
504 free_idcache(gcache
);