summaryrefslogtreecommitdiffstats
path: root/misc-utils/namei.c
blob: 65faad4f3c264bbbcdca45dd8af1aaa1c0d09b2a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
/*-------------------------------------------------------------

The namei program

By: Roger S. Southwick

May 2, 1990


Modifications by Steve Tell  March 28, 1991

usage: namei pathname [pathname ... ]

This program reads it's arguments as pathnames to any type
of Unix file (symlinks, files, directories, and so forth). 
The program then follows each pathname until a terminal 
point is found (a file, directory, char device, etc).
If it finds a symbolic link, we show the link, and start
following it, indenting the output to show the context.

This program is useful for finding a "too many levels of
symbolic links" problems.

For each line output, the program puts a file type first:

   f: = the pathname we are currently trying to resolve
    d = directory
    D = directory that is a mount point
    l = symbolic link (both the link and it's contents are output)
    s = socket
    b = block device
    c = character device
    - = regular file
    ? = an error of some kind

The program prints an informative messages when we exceed
the maximum number of symbolic links this system can have.

The program exits with a 1 status ONLY if it finds it cannot
chdir to /,  or if it encounters an unknown file type.

1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
- added Native Language Support

-------------------------------------------------------------*/

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include "nls.h"

#define ERR	strerror(errno),errno

int symcount;
int mflag = 0;
int xflag = 0;

#ifndef MAXSYMLINKS
#define MAXSYMLINKS 256
#endif

static char *pperm(unsigned short);
static void namei(char *, int);
static void usage(void);

int
main(int argc, char **argv) {
    extern int optind;
    int c;
    char curdir[MAXPATHLEN];

    setlocale(LC_ALL, "");
    bindtextdomain(PACKAGE, LOCALEDIR);
    textdomain(PACKAGE);
    
    if(argc < 2)
	usage();

    while((c = getopt(argc, argv, "mx")) != EOF){
	switch(c){
	    case 'm':
		mflag = !mflag;
		break;
	    
	    case 'x':
		xflag = !xflag;
		break;

	    case '?':
	    default:
		usage();
	}
    }

    if(getcwd(curdir, sizeof(curdir)) == NULL){
	(void)fprintf(stderr, _("namei: unable to get current directory - %s\n"), curdir);
	exit(1);
    }


    for(; optind < argc; optind++){
	(void)printf("f: %s\n", argv[optind]);
	symcount = 1;
	namei(argv[optind], 0);

	if(chdir(curdir) == -1){
	    (void)fprintf(stderr, _("namei: unable to chdir to %s - %s (%d)\n"), curdir, ERR);
	    exit(1);
	}
    }
    return 0;
}

static void
usage(void) {
    (void)fprintf(stderr,_("usage: namei [-mx] pathname [pathname ...]\n"));
    exit(1);
}

#ifndef NODEV
#define NODEV		(dev_t)(-1)
#endif

