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 61 static void 62 usage(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 97 static void 98 print_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 120 static void 121 print_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 140 static void 141 print_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 171 static void 172 print_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 198 int 199 main(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