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