static void
namei(char *file, int lev) {
    char *cp;
    char buf[BUFSIZ], sym[BUFSIZ];
    struct stat stb;
    int i;
    dev_t lastdev = NODEV;

    /*
     * See if the file has a leading /, and if so cd to root
     */
    
    if(*file == '/'){
	while(*file == '/')
	    file++;
	
	if(chdir("/") == -1){
	    (void)fprintf(stderr,_("namei: could not chdir to root!\n"));
	    exit(1);
	}
	for(i = 0; i < lev; i++)
	    (void)printf("  ");

	if(stat("/", &stb) == -1){
	    (void)fprintf(stderr, _("namei: could not stat root!\n"));
	    exit(1);
	}
	lastdev = stb.st_dev;

	if(mflag)
	    (void)printf(" d%s /\n", pperm(stb.st_mode));
	else
	    (void)printf(" d /\n");
    }

    for(;;){

	/*
	 * Copy up to the next / (or nil) into buf
	 */
	
	for(cp = buf; *file != '\0' && *file != '/'; cp++, file++)
	    *cp = *file;
	
	while(*file == '/')	/* eat extra /'s	*/
	    file++;

	*cp = '\0';

	if(buf[0] == '\0'){

	    /*
	     * Buf is empty, so therefore we are done
	     * with this level of file
	     */

	    return;
	}

	for(i = 0; i < lev; i++)
	    (void)printf("  ");

	/*
	 * See what type of critter this file is
	 */
	
	if(lstat(buf, &stb) == -1){
	    (void)printf(" ? %s - %s (%d)\n", buf, ERR);
	    return;
	}

	switch(stb.st_mode & S_IFMT){
	    case S_IFDIR:

		/*
		 * File is a directory, chdir to it
		 */
		
		if(chdir(buf) == -1){
		    (void)printf(_(" ? could not chdir into %s - %s (%d)\n"), buf, ERR );
		    return;
		}
		if(xflag && lastdev != stb.st_dev && lastdev != NODEV){
		    /* Across mnt point */
		    if(mflag)
			(void)printf(" D%s %s\n", pperm(stb.st_mode), buf);
		    else
			(void)printf(" D %s\n", buf);
		}
		else {
		    if(mflag)
			(void)printf(" d%s %s\n", pperm(stb.st_mode), buf);
		    else
			(void)printf(" d %s\n", buf);
		}
		lastdev = stb.st_dev;

		(void)fflush(stdout);
		break;

	    case S_IFLNK:
		/*
		 * Sigh, another symlink.  Read it's contents and
		 * call namei()
		 */
		
		bzero(sym, BUFSIZ);
		if(readlink(buf, sym, BUFSIZ) == -1){
		    (void)printf(_(" ? problems reading symlink %s - %s (%d)\n"), buf, ERR);
		    return;
		}

		if(mflag)
		    (void)printf(" l%s %s -> %s", pperm(stb.st_mode), buf, sym);
		else
		    (void)printf(" l %s -> %s", buf, sym);

		if(symcount > 0 && symcount++ > MAXSYMLINKS){
		    (void)printf(_("  *** EXCEEDED UNIX LIMIT OF SYMLINKS ***\n"));
		    symcount = -1;
		} else {
		    (void)printf("\n");
		    namei(sym, lev + 1);
		}
		break;

	    case S_IFCHR:
		if(mflag)
		    (void)printf(" c%s %s\n", pperm(stb.st_mode), buf);
		else
		    (void)printf(" c %s\n", buf);
		break;
	    
	    case S_IFBLK:
		if(mflag)
		    (void)printf(" b%s %s\n", pperm(stb.st_mode), buf);
		else
		    (void)printf(" b %s\n", buf);
		break;
	    
	    case S_IFSOCK:
		if(mflag)
		    (void)printf(" s%s %s\n", pperm(stb.st_mode), buf);
		else
		    (void)printf(" s %s\n", buf);
		break;

	    case S_IFREG:
		if(mflag)
		    (void)printf(" -%s %s\n", pperm(stb.st_mode), buf);
		else
		    (void)printf(" - %s\n", buf);
		break;
		
	    default:
		(void)fprintf(stderr,_("namei: unknown file type 0%06o on file %s\n"), stb.st_mode, buf );
		exit(1);
	    
	}
    }
}

/* Take a 
 * Mode word, as from a struct stat, and return
 * a pointer to a static string containing a printable version like ls.
 * For example 0755 produces "rwxr-xr-x"
 */
static char *
pperm(unsigned short mode) {
	unsigned short m;
	static char buf[16];
	char *bp;
	char *lschars = "xwrxwrxwr";  /* the complete string backwards */
	char *cp;
	int i;

	for(i = 0, cp = lschars, m = mode, bp = &buf[8];
	    i < 9;
	    i++, cp++, m >>= 1, bp--) {

		if(m & 1)
			*bp = *cp;
		else
			*bp = '-';
	    }
	buf[9] = '\0';

	if(mode & S_ISUID)  {
		if(buf[2] == 'x')
			buf[2] = 's';
		else
			buf[2] = 'S';
	}
	if(mode & S_ISGID)  {
		if(buf[5] == 'x')
			buf[5] = 's';
		else
			buf[5] = 'S';
	}
	if(mode & S_ISVTX)  {
		if(buf[8] == 'x')
			buf[8] = 't';
		else
			buf[8] = 'T';
	}

	return &buf[0];
}