Home | History | Annotate | Download | only in zlook
      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 *)(intptr_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