]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - misc-utils/namei.c
Merge branch '2018wk48' of https://github.com/kerolasa/util-linux
[thirdparty/util-linux.git] / misc-utils / namei.c
index f133e40fa51325ef7896d3c3f66b31a37362eac3..60171f60028225f3b616ad6145ba7f0884413254 100644 (file)
  * 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).
  */
 
@@ -24,7 +24,6 @@
 #include <unistd.h>
 #include <getopt.h>
 #include <string.h>
-#include <strings.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <sys/types.h>
 #include "nls.h"
 #include "widechar.h"
 #include "strutils.h"
+#include "closestream.h"
+#include "idcache.h"
 
 #ifndef MAXSYMLINKS
 #define MAXSYMLINKS 256
 #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)
@@ -62,112 +59,13 @@ struct namei {
        struct namei    *next;          /* next item */
        int             level;
        int             mountpoint;     /* is mount point */
-       int             noent;          /* is this item not existing */
-};
-
-struct idcache {
-       unsigned long int       id;
-       char                    *name;
-       struct idcache          *next;
+       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)
 {
@@ -185,6 +83,7 @@ readlink_to_namei(struct namei *nm, const char *path)
 {
        char sym[PATH_MAX];
        ssize_t sz;
+       int isrel = 0;
 
        sz = readlink(path, sym, sizeof(sym));
        if (sz < 1)
@@ -192,20 +91,26 @@ readlink_to_namei(struct namei *nm, const char *path)
        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 = 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';
 }
 
@@ -221,15 +126,13 @@ dotdot_stat(const char *dirname, struct stat *st)
                return NULL;
 
        len = strlen(dirname);
-       path = malloc(len + sizeof(DOTDOTDIR));
-       if (!path)
-               err(EXIT_FAILURE, _("out of memory?"));
+       path = xmalloc(len + sizeof(DOTDOTDIR));
 
        memcpy(path, dirname, len);
        memcpy(path + len, DOTDOTDIR, sizeof(DOTDOTDIR));
 
        if (stat(path, st))
-               err(EXIT_FAILURE, _("could not stat '%s'"), path);
+               err(EXIT_FAILURE, _("stat of %s failed"), path);
        free(path);
        return st;
 }
@@ -241,26 +144,23 @@ 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?"));
+       nm->name = xstrdup(fname);
 
-       nm->noent = (lstat(path, &nm->st) == -1);
-       if (nm->noent)
+       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(nm->st.st_uid);
-               add_gid(nm->st.st_gid);
+               add_uid(ucache, nm->st.st_uid);
+               add_gid(gcache, nm->st.st_gid);
        }
 
        if ((flags & NAMEI_MNTS) && S_ISDIR(nm->st.st_mode)) {
@@ -292,9 +192,7 @@ add_namei(struct namei *parent, const char *orgpath, int start, struct namei **l
                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 */
@@ -374,11 +272,20 @@ print_namei(struct namei *nm, char *path)
                char md[11];
 
                if (nm->noent) {
-                       printf(_("%s - No such file or directory\n"), nm->name);
+                       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;
                }
 
-               strmode(nm->st.st_mode, md);
+               xstrmode(nm->st.st_mode, md);
 
                if (nm->mountpoint)
                        md[0] = 'D';
@@ -395,9 +302,9 @@ print_namei(struct namei *nm, char *path)
                        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);
                }
 
@@ -414,67 +321,66 @@ print_namei(struct namei *nm, char *path)
        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"
-       " -V, --version       output version information and exit\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);
 }
 
 static const struct option longopts[] =
 {
-       { "help",       0, 0, 'h' },
-       { "version",    0, 0, 'V' },
-       { "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);
+       atexit(close_stdout);
 
-       if (argc < 2)
-               usage(EXIT_FAILURE);
-
-       while ((c = getopt_long(argc, argv, "+h?Vlmnovx", longopts, NULL)) != -1) {
+       while ((c = getopt_long(argc, argv, "hVlmnovx", longopts, NULL)) != -1) {
                switch(c) {
                case 'h':
-               case '?':
-                       usage(EXIT_SUCCESS);
+                       usage();
                        break;
                case 'V':
-                       printf(_("%s from %s\n"), program_invocation_short_name,
-                                                 PACKAGE_STRING);
+                       printf(UTIL_LINUX_VERSION);
                        return EXIT_SUCCESS;
                case 'l':
                        flags |= (NAMEI_OWNERS | NAMEI_MODES | NAMEI_VERTICAL);
@@ -493,9 +399,24 @@ main(int argc, char **argv)
                        break;
                case 'v':
                        flags |= NAMEI_VERTICAL;
+                       break;
+               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;