]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/namei.c
raw: default to /dev/raw/rawctl
[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)
6dbe3af9 54
db8a3e23 55
c84a633a
KZ
56struct namei {
57 struct stat st; /* item lstat() */
58 char *name; /* item name */
59 char *abslink; /* absolute symlink path */
60 int relstart; /* offset of relative path in 'abslink' */
61 struct namei *next; /* next item */
62 int level;
63};
6dbe3af9 64
4cfcf09b
KZ
65struct idcache {
66 unsigned long int id;
67 char *name;
68 struct idcache *next;
69};
70
71static int flags;
72static int uwidth; /* maximal width of username */
73static int gwidth; /* maximal width of groupname */
74static struct idcache *gcache; /* groupnames */
75static struct idcache *ucache; /* usernames */
76
77static struct idcache *
78get_id(struct idcache *ic, unsigned long int id)
79{
80 while(ic) {
81 if (ic->id == id)
82 return ic;
83 ic = ic->next;
84 }
85 return NULL;
86}
87
88static void
89free_idcache(struct idcache *ic)
90{
91 while(ic) {
92 struct idcache *next = ic->next;
93 free(ic->name);
94 free(ic);
95 ic = next;
96 }
97}
98
99static void
100add_id(struct idcache **ic, char *name, unsigned long int id, int *width)
101{
102 struct idcache *nc, *x;
103 int w = 0;
104
105 nc = calloc(1, sizeof(*nc));
106 if (!nc)
107 goto alloc_err;
108 nc->id = id;
109
110 if (name) {
111#ifdef HAVE_WIDECHAR
112 wchar_t wc[LOGIN_NAME_MAX + 1];
113
114 if (mbstowcs(wc, name, LOGIN_NAME_MAX) > 0) {
115 wc[LOGIN_NAME_MAX] = '\0';
116 w = wcswidth(wc, LOGIN_NAME_MAX);
117 }
118 else
119#endif
120 w = strlen(name);
121 }
122 /* note, we ignore names with non-printable widechars */
123 if (w > 0)
124 nc->name = strdup(name);
125 else if (asprintf(&nc->name, "%lu", id) == -1)
126 nc->name = NULL;
127 if (!nc->name)
128 goto alloc_err;
129
130 for (x = *ic; x && x->next; x = x->next);
131
132 /* add 'nc' at end of the 'ic' list */
133 if (x)
134 x->next = nc;
135 else
136 *ic = nc;
137 if (w <= 0)
138 w = strlen(nc->name);
139 *width = *width < w ? w : *width;
140
141 return;
142alloc_err:
143 err(EXIT_FAILURE, _("out of memory?"));
144}
145
146static void
147add_uid(unsigned long int id)
148{
149 struct idcache *ic = get_id(ucache, id);
150
151 if (!ic) {
152 struct passwd *pw = getpwuid((uid_t) id);
153 add_id(&ucache, pw ? pw->pw_name : NULL, id, &uwidth);
154 }
155}
156
157static void
158add_gid(unsigned long int id)
159{
160 struct idcache *ic = get_id(gcache, id);
161
162 if (!ic) {
163 struct group *gr = getgrgid((gid_t) id);
164 add_id(&gcache, gr ? gr->gr_name : NULL, id, &gwidth);
165 }
166}
167
c84a633a
KZ
168static void
169free_namei(struct namei *nm)
170{
171 while (nm) {
172 struct namei *next = nm->next;
173 free(nm->name);
174 free(nm->abslink);
175 free(nm);
176 nm = next;
6dbe3af9 177 }
6dbe3af9
KZ
178}
179
22853e4a 180static void
c84a633a
KZ
181readlink_to_namei(struct namei *nm, const char *path)
182{
183 char sym[PATH_MAX];
184 size_t sz;
185
186 sz = readlink(path, sym, sizeof(sym));
187 if (sz < 1)
188 err(EXIT_FAILURE, _("failed to read symlink: %s"), path);
189 if (*sym != '/') {
190 char *p = strrchr(path, '/');
191
192 nm->relstart = p ? p - path : strlen(path);
193 sz += nm->relstart + 1;
194 }
195 nm->abslink = malloc(sz + 1);
196 if (!nm->abslink)
197 err(EXIT_FAILURE, _("out of memory?"));
198
199 if (*sym != '/') {
200 memcpy(nm->abslink, path, nm->relstart);
201 *(nm->abslink + nm->relstart) = '/';
202 nm->relstart++;
203 memcpy(nm->abslink + nm->relstart, sym, sz);
204 } else
205 memcpy(nm->abslink, sym, sz);
206 nm->abslink[sz] = '\0';
6dbe3af9
KZ
207}
208
c84a633a
KZ
209static struct namei *
210new_namei(struct namei *parent, const char *path, const char *fname, int lev)
211{
212 struct namei *nm;
213
214 if (!fname)
215 return NULL;
216 nm = calloc(1, sizeof(*nm));
217 if (!nm)
218 err(EXIT_FAILURE, _("out of memory?"));
219 if (parent)
220 parent->next = nm;
221
222 nm->level = lev;
223 nm->name = strdup(fname);
224 if (!nm->name)
225 err(EXIT_FAILURE, _("out of memory?"));
226 if (lstat(path, &nm->st) == -1)
227 err(EXIT_FAILURE, _("could not stat '%s'"), path);
228 return nm;
229}
6dbe3af9 230
c84a633a
KZ
231static struct namei *
232add_namei(struct namei *parent, const char *orgpath, int start, struct namei **last)
233{
234 struct namei *nm = NULL, *first = NULL;
235 char *fname, *end, *path;
236 int level = 0;
237
238 if (!orgpath)
239 return NULL;
240 if (parent) {
241 nm = parent;
242 level = parent->level + 1;
6dbe3af9 243 }
c84a633a
KZ
244 path = strdup(orgpath);
245 if (!path)
246 err(EXIT_FAILURE, _("out of memory?"));
247 fname = path + start;
248
249 /* root directory */
250 if (*fname == '/') {
251 while (*fname == '/')
252 fname++; /* eat extra '/' */
253 first = nm = new_namei(nm, "/", "/", level);
6dbe3af9 254 }
6dbe3af9 255
c84a633a
KZ
256 for (end = fname; fname && end; ) {
257 /* set end of filename */
258 end = strchr(fname, '/');
259 if (end)
260 *end = '\0';
261
262 /* create a new entry */
263 nm = new_namei(nm, path, fname, level);
264 if (!first)
265 first = nm;
266 if (S_ISLNK(nm->st.st_mode))
267 readlink_to_namei(nm, path);
4cfcf09b
KZ
268 if (flags & NAMEI_OWNERS) {
269 add_uid(nm->st.st_uid);
270 add_gid(nm->st.st_gid);
271 }
c84a633a
KZ
272 /* set begin of the next filename */
273 if (end) {
274 *end++ = '/';
275 while (*end == '/')
276 end++; /* eat extra '/' */
277 }
278 fname = end;
e8f26419
KZ
279 }
280
c84a633a
KZ
281 if (last)
282 *last = nm;
283 return first;
284}
6dbe3af9 285
6dbe3af9 286
c84a633a
KZ
287static int
288follow_symlinks(struct namei *nm)
289{
290 int symcount = 0;
6dbe3af9 291
c84a633a
KZ
292 for (; nm; nm = nm->next) {
293 struct namei *next, *last;
6dbe3af9 294
c84a633a
KZ
295 if (!S_ISLNK(nm->st.st_mode))
296 continue;
297 if (++symcount > MAXSYMLINKS) {
298 /* drop the rest of the list */
299 free_namei(nm->next);
300 nm->next = NULL;
301 return -1;
302 }
303 next = nm->next;
304 nm->next = add_namei(nm, nm->abslink, nm->relstart, &last);
305 if (last)
306 last->next = next;
307 else
308 nm->next = next;
d8ef4c19 309 }
c84a633a
KZ
310 return 0;
311}
d8ef4c19 312
c84a633a
KZ
313static void
314strmode(mode_t mode, char *str)
315{
316 if (S_ISDIR(mode))
317 str[0] = 'd';
318 else if (S_ISLNK(mode))
319 str[0] = 'l';
320 else if (S_ISCHR(mode))
321 str[0] = 'c';
322 else if (S_ISBLK(mode))
323 str[0] = 'b';
324 else if (S_ISSOCK(mode))
325 str[0] = 's';
326 else if (S_ISFIFO(mode))
327 str[0] = 'p';
328 else if (S_ISREG(mode))
329 str[0] = '-';
330
331 str[1] = mode & S_IRUSR ? 'r' : '-';
332 str[2] = mode & S_IWUSR ? 'w' : '-';
333 str[3] = (mode & S_ISUID
334 ? (mode & S_IXUSR ? 's' : 'S')
335 : (mode & S_IXUSR ? 'x' : '-'));
336 str[4] = mode & S_IRGRP ? 'r' : '-';
337 str[5] = mode & S_IWGRP ? 'w' : '-';
338 str[6] = (mode & S_ISGID
339 ? (mode & S_IXGRP ? 's' : 'S')
340 : (mode & S_IXGRP ? 'x' : '-'));
341 str[7] = mode & S_IROTH ? 'r' : '-';
342 str[8] = mode & S_IWOTH ? 'w' : '-';
343 str[9] = (mode & S_ISVTX
344 ? (mode & S_IXOTH ? 't' : 'T')
345 : (mode & S_IXOTH ? 'x' : '-'));
346 str[10] = '\0';
347}
6dbe3af9 348
c84a633a
KZ
349static void
350print_namei(struct namei *nm, char *path)
351{
352 struct namei *prev = NULL;
353 int i;
d8ef4c19 354
c84a633a
KZ
355 if (path)
356 printf("f: %s\n", path);
6dbe3af9 357
c84a633a
KZ
358 for (; nm; prev = nm, nm = nm->next) {
359 char md[11];
db8a3e23 360
c84a633a 361 strmode(nm->st.st_mode, md);
6dbe3af9 362
c84a633a
KZ
363 if ((flags & NAMEI_MNTS) && prev &&
364 S_ISDIR(nm->st.st_mode) && S_ISDIR(prev->st.st_mode) &&
365 prev->st.st_dev != nm->st.st_dev)
366 md[0] = 'D';
6dbe3af9 367
c84a633a
KZ
368 for (i = 0; i < nm->level; i++)
369 fputs(" ", stdout);
6dbe3af9 370
c84a633a
KZ
371 if (flags & NAMEI_MODES)
372 printf(" %s", md);
6dbe3af9 373 else
c84a633a 374 printf(" %c", md[0]);
db8a3e23 375
4cfcf09b
KZ
376 if (flags & NAMEI_OWNERS) {
377 printf(" %-*s", uwidth,
378 get_id(ucache, nm->st.st_uid)->name);
379 printf(" %-*s", gwidth,
380 get_id(gcache, nm->st.st_gid)->name);
381 }
c84a633a
KZ
382 if (S_ISLNK(nm->st.st_mode))
383 printf(" %s -> %s\n", nm->name,
384 nm->abslink + nm->relstart);
6dbe3af9 385 else
c84a633a
KZ
386 printf(" %s\n", nm->name);
387 }
388}
db8a3e23 389
c84a633a
KZ
390static void
391usage(int rc)
392{
393 const char *p = program_invocation_short_name;
6dbe3af9 394
c84a633a
KZ
395 if (!*p)
396 p = "namei";
f062c8a6 397
c84a633a
KZ
398 printf(_("\nUsage: %s [options] pathname [pathname ...]\n"), p);
399 printf(_("\nOptions:\n"));
db8a3e23 400
c84a633a
KZ
401 printf(_(
402 " -h, --help displays this help text\n"
403 " -x, --mountpoints show mount point directories with a 'D'\n"
404 " -m, --modes show the mode bits of each file\n"
4cfcf09b
KZ
405 " -o, --owners show owner and group name of each file\n"
406 " -l, --long use a long listing format (-m -o)\n"
c84a633a 407 " -n, --nosymlinks don't follow symlinks\n"));
db8a3e23 408
c84a633a
KZ
409 printf(_("\nFor more information see namei(1).\n"));
410 exit(rc);
6dbe3af9
KZ
411}
412
c84a633a
KZ
413struct option longopts[] =
414{
415 { "help", 0, 0, 'h' },
416 { "mountpoints",0, 0, 'x' },
417 { "modes", 0, 0, 'm' },
4cfcf09b
KZ
418 { "owners", 0, 0, 'o' },
419 { "long", 0, 0, 'l' },
c84a633a
KZ
420 { "nolinks", 0, 0, 'n' },
421 { NULL, 0, 0, 0 },
422};
6dbe3af9 423
c84a633a
KZ
424int
425main(int argc, char **argv)
426{
427 extern int optind;
428 int c;
429
430 setlocale(LC_ALL, "");
431 bindtextdomain(PACKAGE, LOCALEDIR);
432 textdomain(PACKAGE);
433
434 if (argc < 2)
435 usage(EXIT_FAILURE);
436
4cfcf09b 437 while ((c = getopt_long(argc, argv, "+h?lmnox", longopts, NULL)) != -1) {
c84a633a
KZ
438 switch(c) {
439 case 'h':
440 case '?':
441 usage(EXIT_SUCCESS);
442 break;
4cfcf09b
KZ
443 case 'l':
444 flags |= (NAMEI_OWNERS | NAMEI_MODES);
445 break;
c84a633a
KZ
446 case 'm':
447 flags |= NAMEI_MODES;
448 break;
c84a633a
KZ
449 case 'n':
450 flags |= NAMEI_NOLINKS;
451 break;
4cfcf09b
KZ
452 case 'o':
453 flags |= NAMEI_OWNERS;
454 break;
455 case 'x':
456 flags |= NAMEI_MNTS;
457 break;
c84a633a 458 }
6dbe3af9 459 }
c84a633a
KZ
460
461 for(; optind < argc; optind++) {
462 char *path = argv[optind];
463 struct namei *nm = NULL;
464 struct stat st;
465
466 if (stat(path, &st) != 0)
467 err(EXIT_FAILURE, _("failed to stat: %s"), path);
468
469 nm = add_namei(NULL, path, 0, NULL);
470 if (nm) {
471 int sml = 0;
c84a633a
KZ
472 if (!(flags & NAMEI_NOLINKS))
473 sml = follow_symlinks(nm);
474 print_namei(nm, path);
475 free_namei(nm);
476 if (sml == -1)
477 errx(EXIT_FAILURE,
478 _("%s: exceeded limit of symlinks"),
479 path);
480 }
6dbe3af9
KZ
481 }
482
4cfcf09b
KZ
483 free_idcache(ucache);
484 free_idcache(gcache);
485
c84a633a 486 return EXIT_SUCCESS;
6dbe3af9
KZ
487}
488