From c84a633a92f534452a0b79bc6f909d0f2e1c17d5 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Thu, 30 Oct 2008 10:19:39 +0100 Subject: [PATCH] namei: new re-written version This new version: * not based on chdir() * implemented without recursion (does not depend on stack size) * list of directories is stored in allocated memory (the code is extendable with new functionality (e.g. show usernames, groupnames, selunux contexts, ...). * supports long command line options * adds a new command line option: -n, --nosymlinks don't follow symlinks Signed-off-by: Karel Zak --- misc-utils/namei.1 | 18 +- misc-utils/namei.c | 623 +++++++++++++++++++++------------------------ 2 files changed, 303 insertions(+), 338 deletions(-) diff --git a/misc-utils/namei.1 b/misc-utils/namei.1 index 394eb6df..e35f870b 100644 --- a/misc-utils/namei.1 +++ b/misc-utils/namei.1 @@ -5,10 +5,7 @@ .SH NAME namei - follow a pathname until a terminal point is found .SH SYNOPSIS -.B namei -.I [-mx] -.I pathname -.I "[ pathname ... ]" +\fBnamei\fR [\fIoptions\fR] \fIpathname\fR [\fIpathname\fR ...] .SH DESCRIPTION .I Namei uses its arguments as pathnames to any type @@ -42,15 +39,18 @@ outputs a the following characters to identify the file types found: prints an informative message when the maximum number of symbolic links this system can have has been exceeded. .SH OPTIONS -.TP 8 -.B -x +.IP "\fB\-x, \-\-mountpoints\fP" Show mount point directories with a 'D', rather than a 'd'. -.TP 8 -.B -m +.IP "\fB\-m, \-\-modes\fP" Show the mode bits of each file type in the style of ls(1), for example 'rwxr-xr-x'. +.IP "\fB\-n, \-\-nosymlinks\fP" +Don't follow symlinks. .SH AUTHOR -Roger Southwick (rogers@amadeus.wr.tek.com) +The original +.B namei +program was written by Roger Southwick . +The program was re-written by Karel Zak . .SH BUGS To be discovered. .SH "SEE ALSO" diff --git a/misc-utils/namei.c b/misc-utils/namei.c index e8f7a8bd..da7696a4 100644 --- a/misc-utils/namei.c +++ b/misc-utils/namei.c @@ -1,59 +1,27 @@ -/*------------------------------------------------------------- - -The namei program - -By: Roger S. Southwick - -May 2, 1990 - - -Modifications by Steve Tell March 28, 1991 - -usage: namei pathname [pathname ... ] - -This program reads it's arguments as pathnames to any type -of Unix file (symlinks, files, directories, and so forth). -The program then follows each pathname until a terminal -point is found (a file, directory, char device, etc). -If it finds a symbolic link, we show the link, and start -following it, indenting the output to show the context. - -This program is useful for finding a "too many levels of -symbolic links" problems. - -For each line output, the program puts a file type first: - - f: = the pathname we are currently trying to resolve - d = directory - D = directory that is a mount point - l = symbolic link (both the link and it's contents are output) - s = socket - b = block device - c = character device - p = FIFO (named pipe) - - = regular file - ? = an error of some kind - -The program prints an informative messages when we exceed -the maximum number of symbolic links this system can have. - -The program exits with a 1 status ONLY if it finds it cannot -chdir to /, or if it encounters an unknown file type. - -1999-02-22 Arkadiusz Mi¶kiewicz -- added Native Language Support - -2006-12-15 Karel Zak -- fixed logic; don't follow the path if a component is not directory -- fixed infinite loop of symbolic links; stack size is very limited - -2007-09-10 Li Zefan -- added to identify FIFO - --------------------------------------------------------------*/ - +/* + * Copyright (C) 2008 Karel Zak + * + * This file is part of util-linux-ng. + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * The original namei(1) was writtent by: + * Roger S. Southwick (May 2, 1990) + * Steve Tell (March 28, 1991) + * Arkadiusz Mikiewicz (1999-02-22) + * Li Zefan (2007-09-10). + */ #include #include +#include #include #include #include @@ -61,14 +29,9 @@ chdir to /, or if it encounters an unknown file type. #include #include #include +#include #include "nls.h" -#define ERR strerror(errno),errno - -int symcount; -int mflag = 0; -int xflag = 0; - #ifndef MAXSYMLINKS #define MAXSYMLINKS 256 #endif @@ -77,316 +40,318 @@ int xflag = 0; #define PATH_MAX 4096 #endif -static char *pperm(unsigned short); -static void namei(char *, int, mode_t *); -static void usage(void); +#define NAMEI_NOLINKS (1 << 1) +#define NAMEI_MODES (1 << 2) +#define NAMEI_MNTS (1 << 3) -int -main(int argc, char **argv) { - extern int optind; - int c; -#ifdef HAVE_GET_CURRENT_DIR_NAME - char *curdir; -#else - char curdir[PATH_MAX]; -#endif - - setlocale(LC_ALL, ""); - bindtextdomain(PACKAGE, LOCALEDIR); - textdomain(PACKAGE); - - if(argc < 2) - usage(); - - while((c = getopt(argc, argv, "mx")) != -1){ - switch(c){ - case 'm': - mflag = !mflag; - break; +static int flags; - case 'x': - xflag = !xflag; - break; +struct namei { + struct stat st; /* item lstat() */ + char *name; /* item name */ + char *abslink; /* absolute symlink path */ + int relstart; /* offset of relative path in 'abslink' */ + struct namei *next; /* next item */ + int level; +}; - case '?': - default: - usage(); - } - } - -#ifdef HAVE_GET_CURRENT_DIR_NAME - if (!(curdir = get_current_dir_name())) -#else - if(getcwd(curdir, sizeof(curdir)) == NULL) -#endif - { - (void)fprintf(stderr, - _("namei: unable to get current directory - %s\n"), - curdir); - exit(1); - } - - - for(; optind < argc; optind++){ - mode_t lastmode = 0; - (void)printf("f: %s\n", argv[optind]); - symcount = 1; - namei(argv[optind], 0, &lastmode); - - if(chdir(curdir) == -1){ - (void)fprintf(stderr, - _("namei: unable to chdir to %s - %s (%d)\n"), - curdir, ERR); - exit(1); +static void +free_namei(struct namei *nm) +{ + while (nm) { + struct namei *next = nm->next; + free(nm->name); + free(nm->abslink); + free(nm); + nm = next; } - } - return 0; } static void -usage(void) { - (void)fprintf(stderr,_("usage: namei [-mx] pathname [pathname ...]\n")); - exit(1); +readlink_to_namei(struct namei *nm, const char *path) +{ + char sym[PATH_MAX]; + size_t sz; + + sz = readlink(path, sym, sizeof(sym)); + if (sz < 1) + err(EXIT_FAILURE, _("failed to read symlink: %s"), path); + if (*sym != '/') { + char *p = strrchr(path, '/'); + + nm->relstart = p ? p - path : strlen(path); + sz += nm->relstart + 1; + } + nm->abslink = malloc(sz + 1); + if (!nm->abslink) + err(EXIT_FAILURE, _("out of memory?")); + + if (*sym != '/') { + memcpy(nm->abslink, path, nm->relstart); + *(nm->abslink + nm->relstart) = '/'; + nm->relstart++; + memcpy(nm->abslink + nm->relstart, sym, sz); + } else + memcpy(nm->abslink, sym, sz); + nm->abslink[sz] = '\0'; } -#ifndef NODEV -#define NODEV (dev_t)(-1) -#endif +static struct namei * +new_namei(struct namei *parent, const char *path, const char *fname, int lev) +{ + struct namei *nm; + + if (!fname) + return NULL; + nm = calloc(1, sizeof(*nm)); + if (!nm) + err(EXIT_FAILURE, _("out of memory?")); + 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); + return nm; +} -static void -namei(char *file, int lev, mode_t *lastmode) { - char *cp; - char buf[BUFSIZ], sym[BUFSIZ]; - struct stat stb; - int i; - dev_t lastdev = NODEV; - - /* - * See if the file has a leading /, and if so cd to root - */ - - if(file && *file == '/'){ - while(*file == '/') - file++; - - if(chdir("/") == -1){ - (void)fprintf(stderr,_("namei: could not chdir to root!\n")); - exit(1); +static struct namei * +add_namei(struct namei *parent, const char *orgpath, int start, struct namei **last) +{ + struct namei *nm = NULL, *first = NULL; + char *fname, *end, *path; + int level = 0; + + if (!orgpath) + return NULL; + if (parent) { + nm = parent; + level = parent->level + 1; } - for(i = 0; i < lev; i++) - (void)printf(" "); - - if(stat("/", &stb) == -1){ - (void)fprintf(stderr, _("namei: could not stat root!\n")); - exit(1); + path = strdup(orgpath); + if (!path) + err(EXIT_FAILURE, _("out of memory?")); + fname = path + start; + + /* root directory */ + if (*fname == '/') { + while (*fname == '/') + fname++; /* eat extra '/' */ + first = nm = new_namei(nm, "/", "/", level); } - lastdev = stb.st_dev; - - if(mflag) - (void)printf(" d%s /\n", pperm(stb.st_mode)); - else - (void)printf(" d /\n"); - } - for(; file && *file;){ - - if (strlen(file) >= BUFSIZ) { - fprintf(stderr,_("namei: buf overflow\n")); - return; + for (end = fname; fname && end; ) { + /* set end of filename */ + end = strchr(fname, '/'); + if (end) + *end = '\0'; + + /* create a new entry */ + nm = new_namei(nm, path, fname, level); + if (!first) + first = nm; + if (S_ISLNK(nm->st.st_mode)) + readlink_to_namei(nm, path); + + /* set begin of the next filename */ + if (end) { + *end++ = '/'; + while (*end == '/') + end++; /* eat extra '/' */ + } + fname = end; } - /* - * Copy up to the next / (or nil) into buf - */ - - for(cp = buf; *file != '\0' && *file != '/'; cp++, file++) - *cp = *file; - - while(*file == '/') /* eat extra /'s */ - file++; - - *cp = '\0'; + if (last) + *last = nm; + return first; +} - if(buf[0] == '\0'){ - /* - * Buf is empty, so therefore we are done - * with this level of file - */ +static int +follow_symlinks(struct namei *nm) +{ + int symcount = 0; - return; - } + for (; nm; nm = nm->next) { + struct namei *next, *last; - for(i = 0; i < lev; i++) - (void)printf(" "); - - - /* - * We cannot walk on *path* if a previous element, in the path wasn't - * directory, because there could be a component with same name. Try: - * - * $ touch a b - * $ namei a/b <-- "a" is not directory so namei shouldn't - * check for "b" - */ - if (*lastmode && S_ISDIR(*lastmode)==0 && S_ISLNK(*lastmode)==0){ - (void)printf(" ? %s - %s (%d)\n", buf, strerror(ENOENT), ENOENT); - return; + if (!S_ISLNK(nm->st.st_mode)) + continue; + if (++symcount > MAXSYMLINKS) { + /* drop the rest of the list */ + free_namei(nm->next); + nm->next = NULL; + return -1; + } + next = nm->next; + nm->next = add_namei(nm, nm->abslink, nm->relstart, &last); + if (last) + last->next = next; + else + nm->next = next; } + return 0; +} - /* - * See what type of critter this file is - */ - - if(lstat(buf, &stb) == -1){ - (void)printf(" ? %s - %s (%d)\n", buf, ERR); - return; - } +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'; +} - *lastmode = stb.st_mode; +static void +print_namei(struct namei *nm, char *path) +{ + struct namei *prev = NULL; + int i; - switch(stb.st_mode & S_IFMT){ - case S_IFDIR: + if (path) + printf("f: %s\n", path); - /* - * File is a directory, chdir to it - */ + for (; nm; prev = nm, nm = nm->next) { + char md[11]; - if(chdir(buf) == -1){ - (void)printf(_(" ? could not chdir into %s - %s (%d)\n"), buf, ERR ); - return; - } - if(xflag && lastdev != stb.st_dev && lastdev != NODEV){ - /* Across mnt point */ - if(mflag) - (void)printf(" D%s %s\n", pperm(stb.st_mode), buf); - else - (void)printf(" D %s\n", buf); - } - else { - if(mflag) - (void)printf(" d%s %s\n", pperm(stb.st_mode), buf); - else - (void)printf(" d %s\n", buf); - } - lastdev = stb.st_dev; - - (void)fflush(stdout); - break; - - case S_IFLNK: - /* - * Sigh, another symlink. Read its contents and - * call namei() - */ - bzero(sym, BUFSIZ); - if(readlink(buf, sym, BUFSIZ) == -1){ - (void)printf(_(" ? problems reading symlink %s - %s (%d)\n"), buf, ERR); - return; - } + strmode(nm->st.st_mode, md); - if(mflag) - (void)printf(" l%s %s -> %s", pperm(stb.st_mode), buf, sym); - else - (void)printf(" l %s -> %s", buf, sym); + 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) + md[0] = 'D'; - if(symcount > 0 && symcount++ > MAXSYMLINKS){ - (void)printf(_(" *** EXCEEDED UNIX LIMIT OF SYMLINKS ***\n")); - } else { - (void)printf("\n"); - namei(sym, lev + 1, lastmode); - } - if (symcount > MAXSYMLINKS) - return; - break; + for (i = 0; i < nm->level; i++) + fputs(" ", stdout); - case S_IFCHR: - if(mflag) - (void)printf(" c%s %s\n", pperm(stb.st_mode), buf); + if (flags & NAMEI_MODES) + printf(" %s", md); else - (void)printf(" c %s\n", buf); - break; + printf(" %c", md[0]); - case S_IFBLK: - if(mflag) - (void)printf(" b%s %s\n", pperm(stb.st_mode), buf); + if (S_ISLNK(nm->st.st_mode)) + printf(" %s -> %s\n", nm->name, + nm->abslink + nm->relstart); else - (void)printf(" b %s\n", buf); - break; + printf(" %s\n", nm->name); + } +} - case S_IFSOCK: - if(mflag) - (void)printf(" s%s %s\n", pperm(stb.st_mode), buf); - else - (void)printf(" s %s\n", buf); - break; +static void +usage(int rc) +{ + const char *p = program_invocation_short_name; - case S_IFIFO: - if (mflag) - printf(" p%s %s\n", pperm(stb.st_mode), buf); - else - printf(" p %s\n", buf); - break; + if (!*p) + p = "namei"; - case S_IFREG: - if(mflag) - (void)printf(" -%s %s\n", pperm(stb.st_mode), buf); - else - (void)printf(" - %s\n", buf); - break; + printf(_("\nUsage: %s [options] pathname [pathname ...]\n"), p); + printf(_("\nOptions:\n")); - default: - (void)fprintf(stderr,_("namei: unknown file type 0%06o on file %s\n"), stb.st_mode, buf ); - exit(1); + 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" + " -n, --nosymlinks don't follow symlinks\n")); - } - } + printf(_("\nFor more information see namei(1).\n")); + exit(rc); } -/* Take a - * Mode word, as from a struct stat, and return - * a pointer to a static string containing a printable version like ls. - * For example 0755 produces "rwxr-xr-x" - */ -static char * -pperm(unsigned short mode) { - unsigned short m; - static char buf[16]; - char *bp; - char *lschars = "xwrxwrxwr"; /* the complete string backwards */ - char *cp; - int i; +struct option longopts[] = +{ + { "help", 0, 0, 'h' }, + { "mountpoints",0, 0, 'x' }, + { "modes", 0, 0, 'm' }, + { "nolinks", 0, 0, 'n' }, + { NULL, 0, 0, 0 }, +}; - for(i = 0, cp = lschars, m = mode, bp = &buf[8]; - i < 9; - i++, cp++, m >>= 1, bp--) { - - if(m & 1) - *bp = *cp; - else - *bp = '-'; - } - buf[9] = '\0'; - - if(mode & S_ISUID) { - if(buf[2] == 'x') - buf[2] = 's'; - else - buf[2] = 'S'; - } - if(mode & S_ISGID) { - if(buf[5] == 'x') - buf[5] = 's'; - else - buf[5] = 'S'; +int +main(int argc, char **argv) +{ + extern int optind; + int c; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + if (argc < 2) + usage(EXIT_FAILURE); + + while ((c = getopt_long(argc, argv, "+h?xmn", longopts, NULL)) != -1) { + switch(c) { + case 'h': + case '?': + usage(EXIT_SUCCESS); + break; + case 'm': + flags |= NAMEI_MODES; + break; + case 'x': + flags |= NAMEI_MNTS; + break; + case 'n': + flags |= NAMEI_NOLINKS; + break; + } } - if(mode & S_ISVTX) { - if(buf[8] == 'x') - buf[8] = 't'; - else - buf[8] = 'T'; + + 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); + + nm = add_namei(NULL, path, 0, NULL); + if (nm) { + int sml = 0; + + if (!(flags & NAMEI_NOLINKS)) + sml = follow_symlinks(nm); + print_namei(nm, path); + free_namei(nm); + if (sml == -1) + errx(EXIT_FAILURE, + _("%s: exceeded limit of symlinks"), + path); + } } - return &buf[0]; + return EXIT_SUCCESS; } -- 2.39.5