]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/namei.c
wipefs: fix coding style
[thirdparty/util-linux.git] / misc-utils / namei.c
CommitLineData
c84a633a
KZ
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 */
6dbe3af9 22#include <stdio.h>
fd6b7a7f 23#include <unistd.h>
c84a633a 24#include <getopt.h>
fd6b7a7f 25#include <string.h>
2cccd0ff 26#include <strings.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>
c84a633a 32#include <err.h>
4cfcf09b
KZ
33#include <pwd.h>
34#include <grp.h>
7eda085c 35#include "nls.h"
4cfcf09b 36#include "widechar.h"
6dbe3af9 37
6dbe3af9
KZ
38#ifndef MAXSYMLINKS
39#define MAXSYMLINKS 256
40#endif
41
e27a08ab
KZ
42#ifndef PATH_MAX
43#define PATH_MAX 4096
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;
64};
6dbe3af9 65
4cfcf09b
KZ
66struct idcache {
67 unsigned long int id;
68 char *name;
69 struct idcache *next;
70};
71
72static int flags;
73static int uwidth; /* maximal width of username */
74static int gwidth; /* maximal width of groupname */
75static struct idcache *gcache; /* groupnames */
76static struct idcache *ucache; /* usernames */
77
78static struct idcache *
79get_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
89static void
90free_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
100static void
101add_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;
143alloc_err:
144 err(EXIT_FAILURE, _("out of memory?"));
145}
146
147static void
148add_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
158static void
159add_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
c84a633a
KZ
169static void
170free_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;
6dbe3af9 178 }
6dbe3af9
KZ
179}
180
22853e4a 181static void
c84a633a
KZ
182readlink_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 != '/') {
f7ed29a7 201 /* create the absolute path from the relative symlink */
c84a633a
KZ
202 memcpy(nm->abslink, path, nm->relstart);
203 *(nm->abslink + nm->relstart) = '/';
204 nm->relstart++;
f7ed29a7 205 memcpy(nm->abslink + nm->relstart, sym, sz - nm->relstart);
c84a633a
KZ
206 } else
207 memcpy(nm->abslink, sym, sz);
208 nm->abslink[sz] = '\0';
6dbe3af9
KZ
209}
210
c84a633a
KZ
211static struct namei *
212new_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}
6dbe3af9 232
c84a633a
KZ
233static struct namei *
234add_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;
6dbe3af9 245 }
c84a633a
KZ
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);
6dbe3af9 256 }
6dbe3af9 257
c84a633a
KZ
258 for (end = fname; fname && end; ) {
259 /* set end of filename */
922a121b
KZ
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;
c84a633a
KZ
269 if (!first)
270 first = nm;
271 if (S_ISLNK(nm->st.st_mode))
272 readlink_to_namei(nm, path);
4cfcf09b
KZ
273 if (flags & NAMEI_OWNERS) {
274 add_uid(nm->st.st_uid);
275 add_gid(nm->st.st_gid);
276 }
c84a633a
KZ
277 /* set begin of the next filename */
278 if (end) {
279 *end++ = '/';
280 while (*end == '/')
281 end++; /* eat extra '/' */
282 }
283 fname = end;
e8f26419
KZ
284 }
285
c84a633a
KZ
286 if (last)
287 *last = nm;
288 return first;
289}
6dbe3af9 290
6dbe3af9 291
c84a633a
KZ
292static int
293follow_symlinks(struct namei *nm)
294{
295 int symcount = 0;
6dbe3af9 296
c84a633a
KZ
297 for (; nm; nm = nm->next) {
298 struct namei *next, *last;
6dbe3af9 299
c84a633a
KZ
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;
d8ef4c19 314 }
c84a633a
KZ
315 return 0;
316}
d8ef4c19 317
c84a633a
KZ
318static void
319strmode(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}
6dbe3af9 353
c84a633a
KZ
354static void
355print_namei(struct namei *nm, char *path)
356{
357 struct namei *prev = NULL;
358 int i;
d8ef4c19 359
c84a633a
KZ
360 if (path)
361 printf("f: %s\n", path);
6dbe3af9 362
c84a633a
KZ
363 for (; nm; prev = nm, nm = nm->next) {
364 char md[11];
db8a3e23 365
c84a633a 366 strmode(nm->st.st_mode, md);
6dbe3af9 367
c84a633a
KZ
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';
6dbe3af9 372
c3ecdb3e
KZ
373 if (!(flags & NAMEI_VERTICAL)) {
374 for (i = 0; i < nm->level; i++)
375 fputs(" ", stdout);
376 fputc(' ', stdout);
377 }
6dbe3af9 378
c84a633a 379 if (flags & NAMEI_MODES)
c3ecdb3e 380 printf("%s", md);
6dbe3af9 381 else
c3ecdb3e 382 printf("%c", md[0]);
db8a3e23 383
4cfcf09b
KZ
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 }
c3ecdb3e
KZ
390
391 if (flags & NAMEI_VERTICAL)
392 for (i = 0; i < nm->level; i++)
393 fputs(" ", stdout);
394
c84a633a
KZ
395 if (S_ISLNK(nm->st.st_mode))
396 printf(" %s -> %s\n", nm->name,
397 nm->abslink + nm->relstart);
6dbe3af9 398 else
c84a633a
KZ
399 printf(" %s\n", nm->name);
400 }
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"
416 " -x, --mountpoints show mount point directories with a 'D'\n"
417 " -m, --modes show the mode bits of each file\n"
4cfcf09b 418 " -o, --owners show owner and group name of each file\n"
c3ecdb3e
KZ
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"));
db8a3e23 422
c84a633a
KZ
423 printf(_("\nFor more information see namei(1).\n"));
424 exit(rc);
6dbe3af9
KZ
425}
426
c84a633a
KZ
427struct option longopts[] =
428{
429 { "help", 0, 0, 'h' },
430 { "mountpoints",0, 0, 'x' },
431 { "modes", 0, 0, 'm' },
4cfcf09b
KZ
432 { "owners", 0, 0, 'o' },
433 { "long", 0, 0, 'l' },
c84a633a 434 { "nolinks", 0, 0, 'n' },
c3ecdb3e 435 { "vertical", 0, 0, 'v' },
c84a633a
KZ
436 { NULL, 0, 0, 0 },
437};
6dbe3af9 438
c84a633a
KZ
439int
440main(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
c3ecdb3e 452 while ((c = getopt_long(argc, argv, "+h?lmnovx", longopts, NULL)) != -1) {
c84a633a
KZ
453 switch(c) {
454 case 'h':
455 case '?':
456 usage(EXIT_SUCCESS);
457 break;
4cfcf09b 458 case 'l':
c3ecdb3e 459 flags |= (NAMEI_OWNERS | NAMEI_MODES | NAMEI_VERTICAL);
4cfcf09b 460 break;
c84a633a
KZ
461 case 'm':
462 flags |= NAMEI_MODES;
463 break;
c84a633a
KZ
464 case 'n':
465 flags |= NAMEI_NOLINKS;
466 break;
4cfcf09b
KZ
467 case 'o':
468 flags |= NAMEI_OWNERS;
469 break;
470 case 'x':
471 flags |= NAMEI_MNTS;
472 break;
c3ecdb3e
KZ
473 case 'v':
474 flags |= NAMEI_VERTICAL;
c84a633a 475 }
6dbe3af9 476 }
c84a633a
KZ
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;
c84a633a
KZ
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 }
6dbe3af9
KZ
498 }
499
4cfcf09b
KZ
500 free_idcache(ucache);
501 free_idcache(gcache);
502
c84a633a 503 return EXIT_SUCCESS;
6dbe3af9
KZ
504}
505