]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/namei.c
mcookie: cleanup usage()
[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 *
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 */
87f3feac 22
6dbe3af9 23#include <stdio.h>
fd6b7a7f 24#include <unistd.h>
c84a633a 25#include <getopt.h>
fd6b7a7f 26#include <string.h>
2cccd0ff 27#include <strings.h>
66ee8158 28#include <stdlib.h>
22853e4a 29#include <errno.h>
6dbe3af9
KZ
30#include <sys/types.h>
31#include <sys/stat.h>
32#include <sys/param.h>
4cfcf09b
KZ
33#include <pwd.h>
34#include <grp.h>
fd03d92d
KZ
35
36#include "c.h"
87f3feac 37#include "xalloc.h"
7eda085c 38#include "nls.h"
4cfcf09b 39#include "widechar.h"
ce877f2d 40#include "strutils.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);
4cfcf09b
KZ
126 else if (asprintf(&nc->name, "%lu", id) == -1)
127 nc->name = NULL;
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)
137 w = strlen(nc->name);
138 *width = *width < w ? w : *width;
139
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;
c84a633a
KZ
182
183 sz = readlink(path, sym, sizeof(sym));
184 if (sz < 1)
185 err(EXIT_FAILURE, _("failed to read symlink: %s"), path);
186 if (*sym != '/') {
187 char *p = strrchr(path, '/');
188
d1dca710
KZ
189 nm->relstart = p ? p - path : 0;
190 if (nm->relstart)
191 sz += nm->relstart + 1;
c84a633a 192 }
87f3feac 193 nm->abslink = xmalloc(sz + 1);
c84a633a 194
d1dca710 195 if (*sym != '/' && nm->relstart) {
f7ed29a7 196 /* create the absolute path from the relative symlink */
c84a633a
KZ
197 memcpy(nm->abslink, path, nm->relstart);
198 *(nm->abslink + nm->relstart) = '/';
199 nm->relstart++;
f7ed29a7 200 memcpy(nm->abslink + nm->relstart, sym, sz - nm->relstart);
c84a633a
KZ
201 } else
202 memcpy(nm->abslink, sym, sz);
203 nm->abslink[sz] = '\0';
6dbe3af9
KZ
204}
205
c21e7023
KZ
206static struct stat *
207dotdot_stat(const char *dirname, struct stat *st)
208{
209 char *path;
210 size_t len;
211
212#define DOTDOTDIR "/.."
213
214 if (!dirname)
215 return NULL;
216
217 len = strlen(dirname);
b9779517 218 path = xmalloc(len + sizeof(DOTDOTDIR));
c21e7023
KZ
219
220 memcpy(path, dirname, len);
221 memcpy(path + len, DOTDOTDIR, sizeof(DOTDOTDIR));
222
223 if (stat(path, st))
224 err(EXIT_FAILURE, _("could not stat '%s'"), path);
225 free(path);
226 return st;
227}
228
c84a633a
KZ
229static struct namei *
230new_namei(struct namei *parent, const char *path, const char *fname, int lev)
231{
232 struct namei *nm;
233
234 if (!fname)
235 return NULL;
b9779517 236 nm = xcalloc(1, sizeof(*nm));
c84a633a
KZ
237 if (parent)
238 parent->next = nm;
239
240 nm->level = lev;
b9779517 241 nm->name = xstrdup(fname);
c49e31f4
SK
242
243 nm->noent = (lstat(path, &nm->st) == -1);
244 if (nm->noent)
245 return nm;
a439d194
KZ
246
247 if (S_ISLNK(nm->st.st_mode))
248 readlink_to_namei(nm, path);
249 if (flags & NAMEI_OWNERS) {
250 add_uid(nm->st.st_uid);
251 add_gid(nm->st.st_gid);
252 }
253
c21e7023
KZ
254 if ((flags & NAMEI_MNTS) && S_ISDIR(nm->st.st_mode)) {
255 struct stat stbuf, *sb = NULL;
256
257 if (parent && S_ISDIR(parent->st.st_mode))
258 sb = &parent->st;
259 else if (!parent || S_ISLNK(parent->st.st_mode))
260 sb = dotdot_stat(path, &stbuf);
261
262 if (sb && (sb->st_dev != nm->st.st_dev || /* different device */
263 sb->st_ino == nm->st.st_ino)) /* root directory */
264 nm->mountpoint = 1;
265 }
266
c84a633a
KZ
267 return nm;
268}
6dbe3af9 269
c84a633a
KZ
270static struct namei *
271add_namei(struct namei *parent, const char *orgpath, int start, struct namei **last)
272{
273 struct namei *nm = NULL, *first = NULL;
274 char *fname, *end, *path;
275 int level = 0;
276
277 if (!orgpath)
278 return NULL;
279 if (parent) {
280 nm = parent;
281 level = parent->level + 1;
6dbe3af9 282 }
b9779517 283 path = xstrdup(orgpath);
c84a633a
KZ
284 fname = path + start;
285
286 /* root directory */
287 if (*fname == '/') {
288 while (*fname == '/')
289 fname++; /* eat extra '/' */
290 first = nm = new_namei(nm, "/", "/", level);
6dbe3af9 291 }
6dbe3af9 292
c84a633a
KZ
293 for (end = fname; fname && end; ) {
294 /* set end of filename */
922a121b
KZ
295 if (*fname) {
296 end = strchr(fname, '/');
297 if (end)
298 *end = '\0';
299
300 /* create a new entry */
301 nm = new_namei(nm, path, fname, level);
302 } else
303 end = NULL;
c84a633a
KZ
304 if (!first)
305 first = nm;
c84a633a
KZ
306 /* set begin of the next filename */
307 if (end) {
308 *end++ = '/';
309 while (*end == '/')
310 end++; /* eat extra '/' */
311 }
312 fname = end;
e8f26419
KZ
313 }
314
c84a633a
KZ
315 if (last)
316 *last = nm;
bf13602c
DB
317
318 free(path);
319
c84a633a
KZ
320 return first;
321}
6dbe3af9 322
c84a633a
KZ
323static int
324follow_symlinks(struct namei *nm)
325{
326 int symcount = 0;
6dbe3af9 327
c84a633a
KZ
328 for (; nm; nm = nm->next) {
329 struct namei *next, *last;
6dbe3af9 330
c49e31f4
SK
331 if (nm->noent)
332 continue;
c84a633a
KZ
333 if (!S_ISLNK(nm->st.st_mode))
334 continue;
335 if (++symcount > MAXSYMLINKS) {
336 /* drop the rest of the list */
337 free_namei(nm->next);
338 nm->next = NULL;
339 return -1;
340 }
341 next = nm->next;
342 nm->next = add_namei(nm, nm->abslink, nm->relstart, &last);
343 if (last)
344 last->next = next;
345 else
346 nm->next = next;
d8ef4c19 347 }
c84a633a
KZ
348 return 0;
349}
d8ef4c19 350
c49e31f4 351static int
c84a633a
KZ
352print_namei(struct namei *nm, char *path)
353{
c84a633a 354 int i;
d8ef4c19 355
c84a633a
KZ
356 if (path)
357 printf("f: %s\n", path);
6dbe3af9 358
8fec558b 359 for (; nm; nm = nm->next) {
c84a633a 360 char md[11];
db8a3e23 361
c49e31f4
SK
362 if (nm->noent) {
363 printf(_("%s - No such file or directory\n"), nm->name);
364 return -1;
365 }
366
c84a633a 367 strmode(nm->st.st_mode, md);
6dbe3af9 368
c21e7023 369 if (nm->mountpoint)
c84a633a 370 md[0] = 'D';
6dbe3af9 371
c3ecdb3e
KZ
372 if (!(flags & NAMEI_VERTICAL)) {
373 for (i = 0; i < nm->level; i++)
374 fputs(" ", stdout);
375 fputc(' ', stdout);
376 }
6dbe3af9 377
c84a633a 378 if (flags & NAMEI_MODES)
c3ecdb3e 379 printf("%s", md);
6dbe3af9 380 else
c3ecdb3e 381 printf("%c", md[0]);
db8a3e23 382
4cfcf09b
KZ
383 if (flags & NAMEI_OWNERS) {
384 printf(" %-*s", uwidth,
385 get_id(ucache, nm->st.st_uid)->name);
386 printf(" %-*s", gwidth,
387 get_id(gcache, nm->st.st_gid)->name);
388 }
c3ecdb3e
KZ
389
390 if (flags & NAMEI_VERTICAL)
391 for (i = 0; i < nm->level; i++)
392 fputs(" ", stdout);
393
c84a633a
KZ
394 if (S_ISLNK(nm->st.st_mode))
395 printf(" %s -> %s\n", nm->name,
396 nm->abslink + nm->relstart);
6dbe3af9 397 else
c84a633a
KZ
398 printf(" %s\n", nm->name);
399 }
c49e31f4 400 return 0;
c84a633a 401}
db8a3e23 402
c84a633a
KZ
403static void
404usage(int rc)
405{
406 const char *p = program_invocation_short_name;
6dbe3af9 407
c84a633a
KZ
408 if (!*p)
409 p = "namei";
f062c8a6 410
c84a633a
KZ
411 printf(_("\nUsage: %s [options] pathname [pathname ...]\n"), p);
412 printf(_("\nOptions:\n"));
db8a3e23 413
c84a633a
KZ
414 printf(_(
415 " -h, --help displays this help text\n"
64718fe7 416 " -V, --version output version information and exit\n"
c84a633a
KZ
417 " -x, --mountpoints show mount point directories with a 'D'\n"
418 " -m, --modes show the mode bits of each file\n"
4cfcf09b 419 " -o, --owners show owner and group name of each file\n"
c3ecdb3e
KZ
420 " -l, --long use a long listing format (-m -o -v) \n"
421 " -n, --nosymlinks don't follow symlinks\n"
422 " -v, --vertical vertical align of modes and owners\n"));
db8a3e23 423
c84a633a
KZ
424 printf(_("\nFor more information see namei(1).\n"));
425 exit(rc);
6dbe3af9
KZ
426}
427
6c7d5ae9 428static const struct option longopts[] =
c84a633a
KZ
429{
430 { "help", 0, 0, 'h' },
64718fe7 431 { "version", 0, 0, 'V' },
c84a633a
KZ
432 { "mountpoints",0, 0, 'x' },
433 { "modes", 0, 0, 'm' },
4cfcf09b
KZ
434 { "owners", 0, 0, 'o' },
435 { "long", 0, 0, 'l' },
c84a633a 436 { "nolinks", 0, 0, 'n' },
c3ecdb3e 437 { "vertical", 0, 0, 'v' },
c84a633a
KZ
438 { NULL, 0, 0, 0 },
439};
6dbe3af9 440
c84a633a
KZ
441int
442main(int argc, char **argv)
443{
c84a633a 444 int c;
c49e31f4 445 int rc = EXIT_SUCCESS;
c84a633a
KZ
446
447 setlocale(LC_ALL, "");
448 bindtextdomain(PACKAGE, LOCALEDIR);
449 textdomain(PACKAGE);
450
5a0d9255 451 while ((c = getopt_long(argc, argv, "hVlmnovx", longopts, NULL)) != -1) {
c84a633a
KZ
452 switch(c) {
453 case 'h':
c84a633a
KZ
454 usage(EXIT_SUCCESS);
455 break;
64718fe7
SK
456 case 'V':
457 printf(_("%s from %s\n"), program_invocation_short_name,
458 PACKAGE_STRING);
459 return EXIT_SUCCESS;
4cfcf09b 460 case 'l':
c3ecdb3e 461 flags |= (NAMEI_OWNERS | NAMEI_MODES | NAMEI_VERTICAL);
4cfcf09b 462 break;
c84a633a
KZ
463 case 'm':
464 flags |= NAMEI_MODES;
465 break;
c84a633a
KZ
466 case 'n':
467 flags |= NAMEI_NOLINKS;
468 break;
4cfcf09b
KZ
469 case 'o':
470 flags |= NAMEI_OWNERS;
471 break;
472 case 'x':
473 flags |= NAMEI_MNTS;
474 break;
c3ecdb3e
KZ
475 case 'v':
476 flags |= NAMEI_VERTICAL;
5a0d9255
SK
477 break;
478 default:
479 usage(EXIT_FAILURE);
c84a633a 480 }
6dbe3af9 481 }
c84a633a 482
5a0d9255
SK
483 if (optind == argc) {
484 warnx(_("pathname argument is missing"));
485 usage(EXIT_FAILURE);
486 }
487
c84a633a
KZ
488 for(; optind < argc; optind++) {
489 char *path = argv[optind];
490 struct namei *nm = NULL;
491 struct stat st;
492
493 if (stat(path, &st) != 0)
c49e31f4 494 rc = EXIT_FAILURE;
c84a633a
KZ
495
496 nm = add_namei(NULL, path, 0, NULL);
497 if (nm) {
498 int sml = 0;
c84a633a
KZ
499 if (!(flags & NAMEI_NOLINKS))
500 sml = follow_symlinks(nm);
c49e31f4
SK
501 if (print_namei(nm, path)) {
502 rc = EXIT_FAILURE;
503 continue;
504 }
c84a633a 505 free_namei(nm);
c49e31f4
SK
506 if (sml == -1) {
507 rc = EXIT_FAILURE;
508 warnx(_("%s: exceeded limit of symlinks"), path);
509 continue;
510 }
c84a633a 511 }
6dbe3af9
KZ
512 }
513
4cfcf09b
KZ
514 free_idcache(ucache);
515 free_idcache(gcache);
516
c49e31f4 517 return rc;
6dbe3af9
KZ
518}
519