]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/namei.c
include/c.h: add errtryhelp()
[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"
04a5cb58 41#include "idcache.h"
6dbe3af9 42
6dbe3af9
KZ
43#ifndef MAXSYMLINKS
44#define MAXSYMLINKS 256
45#endif
46
c84a633a
KZ
47#define NAMEI_NOLINKS (1 << 1)
48#define NAMEI_MODES (1 << 2)
49#define NAMEI_MNTS (1 << 3)
4cfcf09b 50#define NAMEI_OWNERS (1 << 4)
c3ecdb3e 51#define NAMEI_VERTICAL (1 << 5)
6dbe3af9 52
db8a3e23 53
c84a633a
KZ
54struct namei {
55 struct stat st; /* item lstat() */
56 char *name; /* item name */
57 char *abslink; /* absolute symlink path */
58 int relstart; /* offset of relative path in 'abslink' */
59 struct namei *next; /* next item */
60 int level;
c21e7023 61 int mountpoint; /* is mount point */
c49e31f4 62 int noent; /* is this item not existing */
c84a633a 63};
6dbe3af9 64
4cfcf09b 65static int flags;
4cfcf09b
KZ
66static struct idcache *gcache; /* groupnames */
67static struct idcache *ucache; /* usernames */
68
c84a633a
KZ
69static void
70free_namei(struct namei *nm)
71{
72 while (nm) {
73 struct namei *next = nm->next;
74 free(nm->name);
75 free(nm->abslink);
76 free(nm);
77 nm = next;
6dbe3af9 78 }
6dbe3af9
KZ
79}
80
22853e4a 81static void
c84a633a
KZ
82readlink_to_namei(struct namei *nm, const char *path)
83{
84 char sym[PATH_MAX];
eb9a65bb 85 ssize_t sz;
89394013 86 int isrel = 0;
c84a633a
KZ
87
88 sz = readlink(path, sym, sizeof(sym));
89 if (sz < 1)
90 err(EXIT_FAILURE, _("failed to read symlink: %s"), path);
91 if (*sym != '/') {
92 char *p = strrchr(path, '/');
93
89394013
KZ
94 if (p) {
95 isrel = 1;
38efd1e2 96 nm->relstart = p - path;
d1dca710 97 sz += nm->relstart + 1;
89394013 98 }
c84a633a 99 }
87f3feac 100 nm->abslink = xmalloc(sz + 1);
c84a633a 101
89394013 102 if (*sym != '/' && isrel) {
f7ed29a7 103 /* create the absolute path from the relative symlink */
c84a633a
KZ
104 memcpy(nm->abslink, path, nm->relstart);
105 *(nm->abslink + nm->relstart) = '/';
106 nm->relstart++;
f7ed29a7 107 memcpy(nm->abslink + nm->relstart, sym, sz - nm->relstart);
c84a633a 108 } else
89394013
KZ
109 /* - absolute link (foo -> /path/bar)
110 * - or link without any subdir (foo -> bar)
111 */
c84a633a 112 memcpy(nm->abslink, sym, sz);
89394013 113
c84a633a 114 nm->abslink[sz] = '\0';
6dbe3af9
KZ
115}
116
c21e7023
KZ
117static struct stat *
118dotdot_stat(const char *dirname, struct stat *st)
119{
120 char *path;
121 size_t len;
122
123#define DOTDOTDIR "/.."
124
125 if (!dirname)
126 return NULL;
127
128 len = strlen(dirname);
b9779517 129 path = xmalloc(len + sizeof(DOTDOTDIR));
c21e7023
KZ
130
131 memcpy(path, dirname, len);
132 memcpy(path + len, DOTDOTDIR, sizeof(DOTDOTDIR));
133
134 if (stat(path, st))
fc14ceba 135 err(EXIT_FAILURE, _("stat of %s failed"), path);
c21e7023
KZ
136 free(path);
137 return st;
138}
139
c84a633a
KZ
140static struct namei *
141new_namei(struct namei *parent, const char *path, const char *fname, int lev)
142{
143 struct namei *nm;
144
145 if (!fname)
146 return NULL;
b9779517 147 nm = xcalloc(1, sizeof(*nm));
c84a633a
KZ
148 if (parent)
149 parent->next = nm;
150
151 nm->level = lev;
b9779517 152 nm->name = xstrdup(fname);
c49e31f4
SK
153
154 nm->noent = (lstat(path, &nm->st) == -1);
155 if (nm->noent)
156 return nm;
a439d194
KZ
157
158 if (S_ISLNK(nm->st.st_mode))
159 readlink_to_namei(nm, path);
160 if (flags & NAMEI_OWNERS) {
04a5cb58
KZ
161 add_uid(ucache, nm->st.st_uid);
162 add_gid(gcache, nm->st.st_gid);
a439d194
KZ
163 }
164
c21e7023
KZ
165 if ((flags & NAMEI_MNTS) && S_ISDIR(nm->st.st_mode)) {
166 struct stat stbuf, *sb = NULL;
167
168 if (parent && S_ISDIR(parent->st.st_mode))
169 sb = &parent->st;
170 else if (!parent || S_ISLNK(parent->st.st_mode))
171 sb = dotdot_stat(path, &stbuf);
172
173 if (sb && (sb->st_dev != nm->st.st_dev || /* different device */
174 sb->st_ino == nm->st.st_ino)) /* root directory */
175 nm->mountpoint = 1;
176 }
177
c84a633a
KZ
178 return nm;
179}
6dbe3af9 180
c84a633a
KZ
181static struct namei *
182add_namei(struct namei *parent, const char *orgpath, int start, struct namei **last)
183{
184 struct namei *nm = NULL, *first = NULL;
185 char *fname, *end, *path;
186 int level = 0;
187
188 if (!orgpath)
189 return NULL;
190 if (parent) {
191 nm = parent;
192 level = parent->level + 1;
6dbe3af9 193 }
b9779517 194 path = xstrdup(orgpath);
c84a633a
KZ
195 fname = path + start;
196
197 /* root directory */
198 if (*fname == '/') {
199 while (*fname == '/')
200 fname++; /* eat extra '/' */
201 first = nm = new_namei(nm, "/", "/", level);
6dbe3af9 202 }
6dbe3af9 203
c84a633a
KZ
204 for (end = fname; fname && end; ) {
205 /* set end of filename */
922a121b
KZ
206 if (*fname) {
207 end = strchr(fname, '/');
208 if (end)
209 *end = '\0';
210
211 /* create a new entry */
212 nm = new_namei(nm, path, fname, level);
213 } else
214 end = NULL;
c84a633a
KZ
215 if (!first)
216 first = nm;
c84a633a
KZ
217 /* set begin of the next filename */
218 if (end) {
219 *end++ = '/';
220 while (*end == '/')
221 end++; /* eat extra '/' */
222 }
223 fname = end;
e8f26419
KZ
224 }
225
c84a633a
KZ
226 if (last)
227 *last = nm;
bf13602c
DB
228
229 free(path);
230
c84a633a
KZ
231 return first;
232}
6dbe3af9 233
c84a633a
KZ
234static int
235follow_symlinks(struct namei *nm)
236{
237 int symcount = 0;
6dbe3af9 238
c84a633a
KZ
239 for (; nm; nm = nm->next) {
240 struct namei *next, *last;
6dbe3af9 241
c49e31f4
SK
242 if (nm->noent)
243 continue;
c84a633a
KZ
244 if (!S_ISLNK(nm->st.st_mode))
245 continue;
246 if (++symcount > MAXSYMLINKS) {
247 /* drop the rest of the list */
248 free_namei(nm->next);
249 nm->next = NULL;
250 return -1;
251 }
252 next = nm->next;
253 nm->next = add_namei(nm, nm->abslink, nm->relstart, &last);
254 if (last)
255 last->next = next;
256 else
257 nm->next = next;
d8ef4c19 258 }
c84a633a
KZ
259 return 0;
260}
d8ef4c19 261
c49e31f4 262static int
c84a633a
KZ
263print_namei(struct namei *nm, char *path)
264{
c84a633a 265 int i;
d8ef4c19 266
c84a633a
KZ
267 if (path)
268 printf("f: %s\n", path);
6dbe3af9 269
8fec558b 270 for (; nm; nm = nm->next) {
c84a633a 271 char md[11];
db8a3e23 272
c49e31f4 273 if (nm->noent) {
cd804450
SK
274 int blanks = 1;
275 if (flags & NAMEI_MODES)
276 blanks += 9;
277 if (flags & NAMEI_OWNERS)
04a5cb58 278 blanks += ucache->width + gcache->width + 2;
cd804450
SK
279 if (!(flags & NAMEI_VERTICAL))
280 blanks += 1;
281 blanks += nm->level * 2;
282 printf("%*s ", blanks, "");
c49e31f4
SK
283 printf(_("%s - No such file or directory\n"), nm->name);
284 return -1;
285 }
286
b0b24b11 287 xstrmode(nm->st.st_mode, md);
6dbe3af9 288
c21e7023 289 if (nm->mountpoint)
c84a633a 290 md[0] = 'D';
6dbe3af9 291
c3ecdb3e
KZ
292 if (!(flags & NAMEI_VERTICAL)) {
293 for (i = 0; i < nm->level; i++)
294 fputs(" ", stdout);
295 fputc(' ', stdout);
296 }
6dbe3af9 297
c84a633a 298 if (flags & NAMEI_MODES)
c3ecdb3e 299 printf("%s", md);
6dbe3af9 300 else
c3ecdb3e 301 printf("%c", md[0]);
db8a3e23 302
4cfcf09b 303 if (flags & NAMEI_OWNERS) {
04a5cb58 304 printf(" %-*s", ucache->width,
4cfcf09b 305 get_id(ucache, nm->st.st_uid)->name);
04a5cb58 306 printf(" %-*s", gcache->width,
4cfcf09b
KZ
307 get_id(gcache, nm->st.st_gid)->name);
308 }
c3ecdb3e
KZ
309
310 if (flags & NAMEI_VERTICAL)
311 for (i = 0; i < nm->level; i++)
312 fputs(" ", stdout);
313
c84a633a
KZ
314 if (S_ISLNK(nm->st.st_mode))
315 printf(" %s -> %s\n", nm->name,
316 nm->abslink + nm->relstart);
6dbe3af9 317 else
c84a633a
KZ
318 printf(" %s\n", nm->name);
319 }
c49e31f4 320 return 0;
c84a633a 321}
db8a3e23 322
54f07289 323static void usage(int rc)
c84a633a
KZ
324{
325 const char *p = program_invocation_short_name;
54f07289 326 FILE *out = rc == EXIT_FAILURE ? stderr : stdout;
6dbe3af9 327
c84a633a
KZ
328 if (!*p)
329 p = "namei";
f062c8a6 330
db433bf7 331 fputs(USAGE_HEADER, out);
54f07289 332 fprintf(out,
09af3db4 333 _(" %s [options] <pathname>...\n"), p);
451dbcfa
BS
334
335 fputs(USAGE_SEPARATOR, out);
336 fputs(_("Follow a pathname until a terminal point is found.\n"), out);
337
db433bf7 338 fputs(USAGE_OPTIONS, out);
54f07289
KZ
339 fputs(_(" -h, --help displays this help text\n"
340 " -V, --version output version information and exit\n"
341 " -x, --mountpoints show mount point directories with a 'D'\n"
342 " -m, --modes show the mode bits of each file\n"
343 " -o, --owners show owner and group name of each file\n"
344 " -l, --long use a long listing format (-m -o -v) \n"
345 " -n, --nosymlinks don't follow symlinks\n"
346 " -v, --vertical vertical align of modes and owners\n"), out);
347
a587cc55 348 fprintf(out, USAGE_MAN_TAIL("namei(1)"));
c84a633a 349 exit(rc);
6dbe3af9
KZ
350}
351
6c7d5ae9 352static const struct option longopts[] =
c84a633a
KZ
353{
354 { "help", 0, 0, 'h' },
64718fe7 355 { "version", 0, 0, 'V' },
c84a633a
KZ
356 { "mountpoints",0, 0, 'x' },
357 { "modes", 0, 0, 'm' },
4cfcf09b
KZ
358 { "owners", 0, 0, 'o' },
359 { "long", 0, 0, 'l' },
c84a633a 360 { "nolinks", 0, 0, 'n' },
c3ecdb3e 361 { "vertical", 0, 0, 'v' },
c84a633a
KZ
362 { NULL, 0, 0, 0 },
363};
6dbe3af9 364
c84a633a
KZ
365int
366main(int argc, char **argv)
367{
c84a633a 368 int c;
c49e31f4 369 int rc = EXIT_SUCCESS;
c84a633a
KZ
370
371 setlocale(LC_ALL, "");
372 bindtextdomain(PACKAGE, LOCALEDIR);
373 textdomain(PACKAGE);
c05a80ca 374 atexit(close_stdout);
c84a633a 375
5a0d9255 376 while ((c = getopt_long(argc, argv, "hVlmnovx", longopts, NULL)) != -1) {
c84a633a
KZ
377 switch(c) {
378 case 'h':
c84a633a
KZ
379 usage(EXIT_SUCCESS);
380 break;
64718fe7 381 case 'V':
e421313d 382 printf(UTIL_LINUX_VERSION);
64718fe7 383 return EXIT_SUCCESS;
4cfcf09b 384 case 'l':
c3ecdb3e 385 flags |= (NAMEI_OWNERS | NAMEI_MODES | NAMEI_VERTICAL);
4cfcf09b 386 break;
c84a633a
KZ
387 case 'm':
388 flags |= NAMEI_MODES;
389 break;
c84a633a
KZ
390 case 'n':
391 flags |= NAMEI_NOLINKS;
392 break;
4cfcf09b
KZ
393 case 'o':
394 flags |= NAMEI_OWNERS;
395 break;
396 case 'x':
397 flags |= NAMEI_MNTS;
398 break;
c3ecdb3e
KZ
399 case 'v':
400 flags |= NAMEI_VERTICAL;
5a0d9255
SK
401 break;
402 default:
403 usage(EXIT_FAILURE);
c84a633a 404 }
6dbe3af9 405 }
c84a633a 406
5a0d9255
SK
407 if (optind == argc) {
408 warnx(_("pathname argument is missing"));
409 usage(EXIT_FAILURE);
410 }
411
04a5cb58
KZ
412 ucache = new_idcache();
413 if (!ucache)
414 err(EXIT_FAILURE, _("failed to allocate UID cache"));
415 gcache = new_idcache();
416 if (!gcache)
417 err(EXIT_FAILURE, _("failed to allocate GID cache"));
418
c84a633a
KZ
419 for(; optind < argc; optind++) {
420 char *path = argv[optind];
421 struct namei *nm = NULL;
422 struct stat st;
423
424 if (stat(path, &st) != 0)
c49e31f4 425 rc = EXIT_FAILURE;
c84a633a
KZ
426
427 nm = add_namei(NULL, path, 0, NULL);
428 if (nm) {
429 int sml = 0;
c84a633a
KZ
430 if (!(flags & NAMEI_NOLINKS))
431 sml = follow_symlinks(nm);
c49e31f4
SK
432 if (print_namei(nm, path)) {
433 rc = EXIT_FAILURE;
434 continue;
435 }
c84a633a 436 free_namei(nm);
c49e31f4
SK
437 if (sml == -1) {
438 rc = EXIT_FAILURE;
439 warnx(_("%s: exceeded limit of symlinks"), path);
440 continue;
441 }
c84a633a 442 }
6dbe3af9
KZ
443 }
444
4cfcf09b
KZ
445 free_idcache(ucache);
446 free_idcache(gcache);
447
c49e31f4 448 return rc;
6dbe3af9
KZ
449}
450