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 : strlen(path);
194 sz += nm->relstart + 1;
196 nm->abslink = malloc(sz + 1);
198 err(EXIT_FAILURE, _("out of memory?"));
201 /* create the absolute path from the relative symlink */
202 memcpy(nm->abslink, path, nm->relstart);
203 *(nm->abslink + nm->relstart) = '/';
205 memcpy(nm->abslink + nm->relstart, sym, sz - nm->relstart);
207 memcpy(nm->abslink, sym, sz);
208 nm->abslink[sz] = '\0';
211 static struct namei *
212 new_namei(struct namei *parent, const char *path, const char *fname, int lev)
218 nm = calloc(1, sizeof(*nm));
220 err(EXIT_FAILURE, _("out of memory?"));
225 nm->name = strdup(fname);
227 err(EXIT_FAILURE, _("out of memory?"));
228 if (lstat(path, &nm->st) == -1)
229 err(EXIT_FAILURE, _("could not stat '%s'"), path);
233 static struct namei *
234 add_namei(struct namei *parent, const char *orgpath, int start, struct namei **last)
236 struct namei *nm = NULL, *first = NULL;
237 char *fname, *end, *path;
244 level = parent->level + 1;
246 path = strdup(orgpath);
248 err(EXIT_FAILURE, _("out of memory?"));
249 fname = path + start;
253 while (*fname == '/')
254 fname++; /* eat extra '/' */
255 first = nm = new_namei(nm, "/", "/", level);
258 for (end = fname; fname && end; ) {
259 /* set end of filename */
261 end = strchr(fname, '/');
265 /* create a new entry */
266 nm = new_namei(nm, path, fname, level);
271 if (S_ISLNK(nm->st.st_mode))
272 readlink_to_namei(nm, path);
273 if (flags & NAMEI_OWNERS) {
274 add_uid(nm->st.st_uid);
275 add_gid(nm->st.st_gid);
277 /* set begin of the next filename */
281 end++; /* eat extra '/' */
293 follow_symlinks(struct namei *nm)
297 for (; nm; nm = nm->next) {
298 struct namei *next, *last;
300 if (!S_ISLNK(nm->st.st_mode))
302 if (++symcount > MAXSYMLINKS) {
303 /* drop the rest of the list */
304 free_namei(nm->next);
309 nm->next = add_namei(nm, nm->abslink, nm->relstart, &last);
319 strmode(mode_t mode, char *str)
323 else if (S_ISLNK(mode))
325 else if (S_ISCHR(mode))
327 else if (S_ISBLK(mode))
329 else if (S_ISSOCK(mode))
331 else if (S_ISFIFO(mode))
333 else if (S_ISREG(mode))
336 str[1] = mode & S_IRUSR ? 'r' : '-';
337 str[2] = mode & S_IWUSR ? 'w' : '-';
338 str[3] = (mode & S_ISUID
339 ? (mode & S_IXUSR ? 's' : 'S')
340 : (mode & S_IXUSR ? 'x' : '-'));
341 str[4] = mode & S_IRGRP ? 'r' : '-';
342 str[5] = mode & S_IWGRP ? 'w' : '-';
343 str[6] = (mode & S_ISGID
344 ? (mode & S_IXGRP ? 's' : 'S')
345 : (mode & S_IXGRP ? 'x' : '-'));
346 str[7] = mode & S_IROTH ? 'r' : '-';
347 str[8] = mode & S_IWOTH ? 'w' : '-';
348 str[9] = (mode & S_ISVTX
349 ? (mode & S_IXOTH ? 't' : 'T')
350 : (mode & S_IXOTH ? 'x' : '-'));
355 print_namei(struct namei *nm, char *path)
357 struct namei *prev = NULL;
361 printf("f: %s\n", path);
363 for (; nm; prev = nm, nm = nm->next) {
366 strmode(nm->st.st_mode, md);
368 if ((flags & NAMEI_MNTS) && prev &&
369 S_ISDIR(nm->st.st_mode) && S_ISDIR(prev->st.st_mode) &&
370 prev->st.st_dev != nm->st.st_dev)
373 if (!(flags & NAMEI_VERTICAL)) {
374 for (i = 0; i < nm->level; i++)
379 if (flags & NAMEI_MODES)
384 if (flags & NAMEI_OWNERS) {
385 printf(" %-*s", uwidth,
386 get_id(ucache, nm->st.st_uid)->name);
387 printf(" %-*s", gwidth,
388 get_id(gcache, nm->st.st_gid)->name);
391 if (flags & NAMEI_VERTICAL)
392 for (i = 0; i < nm->level; i++)
395 if (S_ISLNK(nm->st.st_mode))
396 printf(" %s -> %s\n", nm->name,
397 nm->abslink + nm->relstart);
399 printf(" %s\n", nm->name);
406 const char *p = program_invocation_short_name;
411 printf(_("\nUsage: %s [options] pathname [pathname ...]\n"), p);
412 printf(_("\nOptions:\n"));
415 " -h, --help displays this help text\n"
416 " -x, --mountpoints show mount point directories with a 'D'\n"
417 " -m, --modes show the mode bits of each file\n"
418 " -o, --owners show owner and group name of each file\n"
419 " -l, --long use a long listing format (-m -o -v) \n"
420 " -n, --nosymlinks don't follow symlinks\n"
421 " -v, --vertical vertical align of modes and owners\n"));
423 printf(_("\nFor more information see namei(1).\n"));
427 struct option longopts[] =
429 { "help", 0, 0, 'h' },
430 { "mountpoints",0, 0, 'x' },
431 { "modes", 0, 0, 'm' },
432 { "owners", 0, 0, 'o' },
433 { "long", 0, 0, 'l' },
434 { "nolinks", 0, 0, 'n' },
435 { "vertical", 0, 0, 'v' },
440 main(int argc, char **argv)
445 setlocale(LC_ALL, "");
446 bindtextdomain(PACKAGE, LOCALEDIR);
452 while ((c = getopt_long(argc, argv, "+h?lmnovx", longopts, NULL)) != -1) {
459 flags |= (NAMEI_OWNERS | NAMEI_MODES | NAMEI_VERTICAL);
462 flags |= NAMEI_MODES;
465 flags |= NAMEI_NOLINKS;
468 flags |= NAMEI_OWNERS;
474 flags |= NAMEI_VERTICAL;
478 for(; optind < argc; optind++) {
479 char *path = argv[optind];
480 struct namei *nm = NULL;
483 if (stat(path, &st) != 0)
484 err(EXIT_FAILURE, _("failed to stat: %s"), path);
486 nm = add_namei(NULL, path, 0, NULL);
489 if (!(flags & NAMEI_NOLINKS))
490 sml = follow_symlinks(nm);
491 print_namei(nm, path);
495 _("%s: exceeded limit of symlinks"),
500 free_idcache(ucache);
501 free_idcache(gcache);