]> git.ipfire.org Git - thirdparty/util-linux.git/blob - misc-utils/namei.c
namei: fix memory leak in namei(1)
[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-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 */
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <getopt.h>
25 #include <string.h>
26 #include <strings.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 <err.h>
33 #include <pwd.h>
34 #include <grp.h>
35
36 #include "c.h"
37 #include "nls.h"
38 #include "widechar.h"
39
40 #ifndef MAXSYMLINKS
41 #define MAXSYMLINKS 256
42 #endif
43
44 #ifndef LOGIN_NAME_MAX
45 #define LOGIN_NAME_MAX 256
46 #endif
47
48 #define NAMEI_NOLINKS (1 << 1)
49 #define NAMEI_MODES (1 << 2)
50 #define NAMEI_MNTS (1 << 3)
51 #define NAMEI_OWNERS (1 << 4)
52 #define NAMEI_VERTICAL (1 << 5)
53
54
55 struct namei {
56 struct stat st; /* item lstat() */
57 char *name; /* item name */
58 char *abslink; /* absolute symlink path */
59 int relstart; /* offset of relative path in 'abslink' */
60 struct namei *next; /* next item */
61 int level;
62 int mountpoint; /* is mount point */
63 };
64
65 struct idcache {
66 unsigned long int id;
67 char *name;
68 struct idcache *next;
69 };
70
71 static int flags;
72 static int uwidth; /* maximal width of username */
73 static int gwidth; /* maximal width of groupname */
74 static struct idcache *gcache; /* groupnames */
75 static struct idcache *ucache; /* usernames */
76
77 static struct idcache *
78 get_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
88 static void
89 free_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
99 static void
100 add_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;
142 alloc_err:
143 err(EXIT_FAILURE, _("out of memory?"));
144 }
145
146 static void
147 add_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
157 static void
158 add_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
168 static void
169 free_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;
177 }
178 }
179
180 static void
181 readlink_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 : 0;
193 if (nm->relstart)
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 != '/' && nm->relstart) {
201 /* create the absolute path from the relative symlink */
202 memcpy(nm->abslink, path, nm->relstart);
203 *(nm->abslink + nm->relstart) = '/';
204 nm->relstart++;
205 memcpy(nm->abslink + nm->relstart, sym, sz - nm->relstart);
206 } else
207 memcpy(nm->abslink, sym, sz);
208 nm->abslink[sz] = '\0';
209 }
210
211 static struct stat *
212 dotdot_stat(const char *dirname, struct stat *st)
213 {
214 char *path;
215 size_t len;
216
217 #define DOTDOTDIR "/.."
218
219 if (!dirname)
220 return NULL;
221
222 len = strlen(dirname);
223 path = malloc(len + sizeof(DOTDOTDIR));
224 if (!path)
225 err(EXIT_FAILURE, _("out of memory?"));
226
227 memcpy(path, dirname, len);
228 memcpy(path + len, DOTDOTDIR, sizeof(DOTDOTDIR));
229
230 if (stat(path, st))
231 err(EXIT_FAILURE, _("could not stat '%s'"), path);
232 free(path);
233 return st;
234 }
235
236 static struct namei *
237 new_namei(struct namei *parent, const char *path, const char *fname, int lev)
238 {
239 struct namei *nm;
240
241 if (!fname)
242 return NULL;
243 nm = calloc(1, sizeof(*nm));
244 if (!nm)
245 err(EXIT_FAILURE, _("out of memory?"));
246 if (parent)
247 parent->next = nm;
248
249 nm->level = lev;
250 nm->name = strdup(fname);
251 if (!nm->name)
252 err(EXIT_FAILURE, _("out of memory?"));
253 if (lstat(path, &nm->st) == -1)
254 err(EXIT_FAILURE, _("could not stat '%s'"), path);
255
256 if (S_ISLNK(nm->st.st_mode))
257 readlink_to_namei(nm, path);
258 if (flags & NAMEI_OWNERS) {
259 add_uid(nm->st.st_uid);
260 add_gid(nm->st.st_gid);
261 }
262
263 if ((flags & NAMEI_MNTS) && S_ISDIR(nm->st.st_mode)) {
264 struct stat stbuf, *sb = NULL;
265
266 if (parent && S_ISDIR(parent->st.st_mode))
267 sb = &parent->st;
268 else if (!parent || S_ISLNK(parent->st.st_mode))
269 sb = dotdot_stat(path, &stbuf);
270
271 if (sb && (sb->st_dev != nm->st.st_dev || /* different device */
272 sb->st_ino == nm->st.st_ino)) /* root directory */
273 nm->mountpoint = 1;
274 }
275
276 return nm;
277 }
278
279 static struct namei *
280 add_namei(struct namei *parent, const char *orgpath, int start, struct namei **last)
281 {
282 struct namei *nm = NULL, *first = NULL;
283 char *fname, *end, *path;
284 int level = 0;
285
286 if (!orgpath)
287 return NULL;
288 if (parent) {
289 nm = parent;
290 level = parent->level + 1;
291 }
292 path = strdup(orgpath);
293 if (!path)
294 err(EXIT_FAILURE, _("out of memory?"));
295 fname = path + start;
296
297 /* root directory */
298 if (*fname == '/') {
299 while (*fname == '/')
300 fname++; /* eat extra '/' */
301 first = nm = new_namei(nm, "/", "/", level);
302 }
303
304 for (end = fname; fname && end; ) {
305 /* set end of filename */
306 if (*fname) {
307 end = strchr(fname, '/');
308 if (end)
309 *end = '\0';
310
311 /* create a new entry */
312 nm = new_namei(nm, path, fname, level);
313 } else
314 end = NULL;
315 if (!first)
316 first = nm;
317 /* set begin of the next filename */
318 if (end) {
319 *end++ = '/';
320 while (*end == '/')
321 end++; /* eat extra '/' */
322 }
323 fname = end;
324 }
325
326 if (last)
327 *last = nm;
328
329 free(path);
330
331 return first;
332 }
333
334
335 static int
336 follow_symlinks(struct namei *nm)
337 {
338 int symcount = 0;
339
340 for (; nm; nm = nm->next) {
341 struct namei *next, *last;
342
343 if (!S_ISLNK(nm->st.st_mode))
344 continue;
345 if (++symcount > MAXSYMLINKS) {
346 /* drop the rest of the list */
347 free_namei(nm->next);
348 nm->next = NULL;
349 return -1;
350 }
351 next = nm->next;
352 nm->next = add_namei(nm, nm->abslink, nm->relstart, &last);
353 if (last)
354 last->next = next;
355 else
356 nm->next = next;
357 }
358 return 0;
359 }
360
361 static void
362 strmode(mode_t mode, char *str)
363 {
364 if (S_ISDIR(mode))
365 str[0] = 'd';
366 else if (S_ISLNK(mode))
367 str[0] = 'l';
368 else if (S_ISCHR(mode))
369 str[0] = 'c';
370 else if (S_ISBLK(mode))
371 str[0] = 'b';
372 else if (S_ISSOCK(mode))
373 str[0] = 's';
374 else if (S_ISFIFO(mode))
375 str[0] = 'p';
376 else if (S_ISREG(mode))
377 str[0] = '-';
378
379 str[1] = mode & S_IRUSR ? 'r' : '-';
380 str[2] = mode & S_IWUSR ? 'w' : '-';
381 str[3] = (mode & S_ISUID
382 ? (mode & S_IXUSR ? 's' : 'S')
383 : (mode & S_IXUSR ? 'x' : '-'));
384 str[4] = mode & S_IRGRP ? 'r' : '-';
385 str[5] = mode & S_IWGRP ? 'w' : '-';
386 str[6] = (mode & S_ISGID
387 ? (mode & S_IXGRP ? 's' : 'S')
388 : (mode & S_IXGRP ? 'x' : '-'));
389 str[7] = mode & S_IROTH ? 'r' : '-';
390 str[8] = mode & S_IWOTH ? 'w' : '-';
391 str[9] = (mode & S_ISVTX
392 ? (mode & S_IXOTH ? 't' : 'T')
393 : (mode & S_IXOTH ? 'x' : '-'));
394 str[10] = '\0';
395 }
396
397 static void
398 print_namei(struct namei *nm, char *path)
399 {
400 struct namei *prev = NULL;
401 int i;
402
403 if (path)
404 printf("f: %s\n", path);
405
406 for (; nm; prev = nm, nm = nm->next) {
407 char md[11];
408
409 strmode(nm->st.st_mode, md);
410
411 if (nm->mountpoint)
412 md[0] = 'D';
413
414 if (!(flags & NAMEI_VERTICAL)) {
415 for (i = 0; i < nm->level; i++)
416 fputs(" ", stdout);
417 fputc(' ', stdout);
418 }
419
420 if (flags & NAMEI_MODES)
421 printf("%s", md);
422 else
423 printf("%c", md[0]);
424
425 if (flags & NAMEI_OWNERS) {
426 printf(" %-*s", uwidth,
427 get_id(ucache, nm->st.st_uid)->name);
428 printf(" %-*s", gwidth,
429 get_id(gcache, nm->st.st_gid)->name);
430 }
431
432 if (flags & NAMEI_VERTICAL)
433 for (i = 0; i < nm->level; i++)
434 fputs(" ", stdout);
435
436 if (S_ISLNK(nm->st.st_mode))
437 printf(" %s -> %s\n", nm->name,
438 nm->abslink + nm->relstart);
439 else
440 printf(" %s\n", nm->name);
441 }
442 }
443
444 static void
445 usage(int rc)
446 {
447 const char *p = program_invocation_short_name;
448
449 if (!*p)
450 p = "namei";
451
452 printf(_("\nUsage: %s [options] pathname [pathname ...]\n"), p);
453 printf(_("\nOptions:\n"));
454
455 printf(_(
456 " -h, --help displays this help text\n"
457 " -x, --mountpoints show mount point directories with a 'D'\n"
458 " -m, --modes show the mode bits of each file\n"
459 " -o, --owners show owner and group name of each file\n"
460 " -l, --long use a long listing format (-m -o -v) \n"
461 " -n, --nosymlinks don't follow symlinks\n"
462 " -v, --vertical vertical align of modes and owners\n"));
463
464 printf(_("\nFor more information see namei(1).\n"));
465 exit(rc);
466 }
467
468 struct option longopts[] =
469 {
470 { "help", 0, 0, 'h' },
471 { "mountpoints",0, 0, 'x' },
472 { "modes", 0, 0, 'm' },
473 { "owners", 0, 0, 'o' },
474 { "long", 0, 0, 'l' },
475 { "nolinks", 0, 0, 'n' },
476 { "vertical", 0, 0, 'v' },
477 { NULL, 0, 0, 0 },
478 };
479
480 int
481 main(int argc, char **argv)
482 {
483 extern int optind;
484 int c;
485
486 setlocale(LC_ALL, "");
487 bindtextdomain(PACKAGE, LOCALEDIR);
488 textdomain(PACKAGE);
489
490 if (argc < 2)
491 usage(EXIT_FAILURE);
492
493 while ((c = getopt_long(argc, argv, "+h?lmnovx", longopts, NULL)) != -1) {
494 switch(c) {
495 case 'h':
496 case '?':
497 usage(EXIT_SUCCESS);
498 break;
499 case 'l':
500 flags |= (NAMEI_OWNERS | NAMEI_MODES | NAMEI_VERTICAL);
501 break;
502 case 'm':
503 flags |= NAMEI_MODES;
504 break;
505 case 'n':
506 flags |= NAMEI_NOLINKS;
507 break;
508 case 'o':
509 flags |= NAMEI_OWNERS;
510 break;
511 case 'x':
512 flags |= NAMEI_MNTS;
513 break;
514 case 'v':
515 flags |= NAMEI_VERTICAL;
516 }
517 }
518
519 for(; optind < argc; optind++) {
520 char *path = argv[optind];
521 struct namei *nm = NULL;
522 struct stat st;
523
524 if (stat(path, &st) != 0)
525 err(EXIT_FAILURE, _("failed to stat: %s"), path);
526
527 nm = add_namei(NULL, path, 0, NULL);
528 if (nm) {
529 int sml = 0;
530 if (!(flags & NAMEI_NOLINKS))
531 sml = follow_symlinks(nm);
532 print_namei(nm, path);
533 free_namei(nm);
534 if (sml == -1)
535 errx(EXIT_FAILURE,
536 _("%s: exceeded limit of symlinks"),
537 path);
538 }
539 }
540
541 free_idcache(ucache);
542 free_idcache(gcache);
543
544 return EXIT_SUCCESS;
545 }
546