]> git.ipfire.org Git - thirdparty/util-linux.git/blob - misc-utils/namei.c
namei: gater information about / (root)
[thirdparty/util-linux.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 : 0;
194 if (nm->relstart)
195 sz += nm->relstart + 1;
196 }
197 nm->abslink = malloc(sz + 1);
198 if (!nm->abslink)
199 err(EXIT_FAILURE, _("out of memory?"));
200
201 if (*sym != '/' && nm->relstart) {
202 /* create the absolute path from the relative symlink */
203 memcpy(nm->abslink, path, nm->relstart);
204 *(nm->abslink + nm->relstart) = '/';
205 nm->relstart++;
206 memcpy(nm->abslink + nm->relstart, sym, sz - nm->relstart);
207 } else
208 memcpy(nm->abslink, sym, sz);
209 nm->abslink[sz] = '\0';
210 }
211
212 static struct namei *
213 new_namei(struct namei *parent, const char *path, const char *fname, int lev)
214 {
215 struct namei *nm;
216
217 if (!fname)
218 return NULL;
219 nm = calloc(1, sizeof(*nm));
220 if (!nm)
221 err(EXIT_FAILURE, _("out of memory?"));
222 if (parent)
223 parent->next = nm;
224
225 nm->level = lev;
226 nm->name = strdup(fname);
227 if (!nm->name)
228 err(EXIT_FAILURE, _("out of memory?"));
229 if (lstat(path, &nm->st) == -1)
230 err(EXIT_FAILURE, _("could not stat '%s'"), path);
231
232 if (S_ISLNK(nm->st.st_mode))
233 readlink_to_namei(nm, path);
234 if (flags & NAMEI_OWNERS) {
235 add_uid(nm->st.st_uid);
236 add_gid(nm->st.st_gid);
237 }
238
239 return nm;
240 }
241
242 static struct namei *
243 add_namei(struct namei *parent, const char *orgpath, int start, struct namei **last)
244 {
245 struct namei *nm = NULL, *first = NULL;
246 char *fname, *end, *path;
247 int level = 0;
248
249 if (!orgpath)
250 return NULL;
251 if (parent) {
252 nm = parent;
253 level = parent->level + 1;
254 }
255 path = strdup(orgpath);
256 if (!path)
257 err(EXIT_FAILURE, _("out of memory?"));
258 fname = path + start;
259
260 /* root directory */
261 if (*fname == '/') {
262 while (*fname == '/')
263 fname++; /* eat extra '/' */
264 first = nm = new_namei(nm, "/", "/", level);
265 }
266
267 for (end = fname; fname && end; ) {
268 /* set end of filename */
269 if (*fname) {
270 end = strchr(fname, '/');
271 if (end)
272 *end = '\0';
273
274 /* create a new entry */
275 nm = new_namei(nm, path, fname, level);
276 } else
277 end = NULL;
278 if (!first)
279 first = nm;
280 /* set begin of the next filename */
281 if (end) {
282 *end++ = '/';
283 while (*end == '/')
284 end++; /* eat extra '/' */
285 }
286 fname = end;
287 }
288
289 if (last)
290 *last = nm;
291 return first;
292 }
293
294
295 static int
296 follow_symlinks(struct namei *nm)
297 {
298 int symcount = 0;
299
300 for (; nm; nm = nm->next) {
301 struct namei *next, *last;
302
303 if (!S_ISLNK(nm->st.st_mode))
304 continue;
305 if (++symcount > MAXSYMLINKS) {
306 /* drop the rest of the list */
307 free_namei(nm->next);
308 nm->next = NULL;
309 return -1;
310 }
311 next = nm->next;
312 nm->next = add_namei(nm, nm->abslink, nm->relstart, &last);
313 if (last)
314 last->next = next;
315 else
316 nm->next = next;
317 }
318 return 0;
319 }
320
321 static void
322 strmode(mode_t mode, char *str)
323 {
324 if (S_ISDIR(mode))
325 str[0] = 'd';
326 else if (S_ISLNK(mode))
327 str[0] = 'l';
328 else if (S_ISCHR(mode))
329 str[0] = 'c';
330 else if (S_ISBLK(mode))
331 str[0] = 'b';
332 else if (S_ISSOCK(mode))
333 str[0] = 's';
334 else if (S_ISFIFO(mode))
335 str[0] = 'p';
336 else if (S_ISREG(mode))
337 str[0] = '-';
338
339 str[1] = mode & S_IRUSR ? 'r' : '-';
340 str[2] = mode & S_IWUSR ? 'w' : '-';
341 str[3] = (mode & S_ISUID
342 ? (mode & S_IXUSR ? 's' : 'S')
343 : (mode & S_IXUSR ? 'x' : '-'));
344 str[4] = mode & S_IRGRP ? 'r' : '-';
345 str[5] = mode & S_IWGRP ? 'w' : '-';
346 str[6] = (mode & S_ISGID
347 ? (mode & S_IXGRP ? 's' : 'S')
348 : (mode & S_IXGRP ? 'x' : '-'));
349 str[7] = mode & S_IROTH ? 'r' : '-';
350 str[8] = mode & S_IWOTH ? 'w' : '-';
351 str[9] = (mode & S_ISVTX
352 ? (mode & S_IXOTH ? 't' : 'T')
353 : (mode & S_IXOTH ? 'x' : '-'));
354 str[10] = '\0';
355 }
356
357 static void
358 print_namei(struct namei *nm, char *path)
359 {
360 struct namei *prev = NULL;
361 int i;
362
363 if (path)
364 printf("f: %s\n", path);
365
366 for (; nm; prev = nm, nm = nm->next) {
367 char md[11];
368
369 strmode(nm->st.st_mode, md);
370
371 if ((flags & NAMEI_MNTS) && prev &&
372 S_ISDIR(nm->st.st_mode) && S_ISDIR(prev->st.st_mode) &&
373 prev->st.st_dev != nm->st.st_dev)
374 md[0] = 'D';
375
376 if (!(flags & NAMEI_VERTICAL)) {
377 for (i = 0; i < nm->level; i++)
378 fputs(" ", stdout);
379 fputc(' ', stdout);
380 }
381
382 if (flags & NAMEI_MODES)
383 printf("%s", md);
384 else
385 printf("%c", md[0]);
386
387 if (flags & NAMEI_OWNERS) {
388 printf(" %-*s", uwidth,
389 get_id(ucache, nm->st.st_uid)->name);
390 printf(" %-*s", gwidth,
391 get_id(gcache, nm->st.st_gid)->name);
392 }
393
394 if (flags & NAMEI_VERTICAL)
395 for (i = 0; i < nm->level; i++)
396 fputs(" ", stdout);
397
398 if (S_ISLNK(nm->st.st_mode))
399 printf(" %s -> %s\n", nm->name,
400 nm->abslink + nm->relstart);
401 else
402 printf(" %s\n", nm->name);
403 }
404 }
405
406 static void
407 usage(int rc)
408 {
409 const char *p = program_invocation_short_name;
410
411 if (!*p)
412 p = "namei";
413
414 printf(_("\nUsage: %s [options] pathname [pathname ...]\n"), p);
415 printf(_("\nOptions:\n"));
416
417 printf(_(
418 " -h, --help displays this help text\n"
419 " -x, --mountpoints show mount point directories with a 'D'\n"
420 " -m, --modes show the mode bits of each file\n"
421 " -o, --owners show owner and group name of each file\n"
422 " -l, --long use a long listing format (-m -o -v) \n"
423 " -n, --nosymlinks don't follow symlinks\n"
424 " -v, --vertical vertical align of modes and owners\n"));
425
426 printf(_("\nFor more information see namei(1).\n"));
427 exit(rc);
428 }
429
430 struct option longopts[] =
431 {
432 { "help", 0, 0, 'h' },
433 { "mountpoints",0, 0, 'x' },
434 { "modes", 0, 0, 'm' },
435 { "owners", 0, 0, 'o' },
436 { "long", 0, 0, 'l' },
437 { "nolinks", 0, 0, 'n' },
438 { "vertical", 0, 0, 'v' },
439 { NULL, 0, 0, 0 },
440 };
441
442 int
443 main(int argc, char **argv)
444 {
445 extern int optind;
446 int c;
447
448 setlocale(LC_ALL, "");
449 bindtextdomain(PACKAGE, LOCALEDIR);
450 textdomain(PACKAGE);
451
452 if (argc < 2)
453 usage(EXIT_FAILURE);
454
455 while ((c = getopt_long(argc, argv, "+h?lmnovx", longopts, NULL)) != -1) {
456 switch(c) {
457 case 'h':
458 case '?':
459 usage(EXIT_SUCCESS);
460 break;
461 case 'l':
462 flags |= (NAMEI_OWNERS | NAMEI_MODES | NAMEI_VERTICAL);
463 break;
464 case 'm':
465 flags |= NAMEI_MODES;
466 break;
467 case 'n':
468 flags |= NAMEI_NOLINKS;
469 break;
470 case 'o':
471 flags |= NAMEI_OWNERS;
472 break;
473 case 'x':
474 flags |= NAMEI_MNTS;
475 break;
476 case 'v':
477 flags |= NAMEI_VERTICAL;
478 }
479 }
480
481 for(; optind < argc; optind++) {
482 char *path = argv[optind];
483 struct namei *nm = NULL;
484 struct stat st;
485
486 if (stat(path, &st) != 0)
487 err(EXIT_FAILURE, _("failed to stat: %s"), path);
488
489 nm = add_namei(NULL, path, 0, NULL);
490 if (nm) {
491 int sml = 0;
492 if (!(flags & NAMEI_NOLINKS))
493 sml = follow_symlinks(nm);
494 print_namei(nm, path);
495 free_namei(nm);
496 if (sml == -1)
497 errx(EXIT_FAILURE,
498 _("%s: exceeded limit of symlinks"),
499 path);
500 }
501 }
502
503 free_idcache(ucache);
504 free_idcache(gcache);
505
506 return EXIT_SUCCESS;
507 }
508