xref: /illumos-gate/usr/src/cmd/zlook/zlook.c (revision e802abbda8c322f24d47835734f4a793ef15ddc8)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * This is a test program that uses ioctls to the ZFS Unit Test driver
29  * to perform readdirs or lookups using flags not normally available
30  * to user-land programs.  This allows testing of the flags'
31  * behavior outside of a complicated consumer, such as the SMB driver.
32  */
33 
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <stropts.h>
38 #include <errno.h>
39 #include <sys/stat.h>
40 #include <sys/types.h>
41 #include <sys/dirent.h>
42 #include <sys/attr.h>
43 #include <stddef.h>
44 #include <fcntl.h>
45 #include <string.h>
46 #include <time.h>
47 
48 #define	_KERNEL
49 
50 #include <sys/fs/zut.h>
51 #include <sys/extdirent.h>
52 
53 #undef	_KERNEL
54 
55 #define	MAXBUF (64 * 1024)
56 #define	BIGBUF 4096
57 #define	LILBUF 64
58 
59 #define	DIRENT_NAMELEN(reclen)	\
60 	((reclen) - (offsetof(dirent_t, d_name[0])))
61 
62 static void
63 usage(char *pnam)
64 {
65 	(void) fprintf(stderr, "Usage:\n    %s -l [-is] dir-to-look-in "
66 	    "file-in-dir [xfile-on-file]\n", pnam);
67 	(void) fprintf(stderr, "    %s -i [-ls] dir-to-look-in "
68 	    "file-in-dir [xfile-on-file]\n", pnam);
69 	(void) fprintf(stderr, "    %s -s [-il] dir-to-look-in "
70 	    "file-in-dir [xfile-on-file]\n", pnam);
71 	(void) fprintf(stderr, "\t    Perform a lookup\n");
72 	(void) fprintf(stderr, "\t    -l == lookup\n");
73 	(void) fprintf(stderr, "\t    -i == request FIGNORECASE\n");
74 	(void) fprintf(stderr, "\t    -s == request stat(2) and xvattr info\n");
75 	(void) fprintf(stderr, "    %s -r [-ea] [-b buffer-size-in-bytes] "
76 	    "dir-to-look-in [file-in-dir]\n", pnam);
77 	(void) fprintf(stderr, "    %s -e [-ra] [-b buffer-size-in-bytes] "
78 	    "dir-to-look-in [file-in-dir]\n", pnam);
79 	(void) fprintf(stderr, "    %s -a [-re] [-b buffer-size-in-bytes] "
80 	    "dir-to-look-in [file-in-dir]\n", pnam);
81 	(void) fprintf(stderr, "\t    Perform a readdir\n");
82 	(void) fprintf(stderr, "\t    -r == readdir\n");
83 	(void) fprintf(stderr, "\t    -e == request extended entries\n");
84 	(void) fprintf(stderr, "\t    -a == request access filtering\n");
85 	(void) fprintf(stderr, "\t    -b == buffer size (default 4K)\n");
86 	(void) fprintf(stderr, "    %s -A path\n", pnam);
87 	(void) fprintf(stderr, "\t    Look up _PC_ACCESS_FILTERING "
88 	    "for path with pathconf(2)\n");
89 	(void) fprintf(stderr, "    %s -E path\n", pnam);
90 	(void) fprintf(stderr, "\t    Look up _PC_SATTR_EXISTS "
91 	    "for path with pathconf(2)\n");
92 	(void) fprintf(stderr, "    %s -S path\n", pnam);
93 	(void) fprintf(stderr, "\t    Look up _PC_SATTR_EXISTS "
94 	    "for path with pathconf(2)\n");
95 	exit(EINVAL);
96 }
97 
98 static void
99 print_extd_entries(zut_readdir_t *r)
100 {
101 	struct edirent *eodp;
102 	char *bufstart;
103 
104 	eodp = (edirent_t *)(uintptr_t)r->zr_buf;
105 	bufstart = (char *)eodp;
106 	while ((char *)eodp < bufstart + r->zr_bytes) {
107 		char *blanks = "                ";
108 		int i = 0;
109 		while (i < EDIRENT_NAMELEN(eodp->ed_reclen)) {
110 			if (!eodp->ed_name[i])
111 				break;
112 			(void) printf("%c", eodp->ed_name[i++]);
113 		}
114 		if (i < 16)
115 			(void) printf("%.*s", 16 - i, blanks);
116 		(void) printf("\t%x\n", eodp->ed_eflags);
117 		eodp = (edirent_t *)((intptr_t)eodp + eodp->ed_reclen);
118 	}
119 }
120 
121 static void
122 print_entries(zut_readdir_t *r)
123 {
124 	dirent64_t *dp;
125 	char *bufstart;
126 
127 	dp = (dirent64_t *)r->zr_buf;
128 	bufstart = (char *)dp;
129 	while ((char *)dp < bufstart + r->zr_bytes) {
130 		int i = 0;
131 		while (i < DIRENT_NAMELEN(dp->d_reclen)) {
132 			if (!dp->d_name[i])
133 				break;
134 			(void) printf("%c", dp->d_name[i++]);
135 		}
136 		(void) printf("\n");
137 		dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen);
138 	}
139 }
140 
141 static void
142 print_stats(struct stat64 *sb)
143 {
144 	char timebuf[512];
145 
146 	(void) printf("st_mode\t\t\t%04lo\n", (unsigned long)sb->st_mode);
147 	(void) printf("st_ino\t\t\t%llu\n", (unsigned long long)sb->st_ino);
148 	(void) printf("st_nlink\t\t%lu\n", (unsigned long)sb->st_nlink);
149 	(void) printf("st_uid\t\t\t%d\n", sb->st_uid);
150 	(void) printf("st_gid\t\t\t%d\n", sb->st_gid);
151 	(void) printf("st_size\t\t\t%lld\n", (long long)sb->st_size);
152 	(void) printf("st_blksize\t\t%ld\n", (long)sb->st_blksize);
153 	(void) printf("st_blocks\t\t%lld\n", (long long)sb->st_blocks);
154 
155 	timebuf[0] = 0;
156 	if (ctime_r(&sb->st_atime, timebuf, 512)) {
157 		(void) printf("st_atime\t\t");
158 		(void) printf("%s", timebuf);
159 	}
160 	timebuf[0] = 0;
161 	if (ctime_r(&sb->st_mtime, timebuf, 512)) {
162 		(void) printf("st_mtime\t\t");
163 		(void) printf("%s", timebuf);
164 	}
165 	timebuf[0] = 0;
166 	if (ctime_r(&sb->st_ctime, timebuf, 512)) {
167 		(void) printf("st_ctime\t\t");
168 		(void) printf("%s", timebuf);
169 	}
170 }
171 
172 static void
173 print_xvs(uint64_t xvs)
174 {
175 	uint_t bits;
176 	int idx = 0;
177 
178 	if (xvs == 0)
179 		return;
180 
181 	(void) printf("-------------------\n");
182 	(void) printf("Attribute bit(s) set:\n");
183 	(void) printf("-------------------\n");
184 
185 	bits = xvs & ((1 << F_ATTR_ALL) - 1);
186 	while (bits) {
187 		uint_t rest = bits >> 1;
188 		if (bits & 1) {
189 			(void) printf("%s", attr_to_name((f_attr_t)idx));
190 			if (rest)
191 				(void) printf(", ");
192 		}
193 		idx++;
194 		bits = rest;
195 	}
196 	(void) printf("\n");
197 }
198 
199 int
200 main(int argc, char **argv)
201 {
202 	zut_lookup_t lk = {0};
203 	zut_readdir_t rd = {0};
204 	boolean_t checking = B_FALSE;
205 	boolean_t looking = B_FALSE;
206 	boolean_t reading = B_FALSE;
207 	boolean_t bflag = B_FALSE;
208 	long rddir_bufsize = BIGBUF;
209 	int error = 0;
210 	int check;
211 	int fd;
212 	int c;
213 
214 	while ((c = getopt(argc, argv, "lisaerb:ASE")) != -1) {
215 		switch (c) {
216 		case 'l':
217 			looking = B_TRUE;
218 			break;
219 		case 'i':
220 			lk.zl_reqflags |= ZUT_IGNORECASE;
221 			looking = B_TRUE;
222 			break;
223 		case 's':
224 			lk.zl_reqflags |= ZUT_GETSTAT;
225 			looking = B_TRUE;
226 			break;
227 		case 'a':
228 			rd.zr_reqflags |= ZUT_ACCFILTER;
229 			reading = B_TRUE;
230 			break;
231 		case 'e':
232 			rd.zr_reqflags |= ZUT_EXTRDDIR;
233 			reading = B_TRUE;
234 			break;
235 		case 'r':
236 			reading = B_TRUE;
237 			break;
238 		case 'b':
239 			reading = B_TRUE;
240 			bflag = B_TRUE;
241 			rddir_bufsize = strtol(optarg, NULL, 0);
242 			break;
243 		case 'A':
244 			checking = B_TRUE;
245 			check = _PC_ACCESS_FILTERING;
246 			break;
247 		case 'S':
248 			checking = B_TRUE;
249 			check = _PC_SATTR_ENABLED;
250 			break;
251 		case 'E':
252 			checking = B_TRUE;
253 			check = _PC_SATTR_EXISTS;
254 			break;
255 		case '?':
256 		default:
257 			usage(argv[0]);		/* no return */
258 		}
259 	}
260 
261 	if ((checking && looking) || (checking && reading) ||
262 	    (looking && reading) || (!reading && bflag) ||
263 	    (!checking && !reading && !looking))
264 		usage(argv[0]);		/* no return */
265 
266 	if (rddir_bufsize < LILBUF || rddir_bufsize > MAXBUF) {
267 		(void) fprintf(stderr, "Sorry, buffer size "
268 		    "must be >= %d and less than or equal to %d bytes.\n",
269 		    LILBUF, MAXBUF);
270 		exit(EINVAL);
271 	}
272 
273 	if (checking) {
274 		char pathbuf[MAXPATHLEN];
275 		long result;
276 
277 		if (argc - optind < 1)
278 			usage(argv[0]);		/* no return */
279 		(void) strlcpy(pathbuf, argv[optind], MAXPATHLEN);
280 		result = pathconf(pathbuf, check);
281 		(void) printf("pathconf(2) check for %s\n", pathbuf);
282 		switch (check) {
283 		case _PC_SATTR_ENABLED:
284 			(void) printf("System attributes ");
285 			if (result != 0)
286 				(void) printf("Enabled\n");
287 			else
288 				(void) printf("Not enabled\n");
289 			break;
290 		case _PC_SATTR_EXISTS:
291 			(void) printf("System attributes ");
292 			if (result != 0)
293 				(void) printf("Exist\n");
294 			else
295 				(void) printf("Do not exist\n");
296 			break;
297 		case _PC_ACCESS_FILTERING:
298 			(void) printf("Access filtering ");
299 			if (result != 0)
300 				(void) printf("Available\n");
301 			else
302 				(void) printf("Not available\n");
303 			break;
304 		}
305 		return (result);
306 	}
307 
308 	if ((fd = open(ZUT_DEV, O_RDONLY)) < 0) {
309 		perror(ZUT_DEV);
310 		return (ENXIO);
311 	}
312 
313 	if (reading) {
314 		char *buf;
315 
316 		if (argc - optind < 1)
317 			usage(argv[0]);		/* no return */
318 
319 		(void) strlcpy(rd.zr_dir, argv[optind], MAXPATHLEN);
320 		if (argc - optind > 1) {
321 			(void) strlcpy(rd.zr_file, argv[optind + 1],
322 			    MAXNAMELEN);
323 			rd.zr_reqflags |= ZUT_XATTR;
324 		}
325 
326 		if ((buf = malloc(rddir_bufsize)) == NULL) {
327 			error = errno;
328 			perror("malloc");
329 			(void) close(fd);
330 			return (error);
331 		}
332 
333 		rd.zr_buf = (uint64_t)(uintptr_t)buf;
334 		rd.zr_buflen = rddir_bufsize;
335 
336 		while (!rd.zr_eof) {
337 			int ierr;
338 
339 			if ((ierr = ioctl(fd, ZUT_IOC_READDIR, &rd)) != 0) {
340 				(void) fprintf(stderr,
341 				    "IOCTL error: %s (%d)\n",
342 				    strerror(ierr), ierr);
343 				free(buf);
344 				(void) close(fd);
345 				return (ierr);
346 			}
347 			if (rd.zr_retcode) {
348 				(void) fprintf(stderr,
349 				    "readdir result: %s (%d)\n",
350 				    strerror(rd.zr_retcode), rd.zr_retcode);
351 				free(buf);
352 				(void) close(fd);
353 				return (rd.zr_retcode);
354 			}
355 			if (rd.zr_reqflags & ZUT_EXTRDDIR)
356 				print_extd_entries(&rd);
357 			else
358 				print_entries(&rd);
359 		}
360 		free(buf);
361 	} else {
362 		int ierr;
363 
364 		if (argc - optind < 2)
365 			usage(argv[0]);		/* no return */
366 
367 		(void) strlcpy(lk.zl_dir, argv[optind], MAXPATHLEN);
368 		(void) strlcpy(lk.zl_file, argv[optind + 1], MAXNAMELEN);
369 		if (argc - optind > 2) {
370 			(void) strlcpy(lk.zl_xfile,
371 			    argv[optind + 2], MAXNAMELEN);
372 			lk.zl_reqflags |= ZUT_XATTR;
373 		}
374 
375 		if ((ierr = ioctl(fd, ZUT_IOC_LOOKUP, &lk)) != 0) {
376 			(void) fprintf(stderr,
377 			    "IOCTL error: %s (%d)\n",
378 			    strerror(ierr), ierr);
379 			(void) close(fd);
380 			return (ierr);
381 		}
382 
383 		(void) printf("\nLookup of ");
384 		if (lk.zl_reqflags & ZUT_XATTR) {
385 			(void) printf("extended attribute \"%s\" of ",
386 			    lk.zl_xfile);
387 		}
388 		(void) printf("file \"%s\" ", lk.zl_file);
389 		(void) printf("in directory \"%s\" ", lk.zl_dir);
390 		if (lk.zl_retcode) {
391 			(void) printf("failed: %s (%d)\n",
392 			    strerror(lk.zl_retcode), lk.zl_retcode);
393 			(void) close(fd);
394 			return (lk.zl_retcode);
395 		}
396 
397 		(void) printf("succeeded.\n");
398 		if (lk.zl_reqflags & ZUT_IGNORECASE) {
399 			(void) printf("----------------------------\n");
400 			(void) printf("dirent flags: 0x%0x\n", lk.zl_deflags);
401 			(void) printf("real name: %s\n", lk.zl_real);
402 		}
403 		if (lk.zl_reqflags & ZUT_GETSTAT) {
404 			(void) printf("----------------------------\n");
405 			print_stats(&lk.zl_statbuf);
406 			print_xvs(lk.zl_xvattrs);
407 		}
408 	}
409 
410 	(void) close(fd);
411 	return (0);
412 }
413