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
usage(char * pnam)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
print_extd_entries(zut_readdir_t * r)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
print_entries(zut_readdir_t * r)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
print_stats(struct stat64 * sb)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
print_xvs(uint64_t xvs)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
main(int argc,char ** argv)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