Upload Tizen:Base source
[framework/base/util-linux-ng.git] / misc-utils / namei.c
1 /*
2  * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
3  *
4  * This file is part of util-linux-ng.
5  *
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.
10  *
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.
15  *
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).
21  */
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <getopt.h>
25 #include <string.h>
26 #include <strings.h>
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <sys/param.h>
32 #include <err.h>
33 #include <pwd.h>
34 #include <grp.h>
35 #include "nls.h"
36 #include "widechar.h"
37
38 #ifndef MAXSYMLINKS
39 #define MAXSYMLINKS 256
40 #endif
41
42 #ifndef PATH_MAX
43 #define PATH_MAX 4096
44 #endif
45
46 #ifndef LOGIN_NAME_MAX
47 #define LOGIN_NAME_MAX 256
48 #endif
49
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)
55
56
57 struct namei {
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 */
63         int             level;
64 };
65
66 struct idcache {
67         unsigned long int       id;
68         char                    *name;
69         struct idcache          *next;
70 };
71
72 static int flags;
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 */
77
78 static struct idcache *
79 get_id(struct idcache *ic, unsigned long int id)
80 {
81         while(ic) {
82                 if (ic->id == id)
83                         return ic;
84                 ic = ic->next;
85         }
86         return NULL;
87 }
88
89 static void
90 free_idcache(struct idcache *ic)
91 {
92         while(ic) {
93                 struct idcache *next = ic->next;
94                 free(ic->name);
95                 free(ic);
96                 ic = next;
97         }
98 }
99
100 static void
101 add_id(struct idcache **ic, char *name, unsigned long int id, int *width)
102 {
103         struct idcache *nc, *x;
104         int w = 0;
105
106         nc = calloc(1, sizeof(*nc));
107         if (!nc)
108                 goto alloc_err;
109         nc->id = id;
110
111         if (name) {
112 #ifdef HAVE_WIDECHAR
113                 wchar_t wc[LOGIN_NAME_MAX + 1];
114
115                 if (mbstowcs(wc, name, LOGIN_NAME_MAX) > 0) {
116                         wc[LOGIN_NAME_MAX] = '\0';
117                         w = wcswidth(wc, LOGIN_NAME_MAX);
118                 }
119                 else
120 #endif
121                         w = strlen(name);
122         }
123         /* note, we ignore names with non-printable widechars */
124         if (w > 0)
125                 nc->name = strdup(name);
126         else if (asprintf(&nc->name, "%lu", id) == -1)
127                 nc->name = NULL;
128         if (!nc->name)
129                 goto alloc_err;
130
131         for (x = *ic; x && x->next; x = x->next);
132
133         /* add 'nc' at end of the 'ic' list */
134         if (x)
135                 x->next = nc;
136         else
137                 *ic = nc;
138         if (w <= 0)
139                 w = strlen(nc->name);
140         *width = *width < w ? w : *width;
141
142         return;
143 alloc_err:
144         err(EXIT_FAILURE, _("out of memory?"));
145 }
146
147 static void
148 add_uid(unsigned long int id)
149 {
150         struct idcache *ic = get_id(ucache, id);
151
152         if (!ic) {
153                 struct passwd *pw = getpwuid((uid_t) id);
154                 add_id(&ucache, pw ? pw->pw_name : NULL, id, &uwidth);
155         }
156 }
157
158 static void
159 add_gid(unsigned long int id)
160 {
161         struct idcache *ic = get_id(gcache, id);
162
163         if (!ic) {
164                 struct group *gr = getgrgid((gid_t) id);
165                 add_id(&gcache, gr ? gr->gr_name : NULL, id, &gwidth);
166         }
167 }
168
169 static void
170 free_namei(struct namei *nm)
171 {
172         while (nm) {
173                 struct namei *next = nm->next;
174                 free(nm->name);
175                 free(nm->abslink);
176                 free(nm);
177                 nm = next;
178         }
179 }
180
181 static void
182 readlink_to_namei(struct namei *nm, const char *path)
183 {
184         char sym[PATH_MAX];
185         size_t sz;
186
187         sz = readlink(path, sym, sizeof(sym));
188         if (sz < 1)
189                 err(EXIT_FAILURE, _("failed to read symlink: %s"), path);
190         if (*sym != '/') {
191                 char *p = strrchr(path, '/');
192
193                 nm->relstart = p ? p - path : strlen(path);
194                 sz += nm->relstart + 1;
195         }
196         nm->abslink = malloc(sz + 1);
197         if (!nm->abslink)
198                 err(EXIT_FAILURE, _("out of memory?"));
199
200         if (*sym != '/') {
201                 /* create the absolute path from the relative symlink */
202                 memcpy(nm->abslink, path, nm->relstart);
203                 *(nm->abslink + nm->relstart) = '/';
204                 nm->relstart++;
205                 memcpy(nm->abslink + nm->relstart, sym, sz - nm->relstart);
206         } else
207                 memcpy(nm->abslink, sym, sz);
208         nm->abslink[sz] = '\0';
209 }
210
211 static struct namei *
212 new_namei(struct namei *parent, const char *path, const char *fname, int lev)
213 {
214         struct namei *nm;
215
216         if (!fname)
217                 return NULL;
218         nm = calloc(1, sizeof(*nm));
219         if (!nm)
220                 err(EXIT_FAILURE, _("out of memory?"));
221         if (parent)
222                 parent->next = nm;
223
224         nm->level = lev;
225         nm->name = strdup(fname);
226         if (!nm->name)
227                 err(EXIT_FAILURE, _("out of memory?"));
228         if (lstat(path, &nm->st) == -1)
229                 err(EXIT_FAILURE, _("could not stat '%s'"), path);
230         return nm;
231 }
232
233 static struct namei *
234 add_namei(struct namei *parent, const char *orgpath, int start, struct namei **last)
235 {
236         struct namei *nm = NULL, *first = NULL;
237         char *fname, *end, *path;
238         int level = 0;
239
240         if (!orgpath)
241                 return NULL;
242         if (parent) {
243                 nm = parent;
244                 level = parent->level + 1;
245         }
246         path = strdup(orgpath);
247         if (!path)
248                 err(EXIT_FAILURE, _("out of memory?"));
249         fname = path + start;
250
251         /* root directory */
252         if (*fname == '/') {
253                 while (*fname == '/')
254                         fname++; /* eat extra '/' */
255                 first = nm = new_namei(nm, "/", "/", level);
256         }
257
258         for (end = fname; fname && end; ) {
259                 /* set end of filename */
260                 if (*fname) {
261                         end = strchr(fname, '/');
262                         if (end)
263                                 *end = '\0';
264
265                         /* create a new entry */
266                         nm = new_namei(nm, path, fname, level);
267                 } else
268                         end = NULL;
269                 if (!first)
270                         first = nm;
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);
276                 }
277                 /* set begin of the next filename */
278                 if (end) {
279                         *end++ = '/';
280                         while (*end == '/')
281                                 end++; /* eat extra '/' */
282                 }
283                 fname = end;
284         }
285
286         if (last)
287                 *last = nm;
288         return first;
289 }
290
291
292 static int
293 follow_symlinks(struct namei *nm)
294 {
295         int symcount = 0;
296
297         for (; nm; nm = nm->next) {
298                 struct namei *next, *last;
299
300                 if (!S_ISLNK(nm->st.st_mode))
301                         continue;
302                 if (++symcount > MAXSYMLINKS) {
303                         /* drop the rest of the list */
304                         free_namei(nm->next);
305                         nm->next = NULL;
306                         return -1;
307                 }
308                 next = nm->next;
309                 nm->next = add_namei(nm, nm->abslink, nm->relstart, &last);
310                 if (last)
311                         last->next = next;
312                 else
313                         nm->next = next;
314         }
315         return 0;
316 }
317
318 static void
319 strmode(mode_t mode, char *str)
320 {
321         if (S_ISDIR(mode))
322                 str[0] = 'd';
323         else if (S_ISLNK(mode))
324                 str[0] = 'l';
325         else if (S_ISCHR(mode))
326                 str[0] = 'c';
327         else if (S_ISBLK(mode))
328                 str[0] = 'b';
329         else if (S_ISSOCK(mode))
330                 str[0] = 's';
331         else if (S_ISFIFO(mode))
332                 str[0] = 'p';
333         else if (S_ISREG(mode))
334                 str[0] = '-';
335
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' : '-'));
351         str[10] = '\0';
352 }
353
354 static void
355 print_namei(struct namei *nm, char *path)
356 {
357         struct namei *prev = NULL;
358         int i;
359
360         if (path)
361                 printf("f: %s\n", path);
362
363         for (; nm; prev = nm, nm = nm->next) {
364                 char md[11];
365
366                 strmode(nm->st.st_mode, md);
367
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)
371                         md[0] = 'D';
372
373                 if (!(flags & NAMEI_VERTICAL)) {
374                         for (i = 0; i < nm->level; i++)
375                                 fputs("  ", stdout);
376                         fputc(' ', stdout);
377                 }
378
379                 if (flags & NAMEI_MODES)
380                         printf("%s", md);
381                 else
382                         printf("%c", md[0]);
383
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);
389                 }
390
391                 if (flags & NAMEI_VERTICAL)
392                         for (i = 0; i < nm->level; i++)
393                                 fputs("  ", stdout);
394
395                 if (S_ISLNK(nm->st.st_mode))
396                         printf(" %s -> %s\n", nm->name,
397                                         nm->abslink + nm->relstart);
398                 else
399                         printf(" %s\n", nm->name);
400         }
401 }
402
403 static void
404 usage(int rc)
405 {
406         const char *p = program_invocation_short_name;
407
408         if (!*p)
409                 p = "namei";
410
411         printf(_("\nUsage: %s [options] pathname [pathname ...]\n"), p);
412         printf(_("\nOptions:\n"));
413
414         printf(_(
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"));
422
423         printf(_("\nFor more information see namei(1).\n"));
424         exit(rc);
425 }
426
427 struct option longopts[] =
428 {
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' },
436         { NULL,         0, 0, 0 },
437 };
438
439 int
440 main(int argc, char **argv)
441 {
442         extern int optind;
443         int c;
444
445         setlocale(LC_ALL, "");
446         bindtextdomain(PACKAGE, LOCALEDIR);
447         textdomain(PACKAGE);
448
449         if (argc < 2)
450                 usage(EXIT_FAILURE);
451
452         while ((c = getopt_long(argc, argv, "+h?lmnovx", longopts, NULL)) != -1) {
453                 switch(c) {
454                 case 'h':
455                 case '?':
456                         usage(EXIT_SUCCESS);
457                         break;
458                 case 'l':
459                         flags |= (NAMEI_OWNERS | NAMEI_MODES | NAMEI_VERTICAL);
460                         break;
461                 case 'm':
462                         flags |= NAMEI_MODES;
463                         break;
464                 case 'n':
465                         flags |= NAMEI_NOLINKS;
466                         break;
467                 case 'o':
468                         flags |= NAMEI_OWNERS;
469                         break;
470                 case 'x':
471                         flags |= NAMEI_MNTS;
472                         break;
473                 case 'v':
474                         flags |= NAMEI_VERTICAL;
475                 }
476         }
477
478         for(; optind < argc; optind++) {
479                 char *path = argv[optind];
480                 struct namei *nm = NULL;
481                 struct stat st;
482
483                 if (stat(path, &st) != 0)
484                         err(EXIT_FAILURE, _("failed to stat: %s"), path);
485
486                 nm = add_namei(NULL, path, 0, NULL);
487                 if (nm) {
488                         int sml = 0;
489                         if (!(flags & NAMEI_NOLINKS))
490                                 sml = follow_symlinks(nm);
491                         print_namei(nm, path);
492                         free_namei(nm);
493                         if (sml == -1)
494                                 errx(EXIT_FAILURE,
495                                         _("%s: exceeded limit of symlinks"),
496                                         path);
497                 }
498         }
499
500         free_idcache(ucache);
501         free_idcache(gcache);
502
503         return EXIT_SUCCESS;
504 }
505