/*
* Copyright (C) 2008 Karel Zak <kzak@redhat.com>
*
- * This file is part of util-linux-ng.
+ * This file is part of util-linux.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * The original namei(1) was writtent by:
+ * The original namei(1) was written by:
* Roger S. Southwick (May 2, 1990)
* Steve Tell (March 28, 1991)
- * Arkadiusz Mikiewicz (1999-02-22)
+ * Arkadiusz Miśkiewicz (1999-02-22)
* Li Zefan (2007-09-10).
*/
+
#include <stdio.h>
#include <unistd.h>
#include <getopt.h>
#include <string.h>
-#include <strings.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
-#include <err.h>
#include <pwd.h>
#include <grp.h>
+
+#include "c.h"
+#include "xalloc.h"
#include "nls.h"
#include "widechar.h"
+#include "strutils.h"
+#include "closestream.h"
+#include "idcache.h"
#ifndef MAXSYMLINKS
#define MAXSYMLINKS 256
#endif
-#ifndef PATH_MAX
-#define PATH_MAX 4096
-#endif
-
-#ifndef LOGIN_NAME_MAX
-#define LOGIN_NAME_MAX 256
-#endif
-
#define NAMEI_NOLINKS (1 << 1)
#define NAMEI_MODES (1 << 2)
#define NAMEI_MNTS (1 << 3)
int relstart; /* offset of relative path in 'abslink' */
struct namei *next; /* next item */
int level;
-};
-
-struct idcache {
- unsigned long int id;
- char *name;
- struct idcache *next;
+ int mountpoint; /* is mount point */
+ int noent; /* this item not existing (stores errno from stat()) */
};
static int flags;
-static int uwidth; /* maximal width of username */
-static int gwidth; /* maximal width of groupname */
static struct idcache *gcache; /* groupnames */
static struct idcache *ucache; /* usernames */
-static struct idcache *
-get_id(struct idcache *ic, unsigned long int id)
-{
- while(ic) {
- if (ic->id == id)
- return ic;
- ic = ic->next;
- }
- return NULL;
-}
-
-static void
-free_idcache(struct idcache *ic)
-{
- while(ic) {
- struct idcache *next = ic->next;
- free(ic->name);
- free(ic);
- ic = next;
- }
-}
-
-static void
-add_id(struct idcache **ic, char *name, unsigned long int id, int *width)
-{
- struct idcache *nc, *x;
- int w = 0;
-
- nc = calloc(1, sizeof(*nc));
- if (!nc)
- goto alloc_err;
- nc->id = id;
-
- if (name) {
-#ifdef HAVE_WIDECHAR
- wchar_t wc[LOGIN_NAME_MAX + 1];
-
- if (mbstowcs(wc, name, LOGIN_NAME_MAX) > 0) {
- wc[LOGIN_NAME_MAX] = '\0';
- w = wcswidth(wc, LOGIN_NAME_MAX);
- }
- else
-#endif
- w = strlen(name);
- }
- /* note, we ignore names with non-printable widechars */
- if (w > 0)
- nc->name = strdup(name);
- else if (asprintf(&nc->name, "%lu", id) == -1)
- nc->name = NULL;
- if (!nc->name)
- goto alloc_err;
-
- for (x = *ic; x && x->next; x = x->next);
-
- /* add 'nc' at end of the 'ic' list */
- if (x)
- x->next = nc;
- else
- *ic = nc;
- if (w <= 0)
- w = strlen(nc->name);
- *width = *width < w ? w : *width;
-
- return;
-alloc_err:
- err(EXIT_FAILURE, _("out of memory?"));
-}
-
-static void
-add_uid(unsigned long int id)
-{
- struct idcache *ic = get_id(ucache, id);
-
- if (!ic) {
- struct passwd *pw = getpwuid((uid_t) id);
- add_id(&ucache, pw ? pw->pw_name : NULL, id, &uwidth);
- }
-}
-
-static void
-add_gid(unsigned long int id)
-{
- struct idcache *ic = get_id(gcache, id);
-
- if (!ic) {
- struct group *gr = getgrgid((gid_t) id);
- add_id(&gcache, gr ? gr->gr_name : NULL, id, &gwidth);
- }
-}
-
static void
free_namei(struct namei *nm)
{
readlink_to_namei(struct namei *nm, const char *path)
{
char sym[PATH_MAX];
- size_t sz;
+ ssize_t sz;
+ int isrel = 0;
sz = readlink(path, sym, sizeof(sym));
if (sz < 1)
if (*sym != '/') {
char *p = strrchr(path, '/');
- nm->relstart = p ? p - path : 0;
- if (nm->relstart)
+ if (p) {
+ isrel = 1;
+ nm->relstart = p - path;
sz += nm->relstart + 1;
+ }
}
- nm->abslink = malloc(sz + 1);
- if (!nm->abslink)
- err(EXIT_FAILURE, _("out of memory?"));
+ nm->abslink = xmalloc(sz + 1);
- if (*sym != '/' && nm->relstart) {
+ if (*sym != '/' && isrel) {
/* create the absolute path from the relative symlink */
memcpy(nm->abslink, path, nm->relstart);
*(nm->abslink + nm->relstart) = '/';
nm->relstart++;
memcpy(nm->abslink + nm->relstart, sym, sz - nm->relstart);
} else
+ /* - absolute link (foo -> /path/bar)
+ * - or link without any subdir (foo -> bar)
+ */
memcpy(nm->abslink, sym, sz);
+
nm->abslink[sz] = '\0';
}
+static struct stat *
+dotdot_stat(const char *dirname, struct stat *st)
+{
+ char *path;
+ size_t len;
+
+#define DOTDOTDIR "/.."
+
+ if (!dirname)
+ return NULL;
+
+ len = strlen(dirname);
+ path = xmalloc(len + sizeof(DOTDOTDIR));
+
+ memcpy(path, dirname, len);
+ memcpy(path + len, DOTDOTDIR, sizeof(DOTDOTDIR));
+
+ if (stat(path, st))
+ err(EXIT_FAILURE, _("stat of %s failed"), path);
+ free(path);
+ return st;
+}
+
static struct namei *
new_namei(struct namei *parent, const char *path, const char *fname, int lev)
{
if (!fname)
return NULL;
- nm = calloc(1, sizeof(*nm));
- if (!nm)
- err(EXIT_FAILURE, _("out of memory?"));
+ nm = xcalloc(1, sizeof(*nm));
if (parent)
parent->next = nm;
nm->level = lev;
- nm->name = strdup(fname);
- if (!nm->name)
- err(EXIT_FAILURE, _("out of memory?"));
- if (lstat(path, &nm->st) == -1)
- err(EXIT_FAILURE, _("could not stat '%s'"), path);
+ nm->name = xstrdup(fname);
+
+ if (lstat(path, &nm->st) != 0) {
+ nm->noent = errno;
+ return nm;
+ }
+
+ if (S_ISLNK(nm->st.st_mode))
+ readlink_to_namei(nm, path);
+ if (flags & NAMEI_OWNERS) {
+ add_uid(ucache, nm->st.st_uid);
+ add_gid(gcache, nm->st.st_gid);
+ }
+
+ if ((flags & NAMEI_MNTS) && S_ISDIR(nm->st.st_mode)) {
+ struct stat stbuf, *sb = NULL;
+
+ if (parent && S_ISDIR(parent->st.st_mode))
+ sb = &parent->st;
+ else if (!parent || S_ISLNK(parent->st.st_mode))
+ sb = dotdot_stat(path, &stbuf);
+
+ if (sb && (sb->st_dev != nm->st.st_dev || /* different device */
+ sb->st_ino == nm->st.st_ino)) /* root directory */
+ nm->mountpoint = 1;
+ }
+
return nm;
}
nm = parent;
level = parent->level + 1;
}
- path = strdup(orgpath);
- if (!path)
- err(EXIT_FAILURE, _("out of memory?"));
+ path = xstrdup(orgpath);
fname = path + start;
/* root directory */
end = NULL;
if (!first)
first = nm;
- if (S_ISLNK(nm->st.st_mode))
- readlink_to_namei(nm, path);
- if (flags & NAMEI_OWNERS) {
- add_uid(nm->st.st_uid);
- add_gid(nm->st.st_gid);
- }
/* set begin of the next filename */
if (end) {
*end++ = '/';
if (last)
*last = nm;
+
+ free(path);
+
return first;
}
-
static int
follow_symlinks(struct namei *nm)
{
for (; nm; nm = nm->next) {
struct namei *next, *last;
+ if (nm->noent)
+ continue;
if (!S_ISLNK(nm->st.st_mode))
continue;
if (++symcount > MAXSYMLINKS) {
return 0;
}
-static void
-strmode(mode_t mode, char *str)
-{
- if (S_ISDIR(mode))
- str[0] = 'd';
- else if (S_ISLNK(mode))
- str[0] = 'l';
- else if (S_ISCHR(mode))
- str[0] = 'c';
- else if (S_ISBLK(mode))
- str[0] = 'b';
- else if (S_ISSOCK(mode))
- str[0] = 's';
- else if (S_ISFIFO(mode))
- str[0] = 'p';
- else if (S_ISREG(mode))
- str[0] = '-';
-
- str[1] = mode & S_IRUSR ? 'r' : '-';
- str[2] = mode & S_IWUSR ? 'w' : '-';
- str[3] = (mode & S_ISUID
- ? (mode & S_IXUSR ? 's' : 'S')
- : (mode & S_IXUSR ? 'x' : '-'));
- str[4] = mode & S_IRGRP ? 'r' : '-';
- str[5] = mode & S_IWGRP ? 'w' : '-';
- str[6] = (mode & S_ISGID
- ? (mode & S_IXGRP ? 's' : 'S')
- : (mode & S_IXGRP ? 'x' : '-'));
- str[7] = mode & S_IROTH ? 'r' : '-';
- str[8] = mode & S_IWOTH ? 'w' : '-';
- str[9] = (mode & S_ISVTX
- ? (mode & S_IXOTH ? 't' : 'T')
- : (mode & S_IXOTH ? 'x' : '-'));
- str[10] = '\0';
-}
-
-static void
+static int
print_namei(struct namei *nm, char *path)
{
- struct namei *prev = NULL;
int i;
if (path)
printf("f: %s\n", path);
- for (; nm; prev = nm, nm = nm->next) {
+ for (; nm; nm = nm->next) {
char md[11];
- strmode(nm->st.st_mode, md);
+ if (nm->noent) {
+ int blanks = 1;
+ if (flags & NAMEI_MODES)
+ blanks += 9;
+ if (flags & NAMEI_OWNERS)
+ blanks += ucache->width + gcache->width + 2;
+ if (!(flags & NAMEI_VERTICAL))
+ blanks += 1;
+ blanks += nm->level * 2;
+ printf("%*s ", blanks, "");
+ printf("%s - %s\n", nm->name, strerror(nm->noent));
+ return -1;
+ }
+
+ xstrmode(nm->st.st_mode, md);
- if ((flags & NAMEI_MNTS) && prev &&
- S_ISDIR(nm->st.st_mode) && S_ISDIR(prev->st.st_mode) &&
- prev->st.st_dev != nm->st.st_dev)
+ if (nm->mountpoint)
md[0] = 'D';
if (!(flags & NAMEI_VERTICAL)) {
printf("%c", md[0]);
if (flags & NAMEI_OWNERS) {
- printf(" %-*s", uwidth,
+ printf(" %-*s", ucache->width,
get_id(ucache, nm->st.st_uid)->name);
- printf(" %-*s", gwidth,
+ printf(" %-*s", gcache->width,
get_id(gcache, nm->st.st_gid)->name);
}
else
printf(" %s\n", nm->name);
}
+ return 0;
}
-static void
-usage(int rc)
+static void __attribute__((__noreturn__)) usage(void)
{
const char *p = program_invocation_short_name;
+ FILE *out = stdout;
if (!*p)
p = "namei";
- printf(_("\nUsage: %s [options] pathname [pathname ...]\n"), p);
- printf(_("\nOptions:\n"));
-
- printf(_(
- " -h, --help displays this help text\n"
- " -x, --mountpoints show mount point directories with a 'D'\n"
- " -m, --modes show the mode bits of each file\n"
- " -o, --owners show owner and group name of each file\n"
- " -l, --long use a long listing format (-m -o -v) \n"
- " -n, --nosymlinks don't follow symlinks\n"
- " -v, --vertical vertical align of modes and owners\n"));
-
- printf(_("\nFor more information see namei(1).\n"));
- exit(rc);
+ fputs(USAGE_HEADER, out);
+ fprintf(out,
+ _(" %s [options] <pathname>...\n"), p);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Follow a pathname until a terminal point is found.\n"), out);
+
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(
+ " -x, --mountpoints show mount point directories with a 'D'\n"
+ " -m, --modes show the mode bits of each file\n"
+ " -o, --owners show owner and group name of each file\n"
+ " -l, --long use a long listing format (-m -o -v) \n"
+ " -n, --nosymlinks don't follow symlinks\n"
+ " -v, --vertical vertical align of modes and owners\n"), out);
+ printf(USAGE_HELP_OPTIONS(21));
+
+ printf(USAGE_MAN_TAIL("namei(1)"));
+ exit(EXIT_SUCCESS);
}
-struct option longopts[] =
+static const struct option longopts[] =
{
- { "help", 0, 0, 'h' },
- { "mountpoints",0, 0, 'x' },
- { "modes", 0, 0, 'm' },
- { "owners", 0, 0, 'o' },
- { "long", 0, 0, 'l' },
- { "nolinks", 0, 0, 'n' },
- { "vertical", 0, 0, 'v' },
- { NULL, 0, 0, 0 },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { "mountpoints", no_argument, NULL, 'x' },
+ { "modes", no_argument, NULL, 'm' },
+ { "owners", no_argument, NULL, 'o' },
+ { "long", no_argument, NULL, 'l' },
+ { "nolinks", no_argument, NULL, 'n' },
+ { "vertical", no_argument, NULL, 'v' },
+ { NULL, 0, NULL, 0 },
};
int
main(int argc, char **argv)
{
- extern int optind;
int c;
+ int rc = EXIT_SUCCESS;
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
+ close_stdout_atexit();
- if (argc < 2)
- usage(EXIT_FAILURE);
-
- while ((c = getopt_long(argc, argv, "+h?lmnovx", longopts, NULL)) != -1) {
+ while ((c = getopt_long(argc, argv, "hVlmnovx", longopts, NULL)) != -1) {
switch(c) {
- case 'h':
- case '?':
- usage(EXIT_SUCCESS);
- break;
case 'l':
flags |= (NAMEI_OWNERS | NAMEI_MODES | NAMEI_VERTICAL);
break;
break;
case 'v':
flags |= NAMEI_VERTICAL;
+ break;
+
+ case 'h':
+ usage();
+ case 'V':
+ print_version(EXIT_SUCCESS);
+ default:
+ errtryhelp(EXIT_FAILURE);
}
}
+ if (optind == argc) {
+ warnx(_("pathname argument is missing"));
+ errtryhelp(EXIT_FAILURE);
+ }
+
+ ucache = new_idcache();
+ if (!ucache)
+ err(EXIT_FAILURE, _("failed to allocate UID cache"));
+ gcache = new_idcache();
+ if (!gcache)
+ err(EXIT_FAILURE, _("failed to allocate GID cache"));
+
for(; optind < argc; optind++) {
char *path = argv[optind];
struct namei *nm = NULL;
struct stat st;
if (stat(path, &st) != 0)
- err(EXIT_FAILURE, _("failed to stat: %s"), path);
+ rc = EXIT_FAILURE;
nm = add_namei(NULL, path, 0, NULL);
if (nm) {
int sml = 0;
if (!(flags & NAMEI_NOLINKS))
sml = follow_symlinks(nm);
- print_namei(nm, path);
+ if (print_namei(nm, path)) {
+ rc = EXIT_FAILURE;
+ continue;
+ }
free_namei(nm);
- if (sml == -1)
- errx(EXIT_FAILURE,
- _("%s: exceeded limit of symlinks"),
- path);
+ if (sml == -1) {
+ rc = EXIT_FAILURE;
+ warnx(_("%s: exceeded limit of symlinks"), path);
+ continue;
+ }
}
}
free_idcache(ucache);
free_idcache(gcache);
- return EXIT_SUCCESS;
+ return rc;
}