]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/namei.c
textual: use manual tail usage() macro
[thirdparty/util-linux.git] / misc-utils / namei.c
CommitLineData
c84a633a
KZ
1/*
2 * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
3 *
601d12fb 4 * This file is part of util-linux.
c84a633a
KZ
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 *
b50945d4 16 * The original namei(1) was written by:
c84a633a
KZ
17 * Roger S. Southwick (May 2, 1990)
18 * Steve Tell (March 28, 1991)
b50945d4 19 * Arkadiusz Miƛkiewicz (1999-02-22)
c84a633a
KZ
20 * Li Zefan (2007-09-10).
21 */
87f3feac 22
6dbe3af9 23#include <stdio.h>
fd6b7a7f 24#include <unistd.h>
c84a633a 25#include <getopt.h>
fd6b7a7f 26#include <string.h>
66ee8158 27#include <stdlib.h>
22853e4a 28#include <errno.h>
6dbe3af9
KZ
29#include <sys/types.h>
30#include <sys/stat.h>
31#include <sys/param.h>
4cfcf09b
KZ
32#include <pwd.h>
33#include <grp.h>
fd03d92d
KZ
34
35#include "c.h"
87f3feac 36#include "xalloc.h"
7eda085c 37#include "nls.h"
4cfcf09b 38#include "widechar.h"
ce877f2d 39#include "strutils.h"
c05a80ca 40#include "closestream.h"
6dbe3af9 41
6dbe3af9
KZ
42#ifndef MAXSYMLINKS
43#define MAXSYMLINKS 256
44#endif
45
4cfcf09b
KZ
46#ifndef LOGIN_NAME_MAX
47#define LOGIN_NAME_MAX 256
48#endif
49
c84a633a
KZ
50#define NAMEI_NOLINKS (1 << 1)
51#define NAMEI_MODES (1 << 2)
52#define NAMEI_MNTS (1 << 3)
4cfcf09b 53#define NAMEI_OWNERS (1 << 4)
c3ecdb3e 54#define NAMEI_VERTICAL (1 << 5)
6dbe3af9 55
db8a3e23 56
c84a633a
KZ
57struct 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;
c21e7023 64 int mountpoint; /* is mount point */
c49e31f4 65 int noent; /* is this item not existing */
c84a633a 66};
6dbe3af9 67
4cfcf09b
KZ
68struct idcache {
69 unsigned long int id;
70 char *name;
71 struct idcache *next;
72};
73
74static int flags;
75static int uwidth; /* maximal width of username */
76static int gwidth; /* maximal width of groupname */
77static struct idcache *gcache; /* groupnames */
78static struct idcache *ucache; /* usernames */
79
80static struct idcache *
81get_id(struct idcache *ic, unsigned long int id)
82{
83 while(ic) {
84 if (ic->id == id)
85 return ic;
86 ic = ic->next;
87 }
88 return NULL;
89}
90
91static void
92free_idcache(struct idcache *ic)
93{
94 while(ic) {
95 struct idcache *next = ic->next;
96 free(ic->name);
97 free(ic);
98 ic = next;
99 }
100}
101
102static void
103add_id(struct idcache **ic, char *name, unsigned long int id, int *width)
104{
105 struct idcache *nc, *x;
106 int w = 0;
107
b9779517 108 nc = xcalloc(1, sizeof(*nc));
4cfcf09b
KZ
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)
b9779517 125 nc->name = xstrdup(name);
7a591a1f
KZ
126 else
127 xasprintf(&nc->name, "%lu", id);
4cfcf09b
KZ
128
129 for (x = *ic; x && x->next; x = x->next);
130
131 /* add 'nc' at end of the 'ic' list */
132 if (x)
133 x->next = nc;
134 else
135 *ic = nc;
136 if (w <= 0)
54b6adf3 137 w = nc->name ? strlen(nc->name) : 0;
4cfcf09b 138
54b6adf3 139 *width = *width < w ? w : *width;
4cfcf09b 140 return;
4cfcf09b
KZ
141}
142
143static void
144add_uid(unsigned long int id)
145{
146 struct idcache *ic = get_id(ucache, id);
147
148 if (!ic) {
149 struct passwd *pw = getpwuid((uid_t) id);
150 add_id(&ucache, pw ? pw->pw_name : NULL, id, &uwidth);
151 }
152}
153
154static void
155add_gid(unsigned long int id)
156{
157 struct idcache *ic = get_id(gcache, id);
158
159 if (!ic) {
160 struct group *gr = getgrgid((gid_t) id);
161 add_id(&gcache, gr ? gr->gr_name : NULL, id, &gwidth);
162 }
163}
164
c84a633a
KZ
165static void
166free_namei(struct namei *nm)
167{
168 while (nm) {
169 struct namei *next = nm->next;
170 free(nm->name);
171 free(nm->abslink);
172 free(nm);
173 nm = next;
6dbe3af9 174 }
6dbe3af9
KZ
175}
176
22853e4a 177static void
c84a633a
KZ
178readlink_to_namei(struct namei *nm, const char *path)
179{
180 char sym[PATH_MAX];
eb9a65bb 181 ssize_t sz;
89394013 182 int isrel = 0;
c84a633a
KZ
183
184 sz = readlink(path, sym, sizeof(sym));
185 if (sz < 1)
186 err(EXIT_FAILURE, _("failed to read symlink: %s"), path);
187 if (*sym != '/') {
188 char *p = strrchr(path, '/');
189
89394013
KZ
190 if (p) {
191 isrel = 1;
38efd1e2 192 nm->relstart = p - path;
d1dca710 193 sz += nm->relstart + 1;
89394013 194 }
c84a633a 195 }
87f3feac 196 nm->abslink = xmalloc(sz + 1);
c84a633a 197
89394013 198 if (*sym != '/' && isrel) {
f7ed29a7 199 /* create the absolute path from the relative symlink */
c84a633a
KZ
200 memcpy(nm->abslink, path, nm->relstart);
201 *(nm->abslink + nm->relstart) = '/';
202 nm->relstart++;
f7ed29a7 203 memcpy(nm->abslink + nm->relstart, sym, sz - nm->relstart);
c84a633a 204 } else
89394013
KZ
205 /* - absolute link (foo -> /path/bar)
206 * - or link without any subdir (foo -> bar)
207 */
c84a633a 208 memcpy(nm->abslink, sym, sz);
89394013 209
c84a633a 210 nm->abslink[sz] = '\0';
6dbe3af9
KZ
211}
212
c21e7023
KZ
213static struct stat *
214dotdot_stat(const char *dirname, struct stat *st)
215{
216 char *path;
217 size_t len;
218
219#define DOTDOTDIR "/.."
220
221 if (!dirname)
222 return NULL;
223
224 len = strlen(dirname);
b9779517 225 path = xmalloc(len + sizeof(DOTDOTDIR));
c21e7023
KZ
226
227 memcpy(path, dirname, len);
228 memcpy(path + len, DOTDOTDIR, sizeof(DOTDOTDIR));
229
230 if (stat(path, st))
add1b8af 231 err(EXIT_FAILURE, _("stat failed %s"), path);
c21e7023
KZ
232 free(path);
233 return st;
234}
235
c84a633a
KZ
236static struct namei *
237new_namei(struct namei *parent, const char *path, const char *fname, int lev)
238{
239 struct namei *nm;
240
241 if (!fname)
242 return NULL;
b9779517 243 nm = xcalloc(1, sizeof(*nm));
c84a633a
KZ
244 if (parent)
245 parent->next = nm;
246
247 nm->level = lev;
b9779517 248 nm->name = xstrdup(fname);
c49e31f4
SK
249
250 nm->noent = (lstat(path, &nm->st) == -1);
251 if (nm->noent)
252 return nm;
a439d194
KZ
253
254 if (S_ISLNK(nm->st.st_mode))
255 readlink_to_namei(nm, path);
256 if (flags & NAMEI_OWNERS) {
257 add_uid(nm->st.st_uid);
258 add_gid(nm->st.st_gid);
259 }
260
c21e7023
KZ
261 if ((flags & NAMEI_MNTS) && S_ISDIR(nm->st.st_mode)) {
262 struct stat stbuf, *sb = NULL;
263
264 if (parent && S_ISDIR(parent->st.st_mode))
265 sb = &parent->st;
266 else if (!parent || S_ISLNK(parent->st.st_mode))
267 sb = dotdot_stat(path, &stbuf);
268
269 if (sb && (sb->st_dev != nm->st.st_dev || /* different device */
270 sb->st_ino == nm->st.st_ino)) /* root directory */
271 nm->mountpoint = 1;
272 }
273
c84a633a
KZ
274 return nm;
275}
6dbe3af9 276
c84a633a
KZ
277static struct namei *
278add_namei(struct namei *parent, const char *orgpath, int start, struct namei **last)
279{
280 struct namei *nm = NULL, *first = NULL;
281 char *fname, *end, *path;
282 int level = 0;
283
284 if (!orgpath)
285 return NULL;
286 if (parent) {
287 nm = parent;
288 level = parent->level + 1;
6dbe3af9 289 }
b9779517 290 path = xstrdup(orgpath);
c84a633a
KZ
291 fname = path + start;
292
293 /* root directory */
294 if (*fname == '/') {
295 while (*fname == '/')
296 fname++; /* eat extra '/' */
297 first = nm = new_namei(nm, "/", "/", level);
6dbe3af9 298 }
6dbe3af9 299
c84a633a
KZ
300 for (end = fname; fname && end; ) {
301 /* set end of filename */
922a121b
KZ
302 if (*fname) {
303 end = strchr(fname, '/');
304 if (end)
305 *end = '\0';
306
307 /* create a new entry */
308 nm = new_namei(nm, path, fname, level);
309 } else
310 end = NULL;
c84a633a
KZ
311 if (!first)
312 first = nm;
c84a633a
KZ
313 /* set begin of the next filename */
314 if (end) {
315 *end++ = '/';
316 while (*end == '/')
317 end++; /* eat extra '/' */
318 }
319 fname = end;
e8f26419
KZ
320 }
321
c84a633a
KZ
322 if (last)
323 *last = nm;
bf13602c
DB
324
325 free(path);
326
c84a633a
KZ
327 return first;
328}
6dbe3af9 329
c84a633a
KZ
330static int
331follow_symlinks(struct namei *nm)
332{
333 int symcount = 0;
6dbe3af9 334
c84a633a
KZ
335 for (; nm; nm = nm->next) {
336 struct namei *next, *last;
6dbe3af9 337
c49e31f4
SK
338 if (nm->noent)
339 continue;
c84a633a
KZ
340 if (!S_ISLNK(nm->st.st_mode))
341 continue;
342 if (++symcount > MAXSYMLINKS) {
343 /* drop the rest of the list */
344 free_namei(nm->next);
345 nm->next = NULL;
346 return -1;
347 }
348 next = nm->next;
349 nm->next = add_namei(nm, nm->abslink, nm->relstart, &last);
350 if (last)
351 last->next = next;
352 else
353 nm->next = next;
d8ef4c19 354 }
c84a633a
KZ
355 return 0;
356}
d8ef4c19 357
c49e31f4 358static int
c84a633a
KZ
359print_namei(struct namei *nm, char *path)
360{
c84a633a 361 int i;
d8ef4c19 362
c84a633a
KZ
363 if (path)
364 printf("f: %s\n", path);
6dbe3af9 365
8fec558b 366 for (; nm; nm = nm->next) {
c84a633a 367 char md[11];
db8a3e23 368
c49e31f4 369 if (nm->noent) {
cd804450
SK
370 int blanks = 1;
371 if (flags & NAMEI_MODES)
372 blanks += 9;
373 if (flags & NAMEI_OWNERS)
374 blanks += uwidth + gwidth + 2;
375 if (!(flags & NAMEI_VERTICAL))
376 blanks += 1;
377 blanks += nm->level * 2;
378 printf("%*s ", blanks, "");
c49e31f4
SK
379 printf(_("%s - No such file or directory\n"), nm->name);
380 return -1;
381 }
382
c84a633a 383 strmode(nm->st.st_mode, md);
6dbe3af9 384
c21e7023 385 if (nm->mountpoint)
c84a633a 386 md[0] = 'D';
6dbe3af9 387
c3ecdb3e
KZ
388 if (!(flags & NAMEI_VERTICAL)) {
389 for (i = 0; i < nm->level; i++)
390 fputs(" ", stdout);
391 fputc(' ', stdout);
392 }
6dbe3af9 393
c84a633a 394 if (flags & NAMEI_MODES)
c3ecdb3e 395 printf("%s", md);
6dbe3af9 396 else
c3ecdb3e 397 printf("%c", md[0]);
db8a3e23 398
4cfcf09b
KZ
399 if (flags & NAMEI_OWNERS) {
400 printf(" %-*s", uwidth,
401 get_id(ucache, nm->st.st_uid)->name);
402 printf(" %-*s", gwidth,
403 get_id(gcache, nm->st.st_gid)->name);
404 }
c3ecdb3e
KZ
405
406 if (flags & NAMEI_VERTICAL)
407 for (i = 0; i < nm->level; i++)
408 fputs(" ", stdout);
409
c84a633a
KZ
410 if (S_ISLNK(nm->st.st_mode))
411 printf(" %s -> %s\n", nm->name,
412 nm->abslink + nm->relstart);
6dbe3af9 413 else
c84a633a
KZ
414 printf(" %s\n", nm->name);
415 }
c49e31f4 416 return 0;
c84a633a 417}
db8a3e23 418
54f07289 419static void usage(int rc)
c84a633a
KZ
420{
421 const char *p = program_invocation_short_name;
54f07289 422 FILE *out = rc == EXIT_FAILURE ? stderr : stdout;
6dbe3af9 423
c84a633a
KZ
424 if (!*p)
425 p = "namei";
f062c8a6 426
54f07289
KZ
427 fputs(_("\nUsage:\n"), out);
428 fprintf(out,
09af3db4 429 _(" %s [options] <pathname>...\n"), p);
54f07289
KZ
430
431 fputs(_("\nOptions:\n"), out);
432 fputs(_(" -h, --help displays this help text\n"
433 " -V, --version output version information and exit\n"
434 " -x, --mountpoints show mount point directories with a 'D'\n"
435 " -m, --modes show the mode bits of each file\n"
436 " -o, --owners show owner and group name of each file\n"
437 " -l, --long use a long listing format (-m -o -v) \n"
438 " -n, --nosymlinks don't follow symlinks\n"
439 " -v, --vertical vertical align of modes and owners\n"), out);
440
a587cc55 441 fprintf(out, USAGE_MAN_TAIL("namei(1)"));
c84a633a 442 exit(rc);
6dbe3af9
KZ
443}
444
6c7d5ae9 445static const struct option longopts[] =
c84a633a
KZ
446{
447 { "help", 0, 0, 'h' },
64718fe7 448 { "version", 0, 0, 'V' },
c84a633a
KZ
449 { "mountpoints",0, 0, 'x' },
450 { "modes", 0, 0, 'm' },
4cfcf09b
KZ
451 { "owners", 0, 0, 'o' },
452 { "long", 0, 0, 'l' },
c84a633a 453 { "nolinks", 0, 0, 'n' },
c3ecdb3e 454 { "vertical", 0, 0, 'v' },
c84a633a
KZ
455 { NULL, 0, 0, 0 },
456};
6dbe3af9 457
c84a633a
KZ
458int
459main(int argc, char **argv)
460{
c84a633a 461 int c;
c49e31f4 462 int rc = EXIT_SUCCESS;
c84a633a
KZ
463
464 setlocale(LC_ALL, "");
465 bindtextdomain(PACKAGE, LOCALEDIR);
466 textdomain(PACKAGE);
c05a80ca 467 atexit(close_stdout);
c84a633a 468
5a0d9255 469 while ((c = getopt_long(argc, argv, "hVlmnovx", longopts, NULL)) != -1) {
c84a633a
KZ
470 switch(c) {
471 case 'h':
c84a633a
KZ
472 usage(EXIT_SUCCESS);
473 break;
64718fe7 474 case 'V':
e421313d 475 printf(UTIL_LINUX_VERSION);
64718fe7 476 return EXIT_SUCCESS;
4cfcf09b 477 case 'l':
c3ecdb3e 478 flags |= (NAMEI_OWNERS | NAMEI_MODES | NAMEI_VERTICAL);
4cfcf09b 479 break;
c84a633a
KZ
480 case 'm':
481 flags |= NAMEI_MODES;
482 break;
c84a633a
KZ
483 case 'n':
484 flags |= NAMEI_NOLINKS;
485 break;
4cfcf09b
KZ
486 case 'o':
487 flags |= NAMEI_OWNERS;
488 break;
489 case 'x':
490 flags |= NAMEI_MNTS;
491 break;
c3ecdb3e
KZ
492 case 'v':
493 flags |= NAMEI_VERTICAL;
5a0d9255
SK
494 break;
495 default:
496 usage(EXIT_FAILURE);
c84a633a 497 }
6dbe3af9 498 }
c84a633a 499
5a0d9255
SK
500 if (optind == argc) {
501 warnx(_("pathname argument is missing"));
502 usage(EXIT_FAILURE);
503 }
504
c84a633a
KZ
505 for(; optind < argc; optind++) {
506 char *path = argv[optind];
507 struct namei *nm = NULL;
508 struct stat st;
509
510 if (stat(path, &st) != 0)
c49e31f4 511 rc = EXIT_FAILURE;
c84a633a
KZ
512
513 nm = add_namei(NULL, path, 0, NULL);
514 if (nm) {
515 int sml = 0;
c84a633a
KZ
516 if (!(flags & NAMEI_NOLINKS))
517 sml = follow_symlinks(nm);
c49e31f4
SK
518 if (print_namei(nm, path)) {
519 rc = EXIT_FAILURE;
520 continue;
521 }
c84a633a 522 free_namei(nm);
c49e31f4
SK
523 if (sml == -1) {
524 rc = EXIT_FAILURE;
525 warnx(_("%s: exceeded limit of symlinks"), path);
526 continue;
527 }
c84a633a 528 }
6dbe3af9
KZ
529 }
530
4cfcf09b
KZ
531 free_idcache(ucache);
532 free_idcache(gcache);
533
c49e31f4 534 return rc;
6dbe3af9
KZ
535}
536