]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/namei.c
namei: add --version option
[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
108 nc = calloc(1, sizeof(*nc));
109 if (!nc)
110 goto alloc_err;
111 nc->id = id;
112
113 if (name) {
114#ifdef HAVE_WIDECHAR
115 wchar_t wc[LOGIN_NAME_MAX + 1];
116
117 if (mbstowcs(wc, name, LOGIN_NAME_MAX) > 0) {
118 wc[LOGIN_NAME_MAX] = '\0';
119 w = wcswidth(wc, LOGIN_NAME_MAX);
120 }
121 else
122#endif
123 w = strlen(name);
124 }
125 /* note, we ignore names with non-printable widechars */
126 if (w > 0)
127 nc->name = strdup(name);
128 else if (asprintf(&nc->name, "%lu", id) == -1)
129 nc->name = NULL;
130 if (!nc->name)
131 goto alloc_err;
132
133 for (x = *ic; x && x->next; x = x->next);
134
135 /* add 'nc' at end of the 'ic' list */
136 if (x)
137 x->next = nc;
138 else
139 *ic = nc;
140 if (w <= 0)
141 w = strlen(nc->name);
142 *width = *width < w ? w : *width;
143
144 return;
145alloc_err:
146 err(EXIT_FAILURE, _("out of memory?"));
147}
148
149static void
150add_uid(unsigned long int id)
151{
152 struct idcache *ic = get_id(ucache, id);
153
154 if (!ic) {
155 struct passwd *pw = getpwuid((uid_t) id);
156 add_id(&ucache, pw ? pw->pw_name : NULL, id, &uwidth);
157 }
158}
159
160static void
161add_gid(unsigned long int id)
162{
163 struct idcache *ic = get_id(gcache, id);
164
165 if (!ic) {
166 struct group *gr = getgrgid((gid_t) id);
167 add_id(&gcache, gr ? gr->gr_name : NULL, id, &gwidth);
168 }
169}
170
c84a633a
KZ
171static void
172free_namei(struct namei *nm)
173{
174 while (nm) {
175 struct namei *next = nm->next;
176 free(nm->name);
177 free(nm->abslink);
178 free(nm);
179 nm = next;
6dbe3af9 180 }
6dbe3af9
KZ
181}
182
22853e4a 183static void
c84a633a
KZ
184readlink_to_namei(struct namei *nm, const char *path)
185{
186 char sym[PATH_MAX];
eb9a65bb 187 ssize_t sz;
c84a633a
KZ
188
189 sz = readlink(path, sym, sizeof(sym));
190 if (sz < 1)
191 err(EXIT_FAILURE, _("failed to read symlink: %s"), path);
192 if (*sym != '/') {
193 char *p = strrchr(path, '/');
194
d1dca710
KZ
195 nm->relstart = p ? p - path : 0;
196 if (nm->relstart)
197 sz += nm->relstart + 1;
c84a633a 198 }
87f3feac 199 nm->abslink = xmalloc(sz + 1);
c84a633a 200
d1dca710 201 if (*sym != '/' && nm->relstart) {
f7ed29a7 202 /* create the absolute path from the relative symlink */
c84a633a
KZ
203 memcpy(nm->abslink, path, nm->relstart);
204 *(nm->abslink + nm->relstart) = '/';
205 nm->relstart++;
f7ed29a7 206 memcpy(nm->abslink + nm->relstart, sym, sz - nm->relstart);
c84a633a
KZ
207 } else
208 memcpy(nm->abslink, sym, sz);
209 nm->abslink[sz] = '\0';
6dbe3af9
KZ
210}
211
c21e7023
KZ
212static struct stat *
213dotdot_stat(const char *dirname, struct stat *st)
214{
215 char *path;
216 size_t len;
217
218#define DOTDOTDIR "/.."
219
220 if (!dirname)
221 return NULL;
222
223 len = strlen(dirname);
224 path = malloc(len + sizeof(DOTDOTDIR));
225 if (!path)
226 err(EXIT_FAILURE, _("out of memory?"));
227
228 memcpy(path, dirname, len);
229 memcpy(path + len, DOTDOTDIR, sizeof(DOTDOTDIR));
230
231 if (stat(path, st))
232 err(EXIT_FAILURE, _("could not stat '%s'"), path);
233 free(path);
234 return st;
235}
236
c84a633a
KZ
237static struct namei *
238new_namei(struct namei *parent, const char *path, const char *fname, int lev)
239{
240 struct namei *nm;
241
242 if (!fname)
243 return NULL;
244 nm = calloc(1, sizeof(*nm));
245 if (!nm)
246 err(EXIT_FAILURE, _("out of memory?"));
247 if (parent)
248 parent->next = nm;
249
250 nm->level = lev;
251 nm->name = strdup(fname);
252 if (!nm->name)
253 err(EXIT_FAILURE, _("out of memory?"));
c49e31f4
SK
254
255 nm->noent = (lstat(path, &nm->st) == -1);
256 if (nm->noent)
257 return nm;
a439d194
KZ
258
259 if (S_ISLNK(nm->st.st_mode))
260 readlink_to_namei(nm, path);
261 if (flags & NAMEI_OWNERS) {
262 add_uid(nm->st.st_uid);
263 add_gid(nm->st.st_gid);
264 }
265
c21e7023
KZ
266 if ((flags & NAMEI_MNTS) && S_ISDIR(nm->st.st_mode)) {
267 struct stat stbuf, *sb = NULL;
268
269 if (parent && S_ISDIR(parent->st.st_mode))
270 sb = &parent->st;
271 else if (!parent || S_ISLNK(parent->st.st_mode))
272 sb = dotdot_stat(path, &stbuf);
273
274 if (sb && (sb->st_dev != nm->st.st_dev || /* different device */
275 sb->st_ino == nm->st.st_ino)) /* root directory */
276 nm->mountpoint = 1;
277 }
278
c84a633a
KZ
279 return nm;
280}
6dbe3af9 281
c84a633a
KZ
282static struct namei *
283add_namei(struct namei *parent, const char *orgpath, int start, struct namei **last)
284{
285 struct namei *nm = NULL, *first = NULL;
286 char *fname, *end, *path;
287 int level = 0;
288
289 if (!orgpath)
290 return NULL;
291 if (parent) {
292 nm = parent;
293 level = parent->level + 1;
6dbe3af9 294 }
c84a633a
KZ
295 path = strdup(orgpath);
296 if (!path)
297 err(EXIT_FAILURE, _("out of memory?"));
298 fname = path + start;
299
300 /* root directory */
301 if (*fname == '/') {
302 while (*fname == '/')
303 fname++; /* eat extra '/' */
304 first = nm = new_namei(nm, "/", "/", level);
6dbe3af9 305 }
6dbe3af9 306
c84a633a
KZ
307 for (end = fname; fname && end; ) {
308 /* set end of filename */
922a121b
KZ
309 if (*fname) {
310 end = strchr(fname, '/');
311 if (end)
312 *end = '\0';
313
314 /* create a new entry */
315 nm = new_namei(nm, path, fname, level);
316 } else
317 end = NULL;
c84a633a
KZ
318 if (!first)
319 first = nm;
c84a633a
KZ
320 /* set begin of the next filename */
321 if (end) {
322 *end++ = '/';
323 while (*end == '/')
324 end++; /* eat extra '/' */
325 }
326 fname = end;
e8f26419
KZ
327 }
328
c84a633a
KZ
329 if (last)
330 *last = nm;
bf13602c
DB
331
332 free(path);
333
c84a633a
KZ
334 return first;
335}
6dbe3af9 336
c84a633a
KZ
337static int
338follow_symlinks(struct namei *nm)
339{
340 int symcount = 0;
6dbe3af9 341
c84a633a
KZ
342 for (; nm; nm = nm->next) {
343 struct namei *next, *last;
6dbe3af9 344
c49e31f4
SK
345 if (nm->noent)
346 continue;
c84a633a
KZ
347 if (!S_ISLNK(nm->st.st_mode))
348 continue;
349 if (++symcount > MAXSYMLINKS) {
350 /* drop the rest of the list */
351 free_namei(nm->next);
352 nm->next = NULL;
353 return -1;
354 }
355 next = nm->next;
356 nm->next = add_namei(nm, nm->abslink, nm->relstart, &last);
357 if (last)
358 last->next = next;
359 else
360 nm->next = next;
d8ef4c19 361 }
c84a633a
KZ
362 return 0;
363}
d8ef4c19 364
c49e31f4 365static int
c84a633a
KZ
366print_namei(struct namei *nm, char *path)
367{
c84a633a 368 int i;
d8ef4c19 369
c84a633a
KZ
370 if (path)
371 printf("f: %s\n", path);
6dbe3af9 372
8fec558b 373 for (; nm; nm = nm->next) {
c84a633a 374 char md[11];
db8a3e23 375
c49e31f4
SK
376 if (nm->noent) {
377 printf(_("%s - No such file or directory\n"), nm->name);
378 return -1;
379 }
380
c84a633a 381 strmode(nm->st.st_mode, md);
6dbe3af9 382
c21e7023 383 if (nm->mountpoint)
c84a633a 384 md[0] = 'D';
6dbe3af9 385
c3ecdb3e
KZ
386 if (!(flags & NAMEI_VERTICAL)) {
387 for (i = 0; i < nm->level; i++)
388 fputs(" ", stdout);
389 fputc(' ', stdout);
390 }
6dbe3af9 391
c84a633a 392 if (flags & NAMEI_MODES)
c3ecdb3e 393 printf("%s", md);
6dbe3af9 394 else
c3ecdb3e 395 printf("%c", md[0]);
db8a3e23 396
4cfcf09b
KZ
397 if (flags & NAMEI_OWNERS) {
398 printf(" %-*s", uwidth,
399 get_id(ucache, nm->st.st_uid)->name);
400 printf(" %-*s", gwidth,
401 get_id(gcache, nm->st.st_gid)->name);
402 }
c3ecdb3e
KZ
403
404 if (flags & NAMEI_VERTICAL)
405 for (i = 0; i < nm->level; i++)
406 fputs(" ", stdout);
407
c84a633a
KZ
408 if (S_ISLNK(nm->st.st_mode))
409 printf(" %s -> %s\n", nm->name,
410 nm->abslink + nm->relstart);
6dbe3af9 411 else
c84a633a
KZ
412 printf(" %s\n", nm->name);
413 }
c49e31f4 414 return 0;
c84a633a 415}
db8a3e23 416
c84a633a
KZ
417static void
418usage(int rc)
419{
420 const char *p = program_invocation_short_name;
6dbe3af9 421
c84a633a
KZ
422 if (!*p)
423 p = "namei";
f062c8a6 424
c84a633a
KZ
425 printf(_("\nUsage: %s [options] pathname [pathname ...]\n"), p);
426 printf(_("\nOptions:\n"));
db8a3e23 427
c84a633a
KZ
428 printf(_(
429 " -h, --help displays this help text\n"
64718fe7 430 " -V, --version output version information and exit\n"
c84a633a
KZ
431 " -x, --mountpoints show mount point directories with a 'D'\n"
432 " -m, --modes show the mode bits of each file\n"
4cfcf09b 433 " -o, --owners show owner and group name of each file\n"
c3ecdb3e
KZ
434 " -l, --long use a long listing format (-m -o -v) \n"
435 " -n, --nosymlinks don't follow symlinks\n"
436 " -v, --vertical vertical align of modes and owners\n"));
db8a3e23 437
c84a633a
KZ
438 printf(_("\nFor more information see namei(1).\n"));
439 exit(rc);
6dbe3af9
KZ
440}
441
6c7d5ae9 442static const struct option longopts[] =
c84a633a
KZ
443{
444 { "help", 0, 0, 'h' },
64718fe7 445 { "version", 0, 0, 'V' },
c84a633a
KZ
446 { "mountpoints",0, 0, 'x' },
447 { "modes", 0, 0, 'm' },
4cfcf09b
KZ
448 { "owners", 0, 0, 'o' },
449 { "long", 0, 0, 'l' },
c84a633a 450 { "nolinks", 0, 0, 'n' },
c3ecdb3e 451 { "vertical", 0, 0, 'v' },
c84a633a
KZ
452 { NULL, 0, 0, 0 },
453};
6dbe3af9 454
c84a633a
KZ
455int
456main(int argc, char **argv)
457{
458 extern int optind;
459 int c;
c49e31f4 460 int rc = EXIT_SUCCESS;
c84a633a
KZ
461
462 setlocale(LC_ALL, "");
463 bindtextdomain(PACKAGE, LOCALEDIR);
464 textdomain(PACKAGE);
465
466 if (argc < 2)
467 usage(EXIT_FAILURE);
468
64718fe7 469 while ((c = getopt_long(argc, argv, "+h?Vlmnovx", longopts, NULL)) != -1) {
c84a633a
KZ
470 switch(c) {
471 case 'h':
472 case '?':
473 usage(EXIT_SUCCESS);
474 break;
64718fe7
SK
475 case 'V':
476 printf(_("%s from %s\n"), program_invocation_short_name,
477 PACKAGE_STRING);
478 return EXIT_SUCCESS;
4cfcf09b 479 case 'l':
c3ecdb3e 480 flags |= (NAMEI_OWNERS | NAMEI_MODES | NAMEI_VERTICAL);
4cfcf09b 481 break;
c84a633a
KZ
482 case 'm':
483 flags |= NAMEI_MODES;
484 break;
c84a633a
KZ
485 case 'n':
486 flags |= NAMEI_NOLINKS;
487 break;
4cfcf09b
KZ
488 case 'o':
489 flags |= NAMEI_OWNERS;
490 break;
491 case 'x':
492 flags |= NAMEI_MNTS;
493 break;
c3ecdb3e
KZ
494 case 'v':
495 flags |= NAMEI_VERTICAL;
c84a633a 496 }
6dbe3af9 497 }
c84a633a
KZ
498
499 for(; optind < argc; optind++) {
500 char *path = argv[optind];
501 struct namei *nm = NULL;
502 struct stat st;
503
504 if (stat(path, &st) != 0)
c49e31f4 505 rc = EXIT_FAILURE;
c84a633a
KZ
506
507 nm = add_namei(NULL, path, 0, NULL);
508 if (nm) {
509 int sml = 0;
c84a633a
KZ
510 if (!(flags & NAMEI_NOLINKS))
511 sml = follow_symlinks(nm);
c49e31f4
SK
512 if (print_namei(nm, path)) {
513 rc = EXIT_FAILURE;
514 continue;
515 }
c84a633a 516 free_namei(nm);
c49e31f4
SK
517 if (sml == -1) {
518 rc = EXIT_FAILURE;
519 warnx(_("%s: exceeded limit of symlinks"), path);
520 continue;
521 }
c84a633a 522 }
6dbe3af9
KZ
523 }
524
4cfcf09b
KZ
525 free_idcache(ucache);
526 free_idcache(gcache);
527
c49e31f4 528 return rc;
6dbe3af9
KZ
529}
530