]> git.ipfire.org Git - thirdparty/util-linux.git/blob - misc-utils/namei.c
libmount: fix comment referring to passno field
[thirdparty/util-linux.git] / misc-utils / namei.c
1 /*
2 * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
3 *
4 * This file is part of util-linux.
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 written by:
17 * Roger S. Southwick (May 2, 1990)
18 * Steve Tell (March 28, 1991)
19 * Arkadiusz Miƛkiewicz (1999-02-22)
20 * Li Zefan (2007-09-10).
21 */
22
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <getopt.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <sys/param.h>
32 #include <pwd.h>
33 #include <grp.h>
34
35 #include "c.h"
36 #include "xalloc.h"
37 #include "nls.h"
38 #include "widechar.h"
39 #include "strutils.h"
40 #include "closestream.h"
41 #include "idcache.h"
42
43 #ifndef MAXSYMLINKS
44 #define MAXSYMLINKS 256
45 #endif
46
47 #define NAMEI_NOLINKS (1 << 1)
48 #define NAMEI_MODES (1 << 2)
49 #define NAMEI_MNTS (1 << 3)
50 #define NAMEI_OWNERS (1 << 4)
51 #define NAMEI_VERTICAL (1 << 5)
52
53
54 struct 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;
61 int mountpoint; /* is mount point */
62 int noent; /* this item not existing (stores errno from stat()) */
63 };
64
65 static int flags;
66 static struct idcache *gcache; /* groupnames */
67 static struct idcache *ucache; /* usernames */
68
69 static void
70 free_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;
78 }
79 }
80
81 static void
82 readlink_to_namei(struct namei *nm, const char *path)
83 {
84 char sym[PATH_MAX];
85 ssize_t sz;
86 int isrel = 0;
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
94 if (p) {
95 isrel = 1;
96 nm->relstart = p - path;
97 sz += nm->relstart + 1;
98 }
99 }
100 nm->abslink = xmalloc(sz + 1);
101
102 if (*sym != '/' && isrel) {
103 /* create the absolute path from the relative symlink */
104 memcpy(nm->abslink, path, nm->relstart);
105 *(nm->abslink + nm->relstart) = '/';
106 nm->relstart++;
107 memcpy(nm->abslink + nm->relstart, sym, sz - nm->relstart);
108 } else
109 /* - absolute link (foo -> /path/bar)
110 * - or link without any subdir (foo -> bar)
111 */
112 memcpy(nm->abslink, sym, sz);
113
114 nm->abslink[sz] = '\0';
115 }
116
117 static struct stat *
118 dotdot_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);
129 path = xmalloc(len + sizeof(DOTDOTDIR));
130
131 memcpy(path, dirname, len);
132 memcpy(path + len, DOTDOTDIR, sizeof(DOTDOTDIR));
133
134 if (stat(path, st))
135 err(EXIT_FAILURE, _("stat of %s failed"), path);
136 free(path);
137 return st;
138 }
139
140 static struct namei *
141 new_namei(struct namei *parent, const char *path, const char *fname, int lev)
142 {
143 struct namei *nm;
144
145 if (!fname)
146 return NULL;
147 nm = xcalloc(1, sizeof(*nm));
148 if (parent)
149 parent->next = nm;
150
151 nm->level = lev;
152 nm->name = xstrdup(fname);
153
154 if (lstat(path, &nm->st) != 0) {
155 nm->noent = errno;
156 return nm;
157 }
158
159 if (S_ISLNK(nm->st.st_mode))
160 readlink_to_namei(nm, path);
161 if (flags & NAMEI_OWNERS) {
162 add_uid(ucache, nm->st.st_uid);
163 add_gid(gcache, nm->st.st_gid);
164 }
165
166 if ((flags & NAMEI_MNTS) && S_ISDIR(nm->st.st_mode)) {
167 struct stat stbuf, *sb = NULL;
168
169 if (parent && S_ISDIR(parent->st.st_mode))
170 sb = &parent->st;
171 else if (!parent || S_ISLNK(parent->st.st_mode))
172 sb = dotdot_stat(path, &stbuf);
173
174 if (sb && (sb->st_dev != nm->st.st_dev || /* different device */
175 sb->st_ino == nm->st.st_ino)) /* root directory */
176 nm->mountpoint = 1;
177 }
178
179 return nm;
180 }
181
182 static struct namei *
183 add_namei(struct namei *parent, const char *orgpath, int start, struct namei **last)
184 {
185 struct namei *nm = NULL, *first = NULL;
186 char *fname, *end, *path;
187 int level = 0;
188
189 if (!orgpath)
190 return NULL;
191 if (parent) {
192 nm = parent;
193 level = parent->level + 1;
194 }
195 path = xstrdup(orgpath);
196 fname = path + start;
197
198 /* root directory */
199 if (*fname == '/') {
200 while (*fname == '/')
201 fname++; /* eat extra '/' */
202 first = nm = new_namei(nm, "/", "/", level);
203 }
204
205 for (end = fname; fname && end; ) {
206 /* set end of filename */
207 if (*fname) {
208 end = strchr(fname, '/');
209 if (end)
210 *end = '\0';
211
212 /* create a new entry */
213 nm = new_namei(nm, path, fname, level);
214 } else
215 end = NULL;
216 if (!first)
217 first = nm;
218 /* set begin of the next filename */
219 if (end) {
220 *end++ = '/';
221 while (*end == '/')
222 end++; /* eat extra '/' */
223 }
224 fname = end;
225 }
226
227 if (last)
228 *last = nm;
229
230 free(path);
231
232 return first;
233 }
234
235 static int
236 follow_symlinks(struct namei *nm)
237 {
238 int symcount = 0;
239
240 for (; nm; nm = nm->next) {
241 struct namei *next, *last;
242
243 if (nm->noent)
244 continue;
245 if (!S_ISLNK(nm->st.st_mode))
246 continue;
247 if (++symcount > MAXSYMLINKS) {
248 /* drop the rest of the list */
249 free_namei(nm->next);
250 nm->next = NULL;
251 return -1;
252 }
253 next = nm->next;
254 nm->next = add_namei(nm, nm->abslink, nm->relstart, &last);
255 if (last)
256 last->next = next;
257 else
258 nm->next = next;
259 }
260 return 0;
261 }
262
263 static int
264 print_namei(struct namei *nm, char *path)
265 {
266 int i;
267
268 if (path)
269 printf("f: %s\n", path);
270
271 for (; nm; nm = nm->next) {
272 char md[11];
273
274 if (nm->noent) {
275 int blanks = 1;
276 if (flags & NAMEI_MODES)
277 blanks += 9;
278 if (flags & NAMEI_OWNERS)
279 blanks += ucache->width + gcache->width + 2;
280 if (!(flags & NAMEI_VERTICAL))
281 blanks += 1;
282 blanks += nm->level * 2;
283 printf("%*s ", blanks, "");
284 printf("%s - %s\n", nm->name, strerror(nm->noent));
285 return -1;
286 }
287
288 xstrmode(nm->st.st_mode, md);
289
290 if (nm->mountpoint)
291 md[0] = 'D';
292
293 if (!(flags & NAMEI_VERTICAL)) {
294 for (i = 0; i < nm->level; i++)
295 fputs(" ", stdout);
296 fputc(' ', stdout);
297 }
298
299 if (flags & NAMEI_MODES)
300 printf("%s", md);
301 else
302 printf("%c", md[0]);
303
304 if (flags & NAMEI_OWNERS) {
305 printf(" %-*s", ucache->width,
306 get_id(ucache, nm->st.st_uid)->name);
307 printf(" %-*s", gcache->width,
308 get_id(gcache, nm->st.st_gid)->name);
309 }
310
311 if (flags & NAMEI_VERTICAL)
312 for (i = 0; i < nm->level; i++)
313 fputs(" ", stdout);
314
315 if (S_ISLNK(nm->st.st_mode))
316 printf(" %s -> %s\n", nm->name,
317 nm->abslink + nm->relstart);
318 else
319 printf(" %s\n", nm->name);
320 }
321 return 0;
322 }
323
324 static void __attribute__((__noreturn__)) usage(void)
325 {
326 const char *p = program_invocation_short_name;
327 FILE *out = stdout;
328
329 if (!*p)
330 p = "namei";
331
332 fputs(USAGE_HEADER, out);
333 fprintf(out,
334 _(" %s [options] <pathname>...\n"), p);
335
336 fputs(USAGE_SEPARATOR, out);
337 fputs(_("Follow a pathname until a terminal point is found.\n"), out);
338
339 fputs(USAGE_OPTIONS, out);
340 fputs(_(
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 printf(USAGE_HELP_OPTIONS(21));
348
349 printf(USAGE_MAN_TAIL("namei(1)"));
350 exit(EXIT_SUCCESS);
351 }
352
353 static const struct option longopts[] =
354 {
355 { "help", no_argument, NULL, 'h' },
356 { "version", no_argument, NULL, 'V' },
357 { "mountpoints", no_argument, NULL, 'x' },
358 { "modes", no_argument, NULL, 'm' },
359 { "owners", no_argument, NULL, 'o' },
360 { "long", no_argument, NULL, 'l' },
361 { "nolinks", no_argument, NULL, 'n' },
362 { "vertical", no_argument, NULL, 'v' },
363 { NULL, 0, NULL, 0 },
364 };
365
366 int
367 main(int argc, char **argv)
368 {
369 int c;
370 int rc = EXIT_SUCCESS;
371
372 setlocale(LC_ALL, "");
373 bindtextdomain(PACKAGE, LOCALEDIR);
374 textdomain(PACKAGE);
375 close_stdout_atexit();
376
377 while ((c = getopt_long(argc, argv, "hVlmnovx", longopts, NULL)) != -1) {
378 switch(c) {
379 case 'l':
380 flags |= (NAMEI_OWNERS | NAMEI_MODES | NAMEI_VERTICAL);
381 break;
382 case 'm':
383 flags |= NAMEI_MODES;
384 break;
385 case 'n':
386 flags |= NAMEI_NOLINKS;
387 break;
388 case 'o':
389 flags |= NAMEI_OWNERS;
390 break;
391 case 'x':
392 flags |= NAMEI_MNTS;
393 break;
394 case 'v':
395 flags |= NAMEI_VERTICAL;
396 break;
397
398 case 'h':
399 usage();
400 case 'V':
401 print_version(EXIT_SUCCESS);
402 default:
403 errtryhelp(EXIT_FAILURE);
404 }
405 }
406
407 if (optind == argc) {
408 warnx(_("pathname argument is missing"));
409 errtryhelp(EXIT_FAILURE);
410 }
411
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
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)
425 rc = EXIT_FAILURE;
426
427 nm = add_namei(NULL, path, 0, NULL);
428 if (nm) {
429 int sml = 0;
430 if (!(flags & NAMEI_NOLINKS))
431 sml = follow_symlinks(nm);
432 if (print_namei(nm, path)) {
433 rc = EXIT_FAILURE;
434 continue;
435 }
436 free_namei(nm);
437 if (sml == -1) {
438 rc = EXIT_FAILURE;
439 warnx(_("%s: exceeded limit of symlinks"), path);
440 continue;
441 }
442 }
443 }
444
445 free_idcache(ucache);
446 free_idcache(gcache);
447
448 return rc;
449 }
450